├── .gitignore ├── .env.example ├── src ├── utils │ ├── math.ts │ ├── buffer.ts │ ├── validationAddress.ts │ ├── mutex.ts │ ├── types.ts │ ├── mempool.ts │ └── unisat.api.ts ├── services │ ├── wallet │ │ ├── initializeWallet.ts │ │ └── WIFWallet.ts │ ├── utxo │ │ ├── utxo.management.ts │ │ ├── utxo.singleSend.ts │ │ ├── utxo.split.ts │ │ ├── utxo.reinscribe.singleSend.ts │ │ ├── utxo.singleSendPsbt.ts │ │ ├── utxo.splitPsbt.ts │ │ ├── utxo.reinscribe.singleSendPsbt.ts │ │ └── utxo.ordinalsSendPsbt.ts │ ├── psbt │ │ ├── sendOrdinalPsbt.ts │ │ ├── TapLeafPsbtCreate.ts │ │ └── inscriptionPsbt.ts │ └── tapscript │ │ ├── fileTapScript.ts │ │ ├── textTapScript.ts │ │ └── delegateTapScript.ts ├── config │ └── network.config.ts ├── routes │ ├── wallet.management.route.ts │ ├── status.network.route.ts │ ├── send.ordinals.route.ts │ └── inscription.route.ts └── controller │ ├── estimate.controller.ts │ └── inscribe.controller.ts ├── dist ├── src │ ├── utils │ │ ├── types.js │ │ ├── math.js │ │ ├── buffer.js │ │ ├── validationAddress.js │ │ ├── mutex.js │ │ ├── mempool.js │ │ └── unisat.api.js │ ├── services │ │ ├── wallet │ │ │ ├── initializeWallet.js │ │ │ └── WIFWallet.js │ │ ├── utxo │ │ │ ├── utxo.management.js │ │ │ ├── utxo.singleSend.js │ │ │ ├── utxo.singleSendPsbt.js │ │ │ ├── utxo.reinscribe.singleSend.js │ │ │ ├── utxo.split.js │ │ │ ├── utxo.splitPsbt.js │ │ │ └── utxo.reinscribe.singleSendPsbt.js │ │ ├── tapscript │ │ │ ├── fileTapScript.js │ │ │ ├── textTapScript.js │ │ │ └── delegateTapScript.js │ │ └── psbt │ │ │ ├── sendOrdinalPsbt.js │ │ │ ├── TapLeafPsbt.js │ │ │ ├── TapLeafPsbtCreate.js │ │ │ └── inscriptionPsbt.js │ ├── config │ │ └── network.config.js │ ├── routes │ │ ├── wallet.management.route.js │ │ ├── status.network.route.js │ │ ├── send.ordinals.route.js │ │ └── inscription.route.js │ └── controller │ │ ├── estimate.controller.js │ │ └── inscribe.controller.js └── index.js ├── vercel.json ├── tsconfig.json ├── package.json ├── index.ts └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | PRIVATE_KEY= 2 | NETWORKTYPE= 3 | OPENAPI_UNISAT_TOKEN= -------------------------------------------------------------------------------- /src/utils/math.ts: -------------------------------------------------------------------------------- 1 | export const toInteger = (x: number) => { 2 | return Math.floor(x); 3 | }; 4 | -------------------------------------------------------------------------------- /dist/src/utils/types.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /dist/src/utils/math.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.toInteger = void 0; 4 | const toInteger = (x) => { 5 | return Math.floor(x); 6 | }; 7 | exports.toInteger = toInteger; 8 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "dist/index.js", 6 | "use": "@vercel/node", 7 | "config": { 8 | "includeFiles": ["dist/**"] 9 | } 10 | } 11 | ], 12 | "routes": [ 13 | { 14 | "src": "/(.*)", 15 | "dest": "dist/index.js" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/buffer.ts: -------------------------------------------------------------------------------- 1 | export const splitBuffer = (buffer: Buffer, chunkSize: number) => { 2 | let chunks = []; 3 | for (let i = 0; i < buffer.length; i += chunkSize) { 4 | const chunk = buffer.subarray(i, i + chunkSize); 5 | chunks.push(chunk); 6 | } 7 | return chunks; 8 | }; 9 | 10 | export const toXOnly = (pubkey: Buffer): Buffer => { 11 | return pubkey.subarray(1, 33); 12 | }; 13 | -------------------------------------------------------------------------------- /src/services/wallet/initializeWallet.ts: -------------------------------------------------------------------------------- 1 | import networkConfig from "../../config/network.config"; 2 | import ecc from "@bitcoinerlab/secp256k1"; 3 | import { initEccLib } from "bitcoinjs-lib"; 4 | import { WIFWallet } from "./WIFWallet"; 5 | initEccLib(ecc as any); 6 | 7 | const networkType: string = networkConfig.networkType; 8 | let wallet: any; 9 | 10 | const privateKey: string = process.env.PRIVATE_KEY as string; 11 | wallet = new WIFWallet({ networkType: networkType, privateKey: privateKey }); 12 | export default wallet; 13 | -------------------------------------------------------------------------------- /src/utils/validationAddress.ts: -------------------------------------------------------------------------------- 1 | const bitcoin = require("bitcoinjs-lib"); 2 | import networkConfig from "../config/network.config"; 3 | import { TESTNET } from "../config/network.config"; 4 | 5 | const network = 6 | networkConfig.networkType == TESTNET 7 | ? bitcoin.networks.testnet 8 | : bitcoin.networks.bitcoin; 9 | 10 | export function isValidBitcoinAddress(address: string): Boolean { 11 | try { 12 | bitcoin.address.toOutputScript(address, network); 13 | return true; 14 | } catch (e) { 15 | return false; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "CommonJS", 5 | "lib": [ 6 | "es6" 7 | ], 8 | "rootDir": ".", 9 | "allowJs": true, 10 | "outDir": "./dist", 11 | "strict": true, 12 | "noImplicitAny": true, 13 | "esModuleInterop": true, 14 | "resolveJsonModule": true, 15 | "moduleResolution": "node", 16 | "baseUrl": ".", 17 | "paths": { 18 | "*": ["src/*", "node_modules/*"] 19 | } 20 | }, 21 | "include": ["src"], 22 | "exclude": ["node_modules"] 23 | } -------------------------------------------------------------------------------- /dist/src/utils/buffer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.toXOnly = exports.splitBuffer = void 0; 4 | const splitBuffer = (buffer, chunkSize) => { 5 | let chunks = []; 6 | for (let i = 0; i < buffer.length; i += chunkSize) { 7 | const chunk = buffer.subarray(i, i + chunkSize); 8 | chunks.push(chunk); 9 | } 10 | return chunks; 11 | }; 12 | exports.splitBuffer = splitBuffer; 13 | const toXOnly = (pubkey) => { 14 | return pubkey.subarray(1, 33); 15 | }; 16 | exports.toXOnly = toXOnly; 17 | -------------------------------------------------------------------------------- /src/config/network.config.ts: -------------------------------------------------------------------------------- 1 | import dotenv from "dotenv"; 2 | 3 | dotenv.config(); 4 | 5 | export const WIF = "WIF"; 6 | export const SEED = "SEED"; 7 | export const TESTNET = "testnet"; 8 | export const MAINNET = "mainnet"; 9 | 10 | export const MAXIMUMFEERATE = 100000; 11 | export const SEND_UTXO_FEE_LIMIT = 10000; 12 | 13 | export const DELEGATE_CONTENT = "Delegate"; 14 | export const FILE_CONTENT = "File"; 15 | export const TEXT_CONTENT = "Text"; 16 | 17 | const networkConfig = { 18 | walletType: process.env.PRIVATE_KEY ? WIF : SEED, 19 | networkType: process.env.NETWORKTYPE as string, 20 | }; 21 | 22 | export default networkConfig; 23 | -------------------------------------------------------------------------------- /src/routes/wallet.management.route.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, Router } from "express"; 2 | import { splitUTXO } from "../services/utxo/utxo.split"; 3 | 4 | // Create a new instance of the Express Router 5 | export const WalletManageRoute = Router(); 6 | 7 | // @route GET api/status/recomFeeRate 8 | // @desc Get Recommended Fee Rate 9 | // @access Public 10 | WalletManageRoute.get("/utxo-split", async (req: Request, res: Response) => { 11 | try { 12 | const response = await splitUTXO(); 13 | return res.status(200).send(response); 14 | } catch (error: any) { 15 | console.error(error); 16 | return res.status(400).send({ error }); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /dist/src/services/wallet/initializeWallet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | const network_config_1 = __importDefault(require("../../config/network.config")); 7 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 8 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 9 | const WIFWallet_1 = require("./WIFWallet"); 10 | (0, bitcoinjs_lib_1.initEccLib)(secp256k1_1.default); 11 | const networkType = network_config_1.default.networkType; 12 | let wallet; 13 | const privateKey = process.env.PRIVATE_KEY; 14 | wallet = new WIFWallet_1.WIFWallet({ networkType: networkType, privateKey: privateKey }); 15 | exports.default = wallet; 16 | -------------------------------------------------------------------------------- /src/services/utxo/utxo.management.ts: -------------------------------------------------------------------------------- 1 | interface IUtxo { 2 | txid: string; 3 | vout: number; 4 | value: number; 5 | } 6 | 7 | export const getSendBTCUTXOArray = ( 8 | utxoArray: Array, 9 | amount: number 10 | ) => { 11 | let utxoSum = 0; 12 | let iterator = 0; 13 | let newUtxoArray: Array = []; 14 | let filteredUtxoArray = utxoArray.filter((utxo) => utxo.value > 1000); 15 | let filteredSum = filteredUtxoArray.reduce( 16 | (accum: number, utxo: IUtxo) => accum + utxo.value, 17 | 0 18 | ); 19 | if (filteredSum < amount) { 20 | return { isSuccess: false, data: newUtxoArray }; 21 | } 22 | while (utxoSum <= amount) { 23 | utxoSum += filteredUtxoArray[iterator].value; 24 | newUtxoArray.push(filteredUtxoArray[iterator]); 25 | iterator++; 26 | } 27 | return { isSuccess: true, data: newUtxoArray }; 28 | }; 29 | -------------------------------------------------------------------------------- /dist/src/services/utxo/utxo.management.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.getSendBTCUTXOArray = void 0; 4 | const getSendBTCUTXOArray = (utxoArray, amount) => { 5 | let utxoSum = 0; 6 | let iterator = 0; 7 | let newUtxoArray = []; 8 | let filteredUtxoArray = utxoArray.filter((utxo) => utxo.value > 1000); 9 | let filteredSum = filteredUtxoArray.reduce((accum, utxo) => accum + utxo.value, 0); 10 | if (filteredSum < amount) { 11 | return { isSuccess: false, data: newUtxoArray }; 12 | } 13 | while (utxoSum <= amount) { 14 | utxoSum += filteredUtxoArray[iterator].value; 15 | newUtxoArray.push(filteredUtxoArray[iterator]); 16 | iterator++; 17 | } 18 | return { isSuccess: true, data: newUtxoArray }; 19 | }; 20 | exports.getSendBTCUTXOArray = getSendBTCUTXOArray; 21 | -------------------------------------------------------------------------------- /dist/src/utils/validationAddress.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.isValidBitcoinAddress = void 0; 7 | const bitcoin = require("bitcoinjs-lib"); 8 | const network_config_1 = __importDefault(require("../config/network.config")); 9 | const network_config_2 = require("../config/network.config"); 10 | const network = network_config_1.default.networkType == network_config_2.TESTNET 11 | ? bitcoin.networks.testnet 12 | : bitcoin.networks.bitcoin; 13 | function isValidBitcoinAddress(address) { 14 | try { 15 | bitcoin.address.toOutputScript(address, network); 16 | return true; 17 | } 18 | catch (e) { 19 | return false; 20 | } 21 | } 22 | exports.isValidBitcoinAddress = isValidBitcoinAddress; 23 | -------------------------------------------------------------------------------- /dist/src/config/network.config.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.TEXT_CONTENT = exports.FILE_CONTENT = exports.DELEGATE_CONTENT = exports.SEND_UTXO_FEE_LIMIT = exports.MAXIMUMFEERATE = exports.MAINNET = exports.TESTNET = exports.SEED = exports.WIF = void 0; 7 | const dotenv_1 = __importDefault(require("dotenv")); 8 | dotenv_1.default.config(); 9 | exports.WIF = "WIF"; 10 | exports.SEED = "SEED"; 11 | exports.TESTNET = "testnet"; 12 | exports.MAINNET = "mainnet"; 13 | exports.MAXIMUMFEERATE = 100000; 14 | exports.SEND_UTXO_FEE_LIMIT = 10000; 15 | exports.DELEGATE_CONTENT = "Delegate"; 16 | exports.FILE_CONTENT = "File"; 17 | exports.TEXT_CONTENT = "Text"; 18 | const networkConfig = { 19 | walletType: process.env.PRIVATE_KEY ? exports.WIF : exports.SEED, 20 | networkType: process.env.NETWORKTYPE, 21 | }; 22 | exports.default = networkConfig; 23 | -------------------------------------------------------------------------------- /src/utils/mutex.ts: -------------------------------------------------------------------------------- 1 | import { flagMutex, iterator } from "../.."; 2 | import { app } from "../.."; 3 | 4 | export const setUtxoFlag = async (value: number) => { 5 | const release = await flagMutex.acquire(); 6 | try { 7 | // Perform actions with the flag variable 8 | app.locals.utxoflag = value; 9 | } finally { 10 | release(); 11 | } 12 | }; 13 | 14 | export async function waitUtxoFlag() { 15 | return new Promise((resolve, reject) => { 16 | let intervalId: any; 17 | const checkForUtxo = async () => { 18 | try { 19 | if (!app.locals.utxoflag) { 20 | resolve(); 21 | clearInterval(intervalId); 22 | } 23 | } catch (error) { 24 | reject(error); 25 | clearInterval(intervalId); 26 | } 27 | }; 28 | intervalId = setInterval(checkForUtxo, 200); 29 | }); 30 | } 31 | 32 | export const setApiIterator = async (value: number) => { 33 | const release = await iterator.acquire(); 34 | try { 35 | // Perform actions with the flag variable 36 | app.locals.iterator = value; 37 | } finally { 38 | release(); 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /dist/src/routes/wallet.management.route.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | exports.WalletManageRoute = void 0; 13 | const express_1 = require("express"); 14 | const utxo_split_1 = require("../services/utxo/utxo.split"); 15 | // Create a new instance of the Express Router 16 | exports.WalletManageRoute = (0, express_1.Router)(); 17 | // @route GET api/status/recomFeeRate 18 | // @desc Get Recommended Fee Rate 19 | // @access Public 20 | exports.WalletManageRoute.get("/utxo-split", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 21 | try { 22 | const response = yield (0, utxo_split_1.splitUTXO)(); 23 | return res.status(200).send(response); 24 | } 25 | catch (error) { 26 | console.error(error); 27 | return res.status(400).send({ error }); 28 | } 29 | })); 30 | -------------------------------------------------------------------------------- /src/routes/status.network.route.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, Router } from "express"; 2 | import { getPrice, getFeeRate, getRecommendedFeeRate } from "../utils/mempool"; 3 | import networkConfig from "../config/network.config"; 4 | 5 | // Create a new instance of the Express Router 6 | export const StatusNetworkRoute = Router(); 7 | 8 | // @route GET api/status/price 9 | // @desc Get Bitcoin price 10 | // @access Public 11 | StatusNetworkRoute.get("/price", async (req: Request, res: Response) => { 12 | try { 13 | const response = await getPrice(networkConfig.networkType); 14 | return res.status(200).send(response); 15 | } catch (error: any) { 16 | return res.status(400).send({ error }); 17 | } 18 | }); 19 | 20 | // @route GET api/status/avgFeeRate 21 | // @desc Get Fee Rate 22 | // @access Public 23 | StatusNetworkRoute.get("/avgFeeRate", async (req: Request, res: Response) => { 24 | try { 25 | await getFeeRate(networkConfig.networkType, res); 26 | } catch (error: any) { 27 | return res.status(400).send({ error }); 28 | } 29 | }); 30 | 31 | // @route GET api/status/recomFeeRate 32 | // @desc Get Recommended Fee Rate 33 | // @access Public 34 | StatusNetworkRoute.get( 35 | "/recommendFeeRate", 36 | async (req: Request, res: Response) => { 37 | try { 38 | const recommendFeeRate = await getRecommendedFeeRate( 39 | networkConfig.networkType 40 | ); 41 | res.status(200).send({ recommendFeeRate }); 42 | } catch (error: any) { 43 | res.status(400).send({ error }); 44 | } 45 | } 46 | ); 47 | -------------------------------------------------------------------------------- /src/services/utxo/utxo.singleSend.ts: -------------------------------------------------------------------------------- 1 | import networkConfig, { 2 | SEND_UTXO_FEE_LIMIT, 3 | } from "../../config/network.config"; 4 | import * as Bitcoin from "bitcoinjs-lib"; 5 | import ecc from "@bitcoinerlab/secp256k1"; 6 | import dotenv from "dotenv"; 7 | import { 8 | redeemSingleSendUTXOPsbt, 9 | singleSendUTXOPsbt, 10 | } from "./utxo.singleSendPsbt"; 11 | import { WIFWallet } from "../wallet/WIFWallet"; 12 | import { IUtxo } from "../../utils/types"; 13 | 14 | dotenv.config(); 15 | Bitcoin.initEccLib(ecc); 16 | 17 | const networkType: string = networkConfig.networkType; 18 | let wallet: WIFWallet; 19 | 20 | const privateKey: string = process.env.PRIVATE_KEY as string; 21 | wallet = new WIFWallet({ networkType: networkType, privateKey: privateKey }); 22 | 23 | export const singleSendUTXO = async ( 24 | address: string, 25 | feeRate: number, 26 | userUtxo: IUtxo, 27 | amount: number, 28 | holderStatus: boolean 29 | ) => { 30 | let redeemFee = SEND_UTXO_FEE_LIMIT; 31 | 32 | let redeemPsbt: Bitcoin.Psbt = redeemSingleSendUTXOPsbt( 33 | wallet, 34 | userUtxo, 35 | networkType, 36 | amount, 37 | redeemFee, 38 | holderStatus 39 | ); 40 | redeemPsbt = wallet.signPsbt(redeemPsbt, wallet.ecPair); 41 | redeemFee = redeemPsbt.extractTransaction(true).virtualSize() * feeRate; 42 | 43 | let psbt = singleSendUTXOPsbt( 44 | wallet, 45 | userUtxo, 46 | networkType, 47 | redeemFee, 48 | address, 49 | amount, 50 | holderStatus 51 | ); 52 | let signedPsbt = wallet.signPsbt(psbt, wallet.ecPair); 53 | const tx = signedPsbt.extractTransaction(true); 54 | 55 | return { isSuccess: true, data: tx }; 56 | }; 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "luminex.inscription.service", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx tsc --skipLibCheck", 8 | "start": "node dist/index.js", 9 | "dev": "npx ts-node-dev index.ts", 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "@bitcoinerlab/secp256k1": "^1.1.1", 17 | "@cmdcode/buff-utils": "^1.7.6", 18 | "@cmdcode/crypto-utils": "^1.9.8", 19 | "@cmdcode/tapscript": "^1.4.4", 20 | "@noble/curves": "^1.4.0", 21 | "@types/express": "^4.17.21", 22 | "async-mutex": "^0.5.0", 23 | "axios": "^1.6.5", 24 | "bip32": "^4.0.0", 25 | "bip39": "^3.1.0", 26 | "bitcoinjs-lib": "^6.1.3", 27 | "body-parser": "^1.20.2", 28 | "buff-utils": "^0.0.1", 29 | "cbor": "^9.0.2", 30 | "cors": "^2.8.5", 31 | "dotenv": "^16.3.2", 32 | "ecpair": "^2.1.0", 33 | "express": "^4.18.2", 34 | "express-fileupload": "^1.5.0", 35 | "mongoose": "^8.4.0", 36 | "nodemon": "^3.0.3", 37 | "swagger-jsdoc": "^6.2.8", 38 | "swagger-ui-dist": "^5.17.9", 39 | "swagger-ui-express": "^5.0.0", 40 | "tiny-secp256k1": "^2.2.2", 41 | "tsc-watch": "^6.2.0", 42 | "yamljs": "^0.3.0" 43 | }, 44 | "devDependencies": { 45 | "@types/cors": "^2.8.17", 46 | "@types/express-fileupload": "^1.5.0", 47 | "@types/node": "^20.11.5", 48 | "@types/swagger-jsdoc": "^6.0.4", 49 | "@types/swagger-ui-dist": "^3.30.4", 50 | "@types/swagger-ui-express": "^4.1.6", 51 | "@types/yamljs": "^0.2.34", 52 | "ts-node": "^10.9.2", 53 | "ts-node-dev": "^2.0.0", 54 | "typescript": "^5.3.3" 55 | }, 56 | "engines": { 57 | "node": "20" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/utils/types.ts: -------------------------------------------------------------------------------- 1 | export interface IFile { 2 | mimetype: string; 3 | data: Buffer; 4 | } 5 | 6 | export interface IUtxo { 7 | txid: string; 8 | vout: number; 9 | value: number; 10 | } 11 | 12 | export interface ITextInscription { 13 | receiveAddress: string; 14 | contents: Array; 15 | feeRate: number; 16 | padding: number; 17 | parentId: string; 18 | metadata: string; 19 | metaprotocol: string; 20 | reinscriptionId: string; 21 | btcAmount: number; 22 | sendBtcTxId: string; 23 | txIndex: number; 24 | ordinalsAddress: string; 25 | ordinalsPublicKey: string; 26 | paymentAddress: string; 27 | paymentPublicKey: string; 28 | holderStatus: string; 29 | } 30 | 31 | export interface IFileInscription { 32 | receiveAddress: string; 33 | files: Array; 34 | feeRate: number; 35 | padding: number; 36 | parentId: string; 37 | metadata: string; 38 | metaprotocol: string; 39 | reinscriptionId: string; 40 | btcAmount: number; 41 | sendBtcTxId: string; 42 | txIndex: number; 43 | ordinalsAddress: string; 44 | ordinalsPublicKey: string; 45 | paymentAddress: string; 46 | paymentPublicKey: string; 47 | holderStatus: string; 48 | } 49 | 50 | export interface IDelegateInscription { 51 | receiveAddress: string; 52 | delegateIds: Array; 53 | feeRate: number; 54 | padding: number; 55 | parentId: string; 56 | metadata: string; 57 | metaprotocol: string; 58 | reinscriptionId: string; 59 | btcAmount: number; 60 | sendBtcTxId: string; 61 | txIndex: number; 62 | ordinalsAddress: string; 63 | ordinalsPublicKey: string; 64 | paymentAddress: string; 65 | paymentPublicKey: string; 66 | holderStatus: string; 67 | } 68 | 69 | export interface ISendingOrdinalData { 70 | ordinalsAddress: string; 71 | ordinalsPublicKey: string; 72 | paymentAddress: string; 73 | paymentPublicKey: string; 74 | receiveAddress: string; 75 | parentId: string; 76 | reinscriptionId: string; 77 | feeRate: number; 78 | btcAmount: number; 79 | } 80 | -------------------------------------------------------------------------------- /src/services/utxo/utxo.split.ts: -------------------------------------------------------------------------------- 1 | import networkConfig, { 2 | SEND_UTXO_FEE_LIMIT, 3 | } from "../../config/network.config"; 4 | import { pushBTCpmt } from "../../utils/mempool"; 5 | import * as Bitcoin from "bitcoinjs-lib"; 6 | import ecc from "@bitcoinerlab/secp256k1"; 7 | import dotenv from "dotenv"; 8 | import { WIFWallet } from "../wallet/WIFWallet"; 9 | import { getRecommendedFeeRate } from "../../utils/mempool"; 10 | import { redeemUtxoSplitPsbt, utxoSplitPsbt } from "./utxo.splitPsbt"; 11 | import { getBtcUtxoInfo } from "../../utils/unisat.api"; 12 | 13 | dotenv.config(); 14 | Bitcoin.initEccLib(ecc); 15 | 16 | const networkType: string = networkConfig.networkType; 17 | let wallet: WIFWallet; 18 | 19 | const privateKey: string = process.env.PRIVATE_KEY as string; 20 | wallet = new WIFWallet({ networkType: networkType, privateKey: privateKey }); 21 | 22 | export const splitUTXO = async () => { 23 | const recomFeeRate = await getRecommendedFeeRate(networkType); 24 | const splitFeeRate = recomFeeRate.fastestFee * 1.1; 25 | 26 | const utxos = await getBtcUtxoInfo(wallet.address, networkType); 27 | // let utxos = await getUtxos(wallet.address, networkType) 28 | // utxos = utxos.filter((utxo: IUtxo, index: number) => utxo.value > 5000) 29 | 30 | const filteredUtxos = utxos.filter( 31 | (utxo: any) => utxo.value > SEND_UTXO_FEE_LIMIT 32 | ); 33 | if (filteredUtxos.length) { 34 | let redeemPsbt: Bitcoin.Psbt = redeemUtxoSplitPsbt( 35 | wallet, 36 | filteredUtxos, 37 | networkType 38 | ); 39 | redeemPsbt = wallet.signPsbt(redeemPsbt, wallet.ecPair); 40 | let redeemFee = 41 | redeemPsbt.extractTransaction(true).virtualSize() * splitFeeRate; 42 | let psbt = utxoSplitPsbt(wallet, filteredUtxos, networkType, redeemFee); 43 | let signedPsbt = wallet.signPsbt(psbt, wallet.ecPair); 44 | 45 | const txHex = signedPsbt.extractTransaction(true).toHex(); 46 | 47 | const txId = await pushBTCpmt(txHex, networkType); 48 | 49 | return { isSuccess: true, data: txId }; 50 | } else { 51 | return { isSuccess: false, data: "Wallet UTXO split is failed!" }; 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /src/services/utxo/utxo.reinscribe.singleSend.ts: -------------------------------------------------------------------------------- 1 | import networkConfig, { 2 | SEND_UTXO_FEE_LIMIT, 3 | } from "../../config/network.config"; 4 | import * as Bitcoin from "bitcoinjs-lib"; 5 | import ecc from "@bitcoinerlab/secp256k1"; 6 | import dotenv from "dotenv"; 7 | import { 8 | redeemReinscribeAndUtxoSendPsbt, 9 | ReinscribeAndUtxoSendPsbt, 10 | } from "./utxo.reinscribe.singleSendPsbt"; 11 | import { WIFWallet } from "../wallet/WIFWallet"; 12 | import { getInscriptionInfo } from "../../utils/unisat.api"; 13 | import { IUtxo } from "../../utils/types"; 14 | 15 | dotenv.config(); 16 | Bitcoin.initEccLib(ecc); 17 | 18 | const networkType: string = networkConfig.networkType; 19 | let wallet: WIFWallet; 20 | 21 | const privateKey: string = process.env.PRIVATE_KEY as string; 22 | wallet = new WIFWallet({ networkType: networkType, privateKey: privateKey }); 23 | 24 | export const reinscriptionAndUTXOSend = async ( 25 | reinscriptionId: string, 26 | address: string, 27 | feeRate: number, 28 | userUtxo: IUtxo, 29 | amount: number, 30 | holderStatus: boolean 31 | ) => { 32 | console.log("reinscriptionId => ", reinscriptionId); 33 | 34 | const reinscriptionUTXO: IUtxo = await getInscriptionInfo( 35 | reinscriptionId, 36 | networkConfig.networkType 37 | ); 38 | 39 | console.log("reinscriptionUTXO => ", reinscriptionUTXO); 40 | 41 | let redeemFee = SEND_UTXO_FEE_LIMIT; 42 | 43 | let redeemPsbt: Bitcoin.Psbt = redeemReinscribeAndUtxoSendPsbt( 44 | wallet, 45 | userUtxo, 46 | networkType, 47 | amount, 48 | reinscriptionUTXO, 49 | redeemFee, 50 | holderStatus 51 | ); 52 | redeemPsbt = wallet.signPsbt(redeemPsbt, wallet.ecPair); 53 | redeemFee = redeemPsbt.extractTransaction(true).virtualSize() * feeRate; 54 | 55 | let psbt = ReinscribeAndUtxoSendPsbt( 56 | wallet, 57 | userUtxo, 58 | networkType, 59 | redeemFee, 60 | address, 61 | amount, 62 | reinscriptionUTXO, 63 | holderStatus 64 | ); 65 | let signedPsbt = wallet.signPsbt(psbt, wallet.ecPair); 66 | const tx = signedPsbt.extractTransaction(true); 67 | 68 | return { isSuccess: true, data: tx }; 69 | }; 70 | -------------------------------------------------------------------------------- /src/services/wallet/WIFWallet.ts: -------------------------------------------------------------------------------- 1 | import * as bitcoin from "bitcoinjs-lib"; 2 | import { initEccLib, networks } from "bitcoinjs-lib"; 3 | import ecc from "@bitcoinerlab/secp256k1"; 4 | import ECPairFactory, { type ECPairInterface } from "ecpair"; 5 | import dotenv from "dotenv"; 6 | import { type PublicKey, SecretKey } from "@cmdcode/crypto-utils"; 7 | import { TESTNET } from "../../config/network.config"; 8 | 9 | interface IWIFWallet { 10 | networkType: string; 11 | privateKey: string; 12 | } 13 | 14 | dotenv.config(); 15 | initEccLib(ecc); 16 | 17 | const ECPair = ECPairFactory(ecc); 18 | 19 | export class WIFWallet { 20 | private network: bitcoin.networks.Network; 21 | public ecPair: ECPairInterface; 22 | public address: string; 23 | public output: Buffer; 24 | public publicKey: string; 25 | public seckey?: SecretKey; 26 | public secret: any; 27 | public pubkey: PublicKey; 28 | 29 | constructor(walletParam: IWIFWallet) { 30 | if (walletParam.networkType == TESTNET) { 31 | this.network = networks.testnet; 32 | } else { 33 | this.network = networks.bitcoin; 34 | } 35 | 36 | this.ecPair = ECPair.fromWIF(walletParam.privateKey, this.network); 37 | 38 | this.secret = this.ecPair.privateKey?.toString("hex"); 39 | this.seckey = new SecretKey(this.secret, { type: "taproot" }); 40 | this.pubkey = this.seckey.pub; 41 | 42 | const { address, output } = bitcoin.payments.p2tr({ 43 | internalPubkey: this.ecPair.publicKey.subarray(1, 33), 44 | network: this.network, 45 | }); 46 | this.address = address as string; 47 | this.output = output as Buffer; 48 | this.publicKey = this.ecPair.publicKey.toString("hex"); 49 | } 50 | 51 | signPsbt(psbt: bitcoin.Psbt, ecPair: ECPairInterface): bitcoin.Psbt { 52 | const tweakedChildNode = ecPair.tweak( 53 | bitcoin.crypto.taggedHash("TapTweak", ecPair.publicKey.subarray(1, 33)) 54 | ); 55 | 56 | for (let i = 0; i < psbt.inputCount; i++) { 57 | psbt.signInput(i, tweakedChildNode); 58 | psbt.validateSignaturesOfInput(i, () => true); 59 | psbt.finalizeInput(i); 60 | } 61 | return psbt; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/services/psbt/sendOrdinalPsbt.ts: -------------------------------------------------------------------------------- 1 | import { Psbt, initEccLib } from "bitcoinjs-lib"; 2 | import { ISendingOrdinalData } from "../../utils/types"; 3 | import networkConfig, { 4 | SEND_UTXO_FEE_LIMIT, 5 | } from "../../config/network.config"; 6 | import wallet from "../wallet/initializeWallet"; 7 | import ecc from "@bitcoinerlab/secp256k1"; 8 | import { getSendBTCUTXOArray } from "../../services/utxo/utxo.management"; 9 | import { 10 | OrdinalsUtxoSendPsbt, 11 | RedeemOrdinalsUtxoSendPsbt, 12 | } from "../../services/utxo/utxo.ordinalsSendPsbt"; 13 | import { getBtcUtxoInfo } from "../../utils/unisat.api"; 14 | 15 | initEccLib(ecc as any); 16 | export const sendOrdinalBTCPsbt = async ( 17 | sendingOrdinalData: ISendingOrdinalData 18 | ): Promise => { 19 | const utxos = await getBtcUtxoInfo( 20 | sendingOrdinalData.paymentAddress, 21 | networkConfig.networkType 22 | ); 23 | 24 | let response = getSendBTCUTXOArray( 25 | utxos, 26 | sendingOrdinalData.btcAmount + SEND_UTXO_FEE_LIMIT 27 | ); 28 | 29 | if (!response.isSuccess) { 30 | return { isSuccess: false, data: "Not enough balance on your wallet." }; 31 | } 32 | 33 | let selectedUtxos = response.data; 34 | let redeemFee = SEND_UTXO_FEE_LIMIT; 35 | 36 | for (let i = 0; i < 3; i++) { 37 | let redeemPsbt: Psbt = await RedeemOrdinalsUtxoSendPsbt( 38 | selectedUtxos, 39 | networkConfig.networkType, 40 | sendingOrdinalData, 41 | redeemFee 42 | ); 43 | redeemPsbt = wallet.signPsbt(redeemPsbt, wallet.ecPair); 44 | 45 | redeemFee = 46 | redeemPsbt.extractTransaction(true).virtualSize() * 47 | sendingOrdinalData.feeRate; 48 | 49 | response = getSendBTCUTXOArray( 50 | utxos, 51 | sendingOrdinalData.btcAmount + redeemFee 52 | ); 53 | if (!response.isSuccess) { 54 | return { isSuccess: false, data: "Not enough balance in your wallet." }; 55 | } 56 | selectedUtxos = response.data; 57 | } 58 | 59 | let psbt = await OrdinalsUtxoSendPsbt( 60 | selectedUtxos, 61 | networkConfig.networkType, 62 | sendingOrdinalData, 63 | redeemFee 64 | ); 65 | 66 | return { isSuccess: true, data: psbt }; 67 | }; 68 | -------------------------------------------------------------------------------- /src/services/utxo/utxo.singleSendPsbt.ts: -------------------------------------------------------------------------------- 1 | import * as Bitcoin from "bitcoinjs-lib"; 2 | import ecc from "@bitcoinerlab/secp256k1"; 3 | import { TESTNET } from "../../config/network.config"; 4 | 5 | Bitcoin.initEccLib(ecc); 6 | 7 | interface IUtxo { 8 | txid: string; 9 | vout: number; 10 | value: number; 11 | } 12 | 13 | export const redeemSingleSendUTXOPsbt = ( 14 | wallet: any, 15 | userUtxo: IUtxo, 16 | networkType: string, 17 | amount: number, 18 | fee: number, 19 | holderStatus: boolean 20 | ): Bitcoin.Psbt => { 21 | const psbt = new Bitcoin.Psbt({ 22 | network: 23 | networkType == TESTNET 24 | ? Bitcoin.networks.testnet 25 | : Bitcoin.networks.bitcoin, 26 | }); 27 | psbt.addInput({ 28 | hash: userUtxo.txid, 29 | index: userUtxo.vout, 30 | witnessUtxo: { 31 | value: userUtxo.value, 32 | script: wallet.output, 33 | }, 34 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 35 | }); 36 | 37 | psbt.addOutput({ 38 | address: wallet.address, 39 | value: amount, 40 | }); 41 | if (!holderStatus) { 42 | psbt.addOutput({ 43 | address: wallet.address, 44 | value: userUtxo.value - fee - amount, 45 | }); 46 | } 47 | 48 | return psbt; 49 | }; 50 | 51 | export const singleSendUTXOPsbt = ( 52 | wallet: any, 53 | userUtxo: IUtxo, 54 | networkType: string, 55 | fee: number, 56 | address: string, 57 | amount: number, 58 | holderStatus: boolean 59 | ): Bitcoin.Psbt => { 60 | const psbt = new Bitcoin.Psbt({ 61 | network: 62 | networkType == TESTNET 63 | ? Bitcoin.networks.testnet 64 | : Bitcoin.networks.bitcoin, 65 | }); 66 | 67 | psbt.addInput({ 68 | hash: userUtxo.txid, 69 | index: userUtxo.vout, 70 | witnessUtxo: { 71 | value: userUtxo.value, 72 | script: wallet.output, 73 | }, 74 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 75 | }); 76 | 77 | psbt.addOutput({ 78 | address: address, 79 | value: amount, 80 | }); 81 | 82 | if (!holderStatus) { 83 | psbt.addOutput({ 84 | address: wallet.address, 85 | value: userUtxo.value - fee - amount, 86 | }); 87 | } 88 | return psbt; 89 | }; 90 | -------------------------------------------------------------------------------- /dist/src/utils/mutex.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | exports.setApiIterator = exports.waitUtxoFlag = exports.setUtxoFlag = void 0; 13 | const __1 = require("../.."); 14 | const __2 = require("../.."); 15 | const setUtxoFlag = (value) => __awaiter(void 0, void 0, void 0, function* () { 16 | const release = yield __1.flagMutex.acquire(); 17 | try { 18 | // Perform actions with the flag variable 19 | __2.app.locals.utxoflag = value; 20 | } 21 | finally { 22 | release(); 23 | } 24 | }); 25 | exports.setUtxoFlag = setUtxoFlag; 26 | function waitUtxoFlag() { 27 | return __awaiter(this, void 0, void 0, function* () { 28 | return new Promise((resolve, reject) => { 29 | let intervalId; 30 | const checkForUtxo = () => __awaiter(this, void 0, void 0, function* () { 31 | try { 32 | if (!__2.app.locals.utxoflag) { 33 | resolve(); 34 | clearInterval(intervalId); 35 | } 36 | } 37 | catch (error) { 38 | reject(error); 39 | clearInterval(intervalId); 40 | } 41 | }); 42 | intervalId = setInterval(checkForUtxo, 200); 43 | }); 44 | }); 45 | } 46 | exports.waitUtxoFlag = waitUtxoFlag; 47 | const setApiIterator = (value) => __awaiter(void 0, void 0, void 0, function* () { 48 | const release = yield __1.iterator.acquire(); 49 | try { 50 | // Perform actions with the flag variable 51 | __2.app.locals.iterator = value; 52 | } 53 | finally { 54 | release(); 55 | } 56 | }); 57 | exports.setApiIterator = setApiIterator; 58 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import express, { Express } from "express"; 2 | import cors from "cors"; 3 | import dotenv from "dotenv"; 4 | import fileUpload from "express-fileupload"; 5 | import swaggerUi from "swagger-ui-express"; 6 | import YAML from "yamljs"; 7 | import bodyParser = require("body-parser"); 8 | import { InscriptionRouter } from "./src/routes/inscription.route"; 9 | import { StatusNetworkRoute } from "./src/routes/status.network.route"; 10 | import { WalletManageRoute } from "./src/routes/wallet.management.route"; 11 | import { EstimateFeeRouter } from "./src/routes/estimate.fee.route"; 12 | import { SendOrdinalRouter } from "./src/routes/send.ordinals.route"; 13 | import http from "http"; 14 | import { TESTNET } from "./src/config/network.config"; 15 | import { Mutex } from "async-mutex"; 16 | 17 | export const flagMutex = new Mutex(); 18 | export const iterator = new Mutex(); 19 | 20 | /* 21 | * Load up and parse configuration details from 22 | * the `.env` file to the `process.env` 23 | * object of Node.js 24 | */ 25 | dotenv.config(); 26 | /* 27 | * Create an Express application and get the 28 | * value of the PORT environment variable 29 | * from the `process.env` 30 | */ 31 | let swaggerDocument: any = ""; 32 | 33 | if (process.env.NETWORKTYPE == TESTNET) { 34 | swaggerDocument = YAML.load("swagger_devnet.yaml"); 35 | } else { 36 | swaggerDocument = YAML.load("swagger_mainnet.yaml"); 37 | } 38 | 39 | export const app: Express = express(); 40 | 41 | app.locals.utxoflag = false; 42 | 43 | const server = http.createServer(app); 44 | 45 | const port = process.env.PORT || 8081; 46 | app.use(fileUpload()); 47 | app.use(express.json({ limit: "50mb" })); 48 | app.use(express.urlencoded({ limit: "50mb", extended: true })); 49 | app.use(bodyParser.json({ limit: "50mb" })); 50 | app.use(bodyParser.urlencoded({ limit: "50mb", extended: true })); 51 | app.use(cors()); 52 | 53 | app.use("/api/inscribe", InscriptionRouter); 54 | app.use("/api/estimate", EstimateFeeRouter); 55 | app.use("/api/status", StatusNetworkRoute); 56 | app.use("/api/wallet", WalletManageRoute); 57 | app.use("/api/sendOrdinal", SendOrdinalRouter); 58 | 59 | app.use( 60 | "/api-docs", 61 | swaggerUi.serve, 62 | swaggerUi.setup(swaggerDocument, { explorer: true }) 63 | ); 64 | app.locals.iterator = 0; 65 | /* Start the Express app and listen 66 | for incoming requests on the specified port */ 67 | server.listen(port, () => { 68 | console.log(`[server]: Server is running at http://localhost:${port}`); 69 | }); 70 | -------------------------------------------------------------------------------- /src/services/psbt/TapLeafPsbtCreate.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Transaction, 3 | script, 4 | payments, 5 | initEccLib, 6 | networks, 7 | } from "bitcoinjs-lib"; 8 | import { Taptree } from "bitcoinjs-lib/src/types"; 9 | import { toXOnly } from "../../utils/buffer"; 10 | import networkConfig, { 11 | DELEGATE_CONTENT, 12 | FILE_CONTENT, 13 | TESTNET, 14 | TEXT_CONTENT, 15 | } from "../../config/network.config"; 16 | import ecc from "@bitcoinerlab/secp256k1"; 17 | import wallet from "../wallet/initializeWallet"; 18 | import { reinscriptionAndUTXOSend } from "../utxo/utxo.reinscribe.singleSend"; 19 | import { singleSendUTXO } from "../utxo/utxo.singleSend"; 20 | import { IUtxo } from "utils/types"; 21 | 22 | initEccLib(ecc as any); 23 | 24 | const network = 25 | networkConfig.networkType == TESTNET ? networks.testnet : networks.bitcoin; 26 | const keyPair = wallet.ecPair; 27 | 28 | export const tapleafPsbt = async ( 29 | contentType: string, 30 | inscriptionData: any, 31 | tapScript: Array, 32 | userUtxo: IUtxo, 33 | amount: number 34 | ): Promise => { 35 | const ordinal_script = script.compile(tapScript); 36 | 37 | const scriptTree: Taptree = { 38 | output: ordinal_script, 39 | }; 40 | 41 | const redeem = { 42 | output: ordinal_script, 43 | redeemVersion: 192, 44 | }; 45 | 46 | const ordinal_p2tr = payments.p2tr({ 47 | internalPubkey: toXOnly(keyPair.publicKey), 48 | network, 49 | scriptTree, 50 | redeem, 51 | }); 52 | const address: string = ordinal_p2tr.address ?? ""; 53 | let res: any = {}; 54 | 55 | let inscriptionAmount: number = 0; 56 | if (contentType == TEXT_CONTENT) 57 | inscriptionAmount = inscriptionData.contents.length; 58 | if (contentType == FILE_CONTENT) 59 | inscriptionAmount = inscriptionData.files.length; 60 | if (contentType == DELEGATE_CONTENT) 61 | inscriptionAmount = inscriptionData.delegateIds.length; 62 | 63 | if (inscriptionData.reinscriptionId && inscriptionAmount == 1) { 64 | res = await reinscriptionAndUTXOSend( 65 | inscriptionData.reinscriptionId, 66 | address, 67 | inscriptionData.feeRate, 68 | userUtxo, 69 | amount, 70 | inscriptionData.holderStatus 71 | ); 72 | } else { 73 | res = await singleSendUTXO( 74 | address, 75 | inscriptionData.feeRate, 76 | userUtxo, 77 | amount, 78 | inscriptionData.holderStatus 79 | ); 80 | } 81 | 82 | if (!res.isSuccess) { 83 | console.log(res.data); 84 | } 85 | 86 | return res.data; 87 | }; 88 | export default tapleafPsbt; 89 | -------------------------------------------------------------------------------- /dist/src/routes/status.network.route.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | exports.StatusNetworkRoute = void 0; 16 | const express_1 = require("express"); 17 | const mempool_1 = require("../utils/mempool"); 18 | const network_config_1 = __importDefault(require("../config/network.config")); 19 | // Create a new instance of the Express Router 20 | exports.StatusNetworkRoute = (0, express_1.Router)(); 21 | // @route GET api/status/price 22 | // @desc Get Bitcoin price 23 | // @access Public 24 | exports.StatusNetworkRoute.get("/price", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 25 | try { 26 | const response = yield (0, mempool_1.getPrice)(network_config_1.default.networkType); 27 | return res.status(200).send(response); 28 | } 29 | catch (error) { 30 | return res.status(400).send({ error }); 31 | } 32 | })); 33 | // @route GET api/status/avgFeeRate 34 | // @desc Get Fee Rate 35 | // @access Public 36 | exports.StatusNetworkRoute.get("/avgFeeRate", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 37 | try { 38 | yield (0, mempool_1.getFeeRate)(network_config_1.default.networkType, res); 39 | } 40 | catch (error) { 41 | return res.status(400).send({ error }); 42 | } 43 | })); 44 | // @route GET api/status/recomFeeRate 45 | // @desc Get Recommended Fee Rate 46 | // @access Public 47 | exports.StatusNetworkRoute.get("/recommendFeeRate", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 48 | try { 49 | const recommendFeeRate = yield (0, mempool_1.getRecommendedFeeRate)(network_config_1.default.networkType); 50 | res.status(200).send({ recommendFeeRate }); 51 | } 52 | catch (error) { 53 | res.status(400).send({ error }); 54 | } 55 | })); 56 | -------------------------------------------------------------------------------- /src/services/utxo/utxo.splitPsbt.ts: -------------------------------------------------------------------------------- 1 | import * as Bitcoin from "bitcoinjs-lib"; 2 | import ecc from "@bitcoinerlab/secp256k1"; 3 | import { TESTNET } from "../../config/network.config"; 4 | import { SEND_UTXO_FEE_LIMIT } from "../../config/network.config"; 5 | 6 | Bitcoin.initEccLib(ecc); 7 | 8 | interface IUtxo { 9 | txid: string; 10 | vout: number; 11 | value: number; 12 | } 13 | 14 | export const redeemUtxoSplitPsbt = ( 15 | wallet: any, 16 | inputUtxoArray: Array, 17 | networkType: string 18 | ): Bitcoin.Psbt => { 19 | const psbt = new Bitcoin.Psbt({ 20 | network: 21 | networkType == TESTNET 22 | ? Bitcoin.networks.testnet 23 | : Bitcoin.networks.bitcoin, 24 | }); 25 | let inputUtxoSumValue: number = inputUtxoArray.reduce( 26 | (accumulator: number, currentValue: IUtxo) => 27 | accumulator + currentValue.value, 28 | 0 29 | ); 30 | let outputSize = Math.floor(inputUtxoSumValue / SEND_UTXO_FEE_LIMIT) - 1; 31 | inputUtxoArray.forEach((utxo) => { 32 | psbt.addInput({ 33 | hash: utxo.txid, 34 | index: utxo.vout, 35 | witnessUtxo: { 36 | value: utxo.value, 37 | script: wallet.output, 38 | }, 39 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 40 | }); 41 | }); 42 | for (let i = 0; i < outputSize; i++) { 43 | psbt.addOutput({ 44 | address: wallet.address, 45 | value: SEND_UTXO_FEE_LIMIT, 46 | }); 47 | } 48 | psbt.addOutput({ 49 | address: wallet.address, 50 | value: inputUtxoSumValue - (outputSize + 1) * SEND_UTXO_FEE_LIMIT, 51 | }); 52 | 53 | return psbt; 54 | }; 55 | 56 | export const utxoSplitPsbt = ( 57 | wallet: any, 58 | inputUtxoArray: Array, 59 | networkType: string, 60 | redeemFee: number 61 | ): Bitcoin.Psbt => { 62 | const psbt = new Bitcoin.Psbt({ 63 | network: 64 | networkType == TESTNET 65 | ? Bitcoin.networks.testnet 66 | : Bitcoin.networks.bitcoin, 67 | }); 68 | let inputUtxoSumValue: number = inputUtxoArray.reduce( 69 | (accumulator: number, currentValue: IUtxo) => 70 | accumulator + currentValue.value, 71 | 0 72 | ); 73 | let outputSize = Math.floor( 74 | (inputUtxoSumValue - redeemFee) / SEND_UTXO_FEE_LIMIT 75 | ); 76 | inputUtxoArray.forEach((utxo) => { 77 | psbt.addInput({ 78 | hash: utxo.txid, 79 | index: utxo.vout, 80 | witnessUtxo: { 81 | value: utxo.value, 82 | script: wallet.output, 83 | }, 84 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 85 | }); 86 | }); 87 | for (let i = 0; i < outputSize; i++) { 88 | psbt.addOutput({ 89 | address: wallet.address, 90 | value: SEND_UTXO_FEE_LIMIT, 91 | }); 92 | } 93 | psbt.addOutput({ 94 | address: wallet.address, 95 | value: Math.floor( 96 | inputUtxoSumValue - redeemFee - outputSize * SEND_UTXO_FEE_LIMIT 97 | ), 98 | }); 99 | 100 | return psbt; 101 | }; 102 | -------------------------------------------------------------------------------- /src/services/utxo/utxo.reinscribe.singleSendPsbt.ts: -------------------------------------------------------------------------------- 1 | import * as Bitcoin from "bitcoinjs-lib"; 2 | import ecc from "@bitcoinerlab/secp256k1"; 3 | import { TESTNET } from "../../config/network.config"; 4 | 5 | Bitcoin.initEccLib(ecc); 6 | 7 | interface IUtxo { 8 | txid: string; 9 | vout: number; 10 | value: number; 11 | } 12 | 13 | export const redeemReinscribeAndUtxoSendPsbt = ( 14 | wallet: any, 15 | userUtxo: IUtxo, 16 | networkType: string, 17 | amount: number, 18 | reinscriptionUTXO: IUtxo, 19 | fee: number, 20 | holderStatus: boolean 21 | ): Bitcoin.Psbt => { 22 | const psbt = new Bitcoin.Psbt({ 23 | network: 24 | networkType == TESTNET 25 | ? Bitcoin.networks.testnet 26 | : Bitcoin.networks.bitcoin, 27 | }); 28 | psbt.addInput({ 29 | hash: reinscriptionUTXO.txid, 30 | index: reinscriptionUTXO.vout, 31 | witnessUtxo: { 32 | value: reinscriptionUTXO.value, 33 | script: wallet.output, 34 | }, 35 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 36 | }); 37 | psbt.addInput({ 38 | hash: userUtxo.txid, 39 | index: userUtxo.vout, 40 | witnessUtxo: { 41 | value: userUtxo.value, 42 | script: wallet.output, 43 | }, 44 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 45 | }); 46 | 47 | psbt.addOutput({ 48 | address: wallet.address, 49 | value: amount, 50 | }); 51 | if (!holderStatus) { 52 | psbt.addOutput({ 53 | address: wallet.address, 54 | value: userUtxo.value + reinscriptionUTXO.value - fee - amount, 55 | }); 56 | } 57 | 58 | return psbt; 59 | }; 60 | 61 | export const ReinscribeAndUtxoSendPsbt = ( 62 | wallet: any, 63 | userUtxo: IUtxo, 64 | networkType: string, 65 | fee: number, 66 | address: string, 67 | amount: number, 68 | reinscriptionUTXO: IUtxo, 69 | holderStatus: boolean 70 | ): Bitcoin.Psbt => { 71 | const psbt = new Bitcoin.Psbt({ 72 | network: 73 | networkType == TESTNET 74 | ? Bitcoin.networks.testnet 75 | : Bitcoin.networks.bitcoin, 76 | }); 77 | 78 | psbt.addInput({ 79 | hash: reinscriptionUTXO.txid, 80 | index: reinscriptionUTXO.vout, 81 | witnessUtxo: { 82 | value: reinscriptionUTXO.value, 83 | script: wallet.output, 84 | }, 85 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 86 | }); 87 | 88 | psbt.addInput({ 89 | hash: userUtxo.txid, 90 | index: userUtxo.vout, 91 | witnessUtxo: { 92 | value: userUtxo.value, 93 | script: wallet.output, 94 | }, 95 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 96 | }); 97 | 98 | psbt.addOutput({ 99 | address: address, 100 | value: amount, 101 | }); 102 | 103 | if (!holderStatus) { 104 | psbt.addOutput({ 105 | address: wallet.address, 106 | value: userUtxo.value + reinscriptionUTXO.value - fee - amount, 107 | }); 108 | } 109 | return psbt; 110 | }; 111 | -------------------------------------------------------------------------------- /src/services/tapscript/fileTapScript.ts: -------------------------------------------------------------------------------- 1 | import { IFileInscription, IUtxo } from "../../utils/types"; 2 | import wallet from "../wallet/initializeWallet"; 3 | import { opcodes } from "bitcoinjs-lib"; 4 | import { getInscriptionInfo } from "../../utils/unisat.api"; 5 | import networkConfig from "../../config/network.config"; 6 | import cbor from "cbor"; 7 | import { splitBuffer } from "../../utils/buffer"; 8 | import { toXOnly } from "../../utils/buffer"; 9 | 10 | const keyPair = wallet.ecPair; 11 | 12 | export const fileTapScript = async (inscriptionData: IFileInscription) => { 13 | let tapScript: Array = [toXOnly(keyPair.publicKey), opcodes.OP_CHECKSIG]; 14 | let pointers: Array = []; 15 | 16 | inscriptionData.files.forEach((item: any, index: number) => { 17 | pointers.push(index * inscriptionData.padding); 18 | }); 19 | 20 | if (inscriptionData.parentId) { 21 | let parentInscriptionUTXO: IUtxo = await getInscriptionInfo( 22 | inscriptionData.parentId, 23 | networkConfig.networkType 24 | ); 25 | pointers = pointers.map((pointer, index) => { 26 | return pointer + parentInscriptionUTXO.value; 27 | }); 28 | } 29 | 30 | let pointerBuffer: Array = []; 31 | pointerBuffer = pointers.map((pointer, index) => { 32 | return Buffer.from(pointer.toString(16).padStart(4, "0"), "hex").reverse(); 33 | }); 34 | const parts = inscriptionData.parentId.split("i"); 35 | const parentInscriptionTransactionID = parts[0]; 36 | const inscriptionTransactionBuffer = Buffer.from( 37 | parentInscriptionTransactionID, 38 | "hex" 39 | ).reverse(); 40 | 41 | let parentInscriptionBuffer: Buffer; 42 | const index = parts[1]; 43 | 44 | if (parseInt(index, 10) != 0) { 45 | const indexBuffer = Buffer.from( 46 | parseInt(index, 10).toString(16).padStart(2, "0"), 47 | "hex" 48 | ).reverse(); 49 | parentInscriptionBuffer = Buffer.concat([ 50 | inscriptionTransactionBuffer, 51 | indexBuffer, 52 | ]); 53 | } else { 54 | parentInscriptionBuffer = inscriptionTransactionBuffer; 55 | } 56 | 57 | for (let i = 0; i < inscriptionData.files.length; i++) { 58 | const contentBuffer = inscriptionData.files[i].data; 59 | const contentBufferArray: Array = splitBuffer(contentBuffer, 450); 60 | 61 | let subScript: Array = []; 62 | subScript.push( 63 | opcodes.OP_FALSE, 64 | opcodes.OP_IF, 65 | Buffer.from("ord", "utf8"), 66 | 1, 67 | 1, 68 | Buffer.from(inscriptionData.files[i].mimetype, "utf8") 69 | ); 70 | 71 | subScript.push(1, 2, pointerBuffer[i]); 72 | 73 | subScript.push(1, 3, parentInscriptionBuffer); 74 | 75 | if (inscriptionData.metadata) { 76 | subScript.push(1, 5, cbor.encode(JSON.parse(inscriptionData.metadata))); 77 | } 78 | 79 | if (inscriptionData.metaprotocol) { 80 | subScript.push(1, 7, Buffer.from(inscriptionData.metaprotocol, "utf8")); 81 | } 82 | 83 | subScript.push(opcodes.OP_0); 84 | 85 | contentBufferArray.forEach((item: Buffer) => { 86 | subScript.push(item); 87 | }); 88 | 89 | subScript.push(opcodes.OP_ENDIF); 90 | 91 | tapScript.push(...subScript); 92 | } 93 | return tapScript; 94 | }; 95 | -------------------------------------------------------------------------------- /src/services/tapscript/textTapScript.ts: -------------------------------------------------------------------------------- 1 | import { ITextInscription, IUtxo } from "../../utils/types"; 2 | import wallet from "../wallet/initializeWallet"; 3 | import { opcodes } from "bitcoinjs-lib"; 4 | import { getInscriptionInfo } from "../../utils/unisat.api"; 5 | import networkConfig from "../../config/network.config"; 6 | import cbor from "cbor"; 7 | import { splitBuffer } from "../../utils/buffer"; 8 | import { toXOnly } from "../../utils/buffer"; 9 | 10 | const keyPair = wallet.ecPair; 11 | 12 | export const textTapScript = async (inscriptionData: ITextInscription) => { 13 | let tapScript: Array = [toXOnly(keyPair.publicKey), opcodes.OP_CHECKSIG]; 14 | let pointers: Array = []; 15 | 16 | inscriptionData.contents.forEach((item: string, index: number) => { 17 | pointers.push(index * inscriptionData.padding); 18 | }); 19 | 20 | if (inscriptionData.parentId) { 21 | let parentInscriptionUTXO: IUtxo = await getInscriptionInfo( 22 | inscriptionData.parentId, 23 | networkConfig.networkType 24 | ); 25 | pointers = pointers.map((pointer, index) => { 26 | return pointer + parentInscriptionUTXO.value; 27 | }); 28 | } 29 | 30 | let pointerBuffer: Array = []; 31 | pointerBuffer = pointers.map((pointer, index) => { 32 | return Buffer.from(pointer.toString(16).padStart(4, "0"), "hex").reverse(); 33 | }); 34 | const parts = inscriptionData.parentId.split("i"); 35 | const parentInscriptionTransactionID = parts[0]; 36 | const inscriptionTransactionBuffer = Buffer.from( 37 | parentInscriptionTransactionID, 38 | "hex" 39 | ).reverse(); 40 | 41 | let parentInscriptionBuffer: Buffer; 42 | const index = parts[1]; 43 | 44 | if (parseInt(index, 10) != 0) { 45 | const indexBuffer = Buffer.from( 46 | parseInt(index, 10).toString(16).padStart(2, "0"), 47 | "hex" 48 | ).reverse(); 49 | parentInscriptionBuffer = Buffer.concat([ 50 | inscriptionTransactionBuffer, 51 | indexBuffer, 52 | ]); 53 | } else { 54 | parentInscriptionBuffer = inscriptionTransactionBuffer; 55 | } 56 | 57 | for (let i = 0; i < inscriptionData.contents.length; i++) { 58 | const contentBuffer = Buffer.from(inscriptionData.contents[i]); 59 | const contentBufferArray: Array = splitBuffer(contentBuffer, 450); 60 | 61 | let subScript: Array = []; 62 | subScript.push( 63 | opcodes.OP_FALSE, 64 | opcodes.OP_IF, 65 | Buffer.from("ord", "utf8"), 66 | 1, 67 | 1, 68 | Buffer.from("text/plain", "utf8") 69 | ); 70 | 71 | subScript.push(1, 2, pointerBuffer[i]); 72 | 73 | if (inscriptionData.parentId) { 74 | subScript.push(1, 3, parentInscriptionBuffer); 75 | } 76 | 77 | if (inscriptionData.metadata) { 78 | subScript.push(1, 5, cbor.encode(JSON.parse(inscriptionData.metadata))); 79 | } 80 | 81 | if (inscriptionData.metaprotocol) { 82 | subScript.push(1, 7, Buffer.from(inscriptionData.metaprotocol, "utf8")); 83 | } 84 | 85 | subScript.push(opcodes.OP_0); 86 | 87 | contentBufferArray.forEach((item: Buffer) => { 88 | subScript.push(item); 89 | }); 90 | 91 | subScript.push(opcodes.OP_ENDIF); 92 | 93 | tapScript.push(...subScript); 94 | } 95 | return tapScript; 96 | }; 97 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __importDefault = (this && this.__importDefault) || function (mod) { 3 | return (mod && mod.__esModule) ? mod : { "default": mod }; 4 | }; 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.app = exports.iterator = exports.flagMutex = void 0; 7 | const express_1 = __importDefault(require("express")); 8 | const cors_1 = __importDefault(require("cors")); 9 | const dotenv_1 = __importDefault(require("dotenv")); 10 | const express_fileupload_1 = __importDefault(require("express-fileupload")); 11 | const swagger_ui_express_1 = __importDefault(require("swagger-ui-express")); 12 | const yamljs_1 = __importDefault(require("yamljs")); 13 | const bodyParser = require("body-parser"); 14 | const inscription_route_1 = require("./src/routes/inscription.route"); 15 | const status_network_route_1 = require("./src/routes/status.network.route"); 16 | const wallet_management_route_1 = require("./src/routes/wallet.management.route"); 17 | const estimate_fee_route_1 = require("./src/routes/estimate.fee.route"); 18 | const send_ordinals_route_1 = require("./src/routes/send.ordinals.route"); 19 | const http_1 = __importDefault(require("http")); 20 | const network_config_1 = require("./src/config/network.config"); 21 | const async_mutex_1 = require("async-mutex"); 22 | exports.flagMutex = new async_mutex_1.Mutex(); 23 | exports.iterator = new async_mutex_1.Mutex(); 24 | /* 25 | * Load up and parse configuration details from 26 | * the `.env` file to the `process.env` 27 | * object of Node.js 28 | */ 29 | dotenv_1.default.config(); 30 | /* 31 | * Create an Express application and get the 32 | * value of the PORT environment variable 33 | * from the `process.env` 34 | */ 35 | let swaggerDocument = ""; 36 | if (process.env.NETWORKTYPE == network_config_1.TESTNET) { 37 | swaggerDocument = yamljs_1.default.load("swagger_devnet.yaml"); 38 | } 39 | else { 40 | swaggerDocument = yamljs_1.default.load("swagger_mainnet.yaml"); 41 | } 42 | exports.app = (0, express_1.default)(); 43 | exports.app.locals.utxoflag = false; 44 | const server = http_1.default.createServer(exports.app); 45 | const port = process.env.PORT || 8081; 46 | exports.app.use((0, express_fileupload_1.default)()); 47 | exports.app.use(express_1.default.json({ limit: "50mb" })); 48 | exports.app.use(express_1.default.urlencoded({ limit: "50mb", extended: true })); 49 | exports.app.use(bodyParser.json({ limit: "50mb" })); 50 | exports.app.use(bodyParser.urlencoded({ limit: "50mb", extended: true })); 51 | exports.app.use((0, cors_1.default)()); 52 | exports.app.use("/api/inscribe", inscription_route_1.InscriptionRouter); 53 | exports.app.use("/api/estimate", estimate_fee_route_1.EstimateFeeRouter); 54 | exports.app.use("/api/status", status_network_route_1.StatusNetworkRoute); 55 | exports.app.use("/api/wallet", wallet_management_route_1.WalletManageRoute); 56 | exports.app.use("/api/sendOrdinal", send_ordinals_route_1.SendOrdinalRouter); 57 | exports.app.use("/api-docs", swagger_ui_express_1.default.serve, swagger_ui_express_1.default.setup(swaggerDocument, { explorer: true })); 58 | exports.app.locals.iterator = 0; 59 | /* Start the Express app and listen 60 | for incoming requests on the specified port */ 61 | server.listen(port, () => { 62 | console.log(`[server]: Server is running at http://localhost:${port}`); 63 | }); 64 | -------------------------------------------------------------------------------- /dist/src/services/wallet/WIFWallet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __importDefault = (this && this.__importDefault) || function (mod) { 26 | return (mod && mod.__esModule) ? mod : { "default": mod }; 27 | }; 28 | Object.defineProperty(exports, "__esModule", { value: true }); 29 | exports.WIFWallet = void 0; 30 | const bitcoin = __importStar(require("bitcoinjs-lib")); 31 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 32 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 33 | const ecpair_1 = __importDefault(require("ecpair")); 34 | const dotenv_1 = __importDefault(require("dotenv")); 35 | const crypto_utils_1 = require("@cmdcode/crypto-utils"); 36 | const network_config_1 = require("../../config/network.config"); 37 | dotenv_1.default.config(); 38 | (0, bitcoinjs_lib_1.initEccLib)(secp256k1_1.default); 39 | const ECPair = (0, ecpair_1.default)(secp256k1_1.default); 40 | class WIFWallet { 41 | constructor(walletParam) { 42 | var _a; 43 | if (walletParam.networkType == network_config_1.TESTNET) { 44 | this.network = bitcoinjs_lib_1.networks.testnet; 45 | } 46 | else { 47 | this.network = bitcoinjs_lib_1.networks.bitcoin; 48 | } 49 | this.ecPair = ECPair.fromWIF(walletParam.privateKey, this.network); 50 | this.secret = (_a = this.ecPair.privateKey) === null || _a === void 0 ? void 0 : _a.toString("hex"); 51 | this.seckey = new crypto_utils_1.SecretKey(this.secret, { type: "taproot" }); 52 | this.pubkey = this.seckey.pub; 53 | const { address, output } = bitcoin.payments.p2tr({ 54 | internalPubkey: this.ecPair.publicKey.subarray(1, 33), 55 | network: this.network, 56 | }); 57 | this.address = address; 58 | this.output = output; 59 | this.publicKey = this.ecPair.publicKey.toString("hex"); 60 | } 61 | signPsbt(psbt, ecPair) { 62 | const tweakedChildNode = ecPair.tweak(bitcoin.crypto.taggedHash("TapTweak", ecPair.publicKey.subarray(1, 33))); 63 | for (let i = 0; i < psbt.inputCount; i++) { 64 | psbt.signInput(i, tweakedChildNode); 65 | psbt.validateSignaturesOfInput(i, () => true); 66 | psbt.finalizeInput(i); 67 | } 68 | return psbt; 69 | } 70 | } 71 | exports.WIFWallet = WIFWallet; 72 | -------------------------------------------------------------------------------- /dist/src/services/utxo/utxo.singleSend.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.singleSendUTXO = void 0; 39 | const network_config_1 = __importStar(require("../../config/network.config")); 40 | const Bitcoin = __importStar(require("bitcoinjs-lib")); 41 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 42 | const dotenv_1 = __importDefault(require("dotenv")); 43 | const utxo_singleSendPsbt_1 = require("./utxo.singleSendPsbt"); 44 | const WIFWallet_1 = require("../wallet/WIFWallet"); 45 | dotenv_1.default.config(); 46 | Bitcoin.initEccLib(secp256k1_1.default); 47 | const networkType = network_config_1.default.networkType; 48 | let wallet; 49 | const privateKey = process.env.PRIVATE_KEY; 50 | wallet = new WIFWallet_1.WIFWallet({ networkType: networkType, privateKey: privateKey }); 51 | const singleSendUTXO = (address, feeRate, userUtxo, amount, holderStatus) => __awaiter(void 0, void 0, void 0, function* () { 52 | let redeemFee = network_config_1.SEND_UTXO_FEE_LIMIT; 53 | let redeemPsbt = (0, utxo_singleSendPsbt_1.redeemSingleSendUTXOPsbt)(wallet, userUtxo, networkType, amount, redeemFee, holderStatus); 54 | redeemPsbt = wallet.signPsbt(redeemPsbt, wallet.ecPair); 55 | redeemFee = redeemPsbt.extractTransaction(true).virtualSize() * feeRate; 56 | let psbt = (0, utxo_singleSendPsbt_1.singleSendUTXOPsbt)(wallet, userUtxo, networkType, redeemFee, address, amount, holderStatus); 57 | let signedPsbt = wallet.signPsbt(psbt, wallet.ecPair); 58 | const tx = signedPsbt.extractTransaction(true); 59 | return { isSuccess: true, data: tx }; 60 | }); 61 | exports.singleSendUTXO = singleSendUTXO; 62 | -------------------------------------------------------------------------------- /dist/src/services/utxo/utxo.singleSendPsbt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __importDefault = (this && this.__importDefault) || function (mod) { 26 | return (mod && mod.__esModule) ? mod : { "default": mod }; 27 | }; 28 | Object.defineProperty(exports, "__esModule", { value: true }); 29 | exports.singleSendUTXOPsbt = exports.redeemSingleSendUTXOPsbt = void 0; 30 | const Bitcoin = __importStar(require("bitcoinjs-lib")); 31 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 32 | const network_config_1 = require("../../config/network.config"); 33 | Bitcoin.initEccLib(secp256k1_1.default); 34 | const redeemSingleSendUTXOPsbt = (wallet, userUtxo, networkType, amount, fee, holderStatus) => { 35 | const psbt = new Bitcoin.Psbt({ 36 | network: networkType == network_config_1.TESTNET 37 | ? Bitcoin.networks.testnet 38 | : Bitcoin.networks.bitcoin, 39 | }); 40 | psbt.addInput({ 41 | hash: userUtxo.txid, 42 | index: userUtxo.vout, 43 | witnessUtxo: { 44 | value: userUtxo.value, 45 | script: wallet.output, 46 | }, 47 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 48 | }); 49 | psbt.addOutput({ 50 | address: wallet.address, 51 | value: amount, 52 | }); 53 | if (!holderStatus) { 54 | psbt.addOutput({ 55 | address: wallet.address, 56 | value: userUtxo.value - fee - amount, 57 | }); 58 | } 59 | return psbt; 60 | }; 61 | exports.redeemSingleSendUTXOPsbt = redeemSingleSendUTXOPsbt; 62 | const singleSendUTXOPsbt = (wallet, userUtxo, networkType, fee, address, amount, holderStatus) => { 63 | const psbt = new Bitcoin.Psbt({ 64 | network: networkType == network_config_1.TESTNET 65 | ? Bitcoin.networks.testnet 66 | : Bitcoin.networks.bitcoin, 67 | }); 68 | psbt.addInput({ 69 | hash: userUtxo.txid, 70 | index: userUtxo.vout, 71 | witnessUtxo: { 72 | value: userUtxo.value, 73 | script: wallet.output, 74 | }, 75 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 76 | }); 77 | psbt.addOutput({ 78 | address: address, 79 | value: amount, 80 | }); 81 | if (!holderStatus) { 82 | psbt.addOutput({ 83 | address: wallet.address, 84 | value: userUtxo.value - fee - amount, 85 | }); 86 | } 87 | return psbt; 88 | }; 89 | exports.singleSendUTXOPsbt = singleSendUTXOPsbt; 90 | -------------------------------------------------------------------------------- /src/services/tapscript/delegateTapScript.ts: -------------------------------------------------------------------------------- 1 | import { IDelegateInscription, IUtxo } from "../../utils/types"; 2 | import wallet from "../wallet/initializeWallet"; 3 | import { opcodes } from "bitcoinjs-lib"; 4 | import { getInscriptionInfo } from "../../utils/unisat.api"; 5 | import networkConfig from "../../config/network.config"; 6 | import cbor from "cbor"; 7 | import { toXOnly } from "../../utils/buffer"; 8 | 9 | const keyPair = wallet.ecPair; 10 | 11 | export const delegateTapScript = async ( 12 | inscriptionData: IDelegateInscription 13 | ) => { 14 | let tapScript: Array = [toXOnly(keyPair.publicKey), opcodes.OP_CHECKSIG]; 15 | let pointers: Array = []; 16 | 17 | inscriptionData.delegateIds.forEach((item: string, index: number) => { 18 | pointers.push(index * inscriptionData.padding); 19 | }); 20 | 21 | if (inscriptionData.parentId) { 22 | let parentInscriptionUTXO: IUtxo = await getInscriptionInfo( 23 | inscriptionData.parentId, 24 | networkConfig.networkType 25 | ); 26 | pointers = pointers.map((pointer, index) => { 27 | return pointer + parentInscriptionUTXO.value; 28 | }); 29 | } 30 | 31 | let pointerBuffer: Array = []; 32 | pointerBuffer = pointers.map((pointer, index) => { 33 | return Buffer.from(pointer.toString(16).padStart(4, "0"), "hex").reverse(); 34 | }); 35 | const parts = inscriptionData.parentId.split("i"); 36 | const parentInscriptionTransactionID = parts[0]; 37 | const inscriptionTransactionBuffer = Buffer.from( 38 | parentInscriptionTransactionID, 39 | "hex" 40 | ).reverse(); 41 | 42 | let parentInscriptionBuffer: Buffer; 43 | const index = parts[1]; 44 | 45 | if (parseInt(index, 10) != 0) { 46 | const indexBuffer = Buffer.from( 47 | parseInt(index, 10).toString(16).padStart(2, "0"), 48 | "hex" 49 | ).reverse(); 50 | parentInscriptionBuffer = Buffer.concat([ 51 | inscriptionTransactionBuffer, 52 | indexBuffer, 53 | ]); 54 | } else { 55 | parentInscriptionBuffer = inscriptionTransactionBuffer; 56 | } 57 | 58 | const DelegateIDparts = inscriptionData.delegateIds[0].split("i"); 59 | const delegateInscriptionTransactionID = DelegateIDparts[0]; 60 | const DelegateinscriptionTransactionBuffer = Buffer.from( 61 | delegateInscriptionTransactionID, 62 | "hex" 63 | ).reverse(); 64 | 65 | let DelegateInscriptionBuffer: Buffer; 66 | const DelegateIndex = DelegateIDparts[1]; 67 | 68 | if (parseInt(DelegateIndex, 10) != 0) { 69 | const DelegateIndexBuffer = Buffer.from( 70 | parseInt(DelegateIndex, 10).toString(16).padStart(2, "0"), 71 | "hex" 72 | ).reverse(); 73 | DelegateInscriptionBuffer = Buffer.concat([ 74 | DelegateinscriptionTransactionBuffer, 75 | DelegateIndexBuffer, 76 | ]); 77 | } else { 78 | DelegateInscriptionBuffer = DelegateinscriptionTransactionBuffer; 79 | } 80 | 81 | for (let i = 0; i < inscriptionData.delegateIds.length; i++) { 82 | let subScript: Array = []; 83 | subScript.push(opcodes.OP_FALSE, opcodes.OP_IF, Buffer.from("ord", "utf8")); 84 | 85 | subScript.push(1, 2, pointerBuffer[i]); 86 | 87 | subScript.push(1, 3, parentInscriptionBuffer); 88 | 89 | if (inscriptionData.metadata) { 90 | subScript.push(1, 5, cbor.encode(JSON.parse(inscriptionData.metadata))); 91 | } 92 | 93 | if (inscriptionData.metaprotocol) { 94 | subScript.push(1, 7, Buffer.from(inscriptionData.metaprotocol, "utf8")); 95 | } 96 | subScript.push(1, 11, DelegateInscriptionBuffer); 97 | subScript.push(opcodes.OP_ENDIF); 98 | tapScript.push(...subScript); 99 | } 100 | return tapScript; 101 | }; 102 | -------------------------------------------------------------------------------- /dist/src/services/utxo/utxo.reinscribe.singleSend.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.reinscriptionAndUTXOSend = void 0; 39 | const network_config_1 = __importStar(require("../../config/network.config")); 40 | const Bitcoin = __importStar(require("bitcoinjs-lib")); 41 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 42 | const dotenv_1 = __importDefault(require("dotenv")); 43 | const utxo_reinscribe_singleSendPsbt_1 = require("./utxo.reinscribe.singleSendPsbt"); 44 | const WIFWallet_1 = require("../wallet/WIFWallet"); 45 | const unisat_api_1 = require("../../utils/unisat.api"); 46 | dotenv_1.default.config(); 47 | Bitcoin.initEccLib(secp256k1_1.default); 48 | const networkType = network_config_1.default.networkType; 49 | let wallet; 50 | const privateKey = process.env.PRIVATE_KEY; 51 | wallet = new WIFWallet_1.WIFWallet({ networkType: networkType, privateKey: privateKey }); 52 | const reinscriptionAndUTXOSend = (reinscriptionId, address, feeRate, userUtxo, amount, holderStatus) => __awaiter(void 0, void 0, void 0, function* () { 53 | console.log("reinscriptionId => ", reinscriptionId); 54 | const reinscriptionUTXO = yield (0, unisat_api_1.getInscriptionInfo)(reinscriptionId, network_config_1.default.networkType); 55 | console.log("reinscriptionUTXO => ", reinscriptionUTXO); 56 | let redeemFee = network_config_1.SEND_UTXO_FEE_LIMIT; 57 | let redeemPsbt = (0, utxo_reinscribe_singleSendPsbt_1.redeemReinscribeAndUtxoSendPsbt)(wallet, userUtxo, networkType, amount, reinscriptionUTXO, redeemFee, holderStatus); 58 | redeemPsbt = wallet.signPsbt(redeemPsbt, wallet.ecPair); 59 | redeemFee = redeemPsbt.extractTransaction(true).virtualSize() * feeRate; 60 | let psbt = (0, utxo_reinscribe_singleSendPsbt_1.ReinscribeAndUtxoSendPsbt)(wallet, userUtxo, networkType, redeemFee, address, amount, reinscriptionUTXO, holderStatus); 61 | let signedPsbt = wallet.signPsbt(psbt, wallet.ecPair); 62 | const tx = signedPsbt.extractTransaction(true); 63 | return { isSuccess: true, data: tx }; 64 | }); 65 | exports.reinscriptionAndUTXOSend = reinscriptionAndUTXOSend; 66 | -------------------------------------------------------------------------------- /dist/src/services/tapscript/fileTapScript.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | exports.fileTapScript = void 0; 16 | const initializeWallet_1 = __importDefault(require("../wallet/initializeWallet")); 17 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 18 | const unisat_api_1 = require("../../utils/unisat.api"); 19 | const network_config_1 = __importDefault(require("../../config/network.config")); 20 | const cbor_1 = __importDefault(require("cbor")); 21 | const buffer_1 = require("../../utils/buffer"); 22 | const buffer_2 = require("../../utils/buffer"); 23 | const keyPair = initializeWallet_1.default.ecPair; 24 | const fileTapScript = (inscriptionData) => __awaiter(void 0, void 0, void 0, function* () { 25 | let tapScript = [(0, buffer_2.toXOnly)(keyPair.publicKey), bitcoinjs_lib_1.opcodes.OP_CHECKSIG]; 26 | let pointers = []; 27 | inscriptionData.files.forEach((item, index) => { 28 | pointers.push(index * inscriptionData.padding); 29 | }); 30 | if (inscriptionData.parentId) { 31 | let parentInscriptionUTXO = yield (0, unisat_api_1.getInscriptionInfo)(inscriptionData.parentId, network_config_1.default.networkType); 32 | pointers = pointers.map((pointer, index) => { 33 | return pointer + parentInscriptionUTXO.value; 34 | }); 35 | } 36 | let pointerBuffer = []; 37 | pointerBuffer = pointers.map((pointer, index) => { 38 | return Buffer.from(pointer.toString(16).padStart(4, "0"), "hex").reverse(); 39 | }); 40 | const parts = inscriptionData.parentId.split("i"); 41 | const parentInscriptionTransactionID = parts[0]; 42 | const inscriptionTransactionBuffer = Buffer.from(parentInscriptionTransactionID, "hex").reverse(); 43 | let parentInscriptionBuffer; 44 | const index = parts[1]; 45 | if (parseInt(index, 10) != 0) { 46 | const indexBuffer = Buffer.from(parseInt(index, 10).toString(16).padStart(2, "0"), "hex").reverse(); 47 | parentInscriptionBuffer = Buffer.concat([ 48 | inscriptionTransactionBuffer, 49 | indexBuffer, 50 | ]); 51 | } 52 | else { 53 | parentInscriptionBuffer = inscriptionTransactionBuffer; 54 | } 55 | for (let i = 0; i < inscriptionData.files.length; i++) { 56 | const contentBuffer = inscriptionData.files[i].data; 57 | const contentBufferArray = (0, buffer_1.splitBuffer)(contentBuffer, 450); 58 | let subScript = []; 59 | subScript.push(bitcoinjs_lib_1.opcodes.OP_FALSE, bitcoinjs_lib_1.opcodes.OP_IF, Buffer.from("ord", "utf8"), 1, 1, Buffer.from(inscriptionData.files[i].mimetype, "utf8")); 60 | subScript.push(1, 2, pointerBuffer[i]); 61 | subScript.push(1, 3, parentInscriptionBuffer); 62 | if (inscriptionData.metadata) { 63 | subScript.push(1, 5, cbor_1.default.encode(JSON.parse(inscriptionData.metadata))); 64 | } 65 | if (inscriptionData.metaprotocol) { 66 | subScript.push(1, 7, Buffer.from(inscriptionData.metaprotocol, "utf8")); 67 | } 68 | subScript.push(bitcoinjs_lib_1.opcodes.OP_0); 69 | contentBufferArray.forEach((item) => { 70 | subScript.push(item); 71 | }); 72 | subScript.push(bitcoinjs_lib_1.opcodes.OP_ENDIF); 73 | tapScript.push(...subScript); 74 | } 75 | return tapScript; 76 | }); 77 | exports.fileTapScript = fileTapScript; 78 | -------------------------------------------------------------------------------- /dist/src/services/tapscript/textTapScript.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | exports.textTapScript = void 0; 16 | const initializeWallet_1 = __importDefault(require("../wallet/initializeWallet")); 17 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 18 | const unisat_api_1 = require("../../utils/unisat.api"); 19 | const network_config_1 = __importDefault(require("../../config/network.config")); 20 | const cbor_1 = __importDefault(require("cbor")); 21 | const buffer_1 = require("../../utils/buffer"); 22 | const buffer_2 = require("../../utils/buffer"); 23 | const keyPair = initializeWallet_1.default.ecPair; 24 | const textTapScript = (inscriptionData) => __awaiter(void 0, void 0, void 0, function* () { 25 | let tapScript = [(0, buffer_2.toXOnly)(keyPair.publicKey), bitcoinjs_lib_1.opcodes.OP_CHECKSIG]; 26 | let pointers = []; 27 | inscriptionData.contents.forEach((item, index) => { 28 | pointers.push(index * inscriptionData.padding); 29 | }); 30 | if (inscriptionData.parentId) { 31 | let parentInscriptionUTXO = yield (0, unisat_api_1.getInscriptionInfo)(inscriptionData.parentId, network_config_1.default.networkType); 32 | pointers = pointers.map((pointer, index) => { 33 | return pointer + parentInscriptionUTXO.value; 34 | }); 35 | } 36 | let pointerBuffer = []; 37 | pointerBuffer = pointers.map((pointer, index) => { 38 | return Buffer.from(pointer.toString(16).padStart(4, "0"), "hex").reverse(); 39 | }); 40 | const parts = inscriptionData.parentId.split("i"); 41 | const parentInscriptionTransactionID = parts[0]; 42 | const inscriptionTransactionBuffer = Buffer.from(parentInscriptionTransactionID, "hex").reverse(); 43 | let parentInscriptionBuffer; 44 | const index = parts[1]; 45 | if (parseInt(index, 10) != 0) { 46 | const indexBuffer = Buffer.from(parseInt(index, 10).toString(16).padStart(2, "0"), "hex").reverse(); 47 | parentInscriptionBuffer = Buffer.concat([ 48 | inscriptionTransactionBuffer, 49 | indexBuffer, 50 | ]); 51 | } 52 | else { 53 | parentInscriptionBuffer = inscriptionTransactionBuffer; 54 | } 55 | for (let i = 0; i < inscriptionData.contents.length; i++) { 56 | const contentBuffer = Buffer.from(inscriptionData.contents[i]); 57 | const contentBufferArray = (0, buffer_1.splitBuffer)(contentBuffer, 450); 58 | let subScript = []; 59 | subScript.push(bitcoinjs_lib_1.opcodes.OP_FALSE, bitcoinjs_lib_1.opcodes.OP_IF, Buffer.from("ord", "utf8"), 1, 1, Buffer.from("text/plain", "utf8")); 60 | subScript.push(1, 2, pointerBuffer[i]); 61 | if (inscriptionData.parentId) { 62 | subScript.push(1, 3, parentInscriptionBuffer); 63 | } 64 | if (inscriptionData.metadata) { 65 | subScript.push(1, 5, cbor_1.default.encode(JSON.parse(inscriptionData.metadata))); 66 | } 67 | if (inscriptionData.metaprotocol) { 68 | subScript.push(1, 7, Buffer.from(inscriptionData.metaprotocol, "utf8")); 69 | } 70 | subScript.push(bitcoinjs_lib_1.opcodes.OP_0); 71 | contentBufferArray.forEach((item) => { 72 | subScript.push(item); 73 | }); 74 | subScript.push(bitcoinjs_lib_1.opcodes.OP_ENDIF); 75 | tapScript.push(...subScript); 76 | } 77 | return tapScript; 78 | }); 79 | exports.textTapScript = textTapScript; 80 | -------------------------------------------------------------------------------- /dist/src/services/psbt/sendOrdinalPsbt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.sendOrdinalBTCPsbt = void 0; 39 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 40 | const network_config_1 = __importStar(require("../../config/network.config")); 41 | const initializeWallet_1 = __importDefault(require("../wallet/initializeWallet")); 42 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 43 | const utxo_management_1 = require("../../services/utxo/utxo.management"); 44 | const utxo_ordinalsSendPsbt_1 = require("../../services/utxo/utxo.ordinalsSendPsbt"); 45 | const unisat_api_1 = require("../../utils/unisat.api"); 46 | (0, bitcoinjs_lib_1.initEccLib)(secp256k1_1.default); 47 | const sendOrdinalBTCPsbt = (sendingOrdinalData) => __awaiter(void 0, void 0, void 0, function* () { 48 | const utxos = yield (0, unisat_api_1.getBtcUtxoInfo)(sendingOrdinalData.paymentAddress, network_config_1.default.networkType); 49 | let response = (0, utxo_management_1.getSendBTCUTXOArray)(utxos, sendingOrdinalData.btcAmount + network_config_1.SEND_UTXO_FEE_LIMIT); 50 | if (!response.isSuccess) { 51 | return { isSuccess: false, data: "No enough balance on your wallet." }; 52 | } 53 | let selectedUtxos = response.data; 54 | let redeemFee = network_config_1.SEND_UTXO_FEE_LIMIT; 55 | for (let i = 0; i < 3; i++) { 56 | let redeemPsbt = yield (0, utxo_ordinalsSendPsbt_1.RedeemOrdinalsUtxoSendPsbt)(selectedUtxos, network_config_1.default.networkType, sendingOrdinalData, redeemFee); 57 | redeemPsbt = initializeWallet_1.default.signPsbt(redeemPsbt, initializeWallet_1.default.ecPair); 58 | redeemFee = 59 | redeemPsbt.extractTransaction(true).virtualSize() * 60 | sendingOrdinalData.feeRate; 61 | response = (0, utxo_management_1.getSendBTCUTXOArray)(utxos, sendingOrdinalData.btcAmount + redeemFee); 62 | if (!response.isSuccess) { 63 | return { isSuccess: false, data: "No enough balance on your wallet." }; 64 | } 65 | selectedUtxos = response.data; 66 | } 67 | let psbt = yield (0, utxo_ordinalsSendPsbt_1.OrdinalsUtxoSendPsbt)(selectedUtxos, network_config_1.default.networkType, sendingOrdinalData, redeemFee); 68 | return { isSuccess: true, data: psbt }; 69 | }); 70 | exports.sendOrdinalBTCPsbt = sendOrdinalBTCPsbt; 71 | -------------------------------------------------------------------------------- /dist/src/services/utxo/utxo.split.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.splitUTXO = void 0; 39 | const network_config_1 = __importStar(require("../../config/network.config")); 40 | const mempool_1 = require("../../utils/mempool"); 41 | const Bitcoin = __importStar(require("bitcoinjs-lib")); 42 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 43 | const dotenv_1 = __importDefault(require("dotenv")); 44 | const WIFWallet_1 = require("../wallet/WIFWallet"); 45 | const mempool_2 = require("../../utils/mempool"); 46 | const utxo_splitPsbt_1 = require("./utxo.splitPsbt"); 47 | const unisat_api_1 = require("../../utils/unisat.api"); 48 | dotenv_1.default.config(); 49 | Bitcoin.initEccLib(secp256k1_1.default); 50 | const networkType = network_config_1.default.networkType; 51 | let wallet; 52 | const privateKey = process.env.PRIVATE_KEY; 53 | wallet = new WIFWallet_1.WIFWallet({ networkType: networkType, privateKey: privateKey }); 54 | const splitUTXO = () => __awaiter(void 0, void 0, void 0, function* () { 55 | const recomFeeRate = yield (0, mempool_2.getRecommendedFeeRate)(networkType); 56 | const splitFeeRate = recomFeeRate.fastestFee * 1.1; 57 | const utxos = yield (0, unisat_api_1.getBtcUtxoInfo)(wallet.address, networkType); 58 | // let utxos = await getUtxos(wallet.address, networkType) 59 | // utxos = utxos.filter((utxo: IUtxo, index: number) => utxo.value > 5000) 60 | const filteredUtxos = utxos.filter((utxo) => utxo.value > network_config_1.SEND_UTXO_FEE_LIMIT); 61 | if (filteredUtxos.length) { 62 | let redeemPsbt = (0, utxo_splitPsbt_1.redeemUtxoSplitPsbt)(wallet, filteredUtxos, networkType); 63 | redeemPsbt = wallet.signPsbt(redeemPsbt, wallet.ecPair); 64 | let redeemFee = redeemPsbt.extractTransaction(true).virtualSize() * splitFeeRate; 65 | let psbt = (0, utxo_splitPsbt_1.utxoSplitPsbt)(wallet, filteredUtxos, networkType, redeemFee); 66 | let signedPsbt = wallet.signPsbt(psbt, wallet.ecPair); 67 | const txHex = signedPsbt.extractTransaction(true).toHex(); 68 | const txId = yield (0, mempool_1.pushBTCpmt)(txHex, networkType); 69 | return { isSuccess: true, data: txId }; 70 | } 71 | else { 72 | return { isSuccess: false, data: "Wallet UTXO split is failed!" }; 73 | } 74 | }); 75 | exports.splitUTXO = splitUTXO; 76 | -------------------------------------------------------------------------------- /src/utils/mempool.ts: -------------------------------------------------------------------------------- 1 | import axios, { type AxiosError } from "axios"; 2 | import { TESTNET } from "../config/network.config"; 3 | import { Response } from "express"; 4 | 5 | interface IUtxo { 6 | txid: string; 7 | vout: number; 8 | value: number; 9 | } 10 | 11 | export const getUtxos = async ( 12 | address: string, 13 | networkType: string 14 | ): Promise => { 15 | try { 16 | const url = `https://mempool.space/${ 17 | networkType == TESTNET ? "testnet/" : "" 18 | }api/address/${address}/utxo`; 19 | const res = await axios.get(url); 20 | const confirmedUtxos: IUtxo[] = []; 21 | const unConfirmedUtxos: IUtxo[] = []; 22 | 23 | res.data.forEach((utxoData: any) => { 24 | if (utxoData.status.confirmed) { 25 | confirmedUtxos.push({ 26 | txid: utxoData.txid, 27 | vout: utxoData.vout, 28 | value: utxoData.value, 29 | }); 30 | } else { 31 | unConfirmedUtxos.push({ 32 | txid: utxoData.txid, 33 | vout: utxoData.vout, 34 | value: utxoData.value, 35 | }); 36 | } 37 | }); 38 | return [...confirmedUtxos, ...unConfirmedUtxos]; 39 | } catch (err: any) { 40 | console.log("Get Utxos Error"); 41 | } 42 | }; 43 | 44 | export const pushBTCpmt = async (rawtx: any, networkType: string) => { 45 | const txid = await postData( 46 | `https://mempool.space/${networkType == TESTNET ? "testnet/" : ""}api/tx`, 47 | rawtx 48 | ); 49 | return txid; 50 | }; 51 | 52 | const postData = async ( 53 | url: string, 54 | json: any, 55 | content_type = "text/plain", 56 | apikey = "" 57 | ): Promise => { 58 | try { 59 | const headers: any = {}; 60 | if (content_type) headers["Content-Type"] = content_type; 61 | if (apikey) headers["X-Api-Key"] = apikey; 62 | const res = await axios.post(url, json, { 63 | headers, 64 | }); 65 | return res.data as string; 66 | } catch (err: any) { 67 | console.log("Push Transaction Error"); 68 | console.log(err.response.data); 69 | } 70 | }; 71 | 72 | export const getPrice = async (networkType: string) => { 73 | try { 74 | console.log("https://mempool.space/api/v1/prices"); 75 | 76 | const res: any = await axios.get("https://mempool.space/api/v1/prices"); 77 | return res.data; 78 | } catch (error: any) { 79 | console.log("Get Price Error!"); 80 | } 81 | }; 82 | 83 | export const getBlockHeight = async (networkType: string) => { 84 | try { 85 | const url = `https://mempool.space/api/blocks/tip/height`; 86 | const res = await axios.get(url); 87 | return res.data; 88 | } catch (error: any) { 89 | console.log("Get Price Error!"); 90 | } 91 | }; 92 | 93 | export const getFeeRate = async (networkType: string, response: Response) => { 94 | try { 95 | const height = await getBlockHeight(networkType); 96 | const url = `https://mempool.space/api/v1/blocks/${height}`; 97 | console.log(url); 98 | const blockData: any = await axios.get(url); 99 | const feeRateData = blockData.data.map((item: any) => { 100 | return { timestamp: item.timestamp, avgFeeRate: item.extras.avgFeeRate }; 101 | }); 102 | 103 | return response.status(200).send({ feeRateData }); 104 | } catch (error: any) { 105 | return response.status(400).send({ 106 | type: 1, 107 | data: "Get Fee Rate Error!", 108 | }); 109 | } 110 | }; 111 | 112 | export const getRecommendedFeeRate = async (networkType: string) => { 113 | try { 114 | const url = `https://mempool.space/${ 115 | networkType == TESTNET ? "testnet/" : "" 116 | }api/v1/fees/recommended`; 117 | const response: any = await axios.get(url); 118 | const recommendFeeRate = response.data; 119 | 120 | return recommendFeeRate; 121 | } catch (error: any) { 122 | console.log("Get Recommend Fee Rate Error!"); 123 | } 124 | }; 125 | 126 | export const getTxHex = async ( 127 | txid: string, 128 | networkType: string 129 | ): Promise => { 130 | try { 131 | const url = `https://mempool.space/${ 132 | networkType == TESTNET ? "testnet/" : "" 133 | }api/tx/${txid}/hex`; 134 | const res = await axios.get(url); 135 | 136 | const data = res.data; 137 | return data; 138 | } catch (err: any) { 139 | console.log("Get Tx Hex Error"); 140 | } 141 | }; 142 | -------------------------------------------------------------------------------- /dist/src/services/utxo/utxo.splitPsbt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __importDefault = (this && this.__importDefault) || function (mod) { 26 | return (mod && mod.__esModule) ? mod : { "default": mod }; 27 | }; 28 | Object.defineProperty(exports, "__esModule", { value: true }); 29 | exports.utxoSplitPsbt = exports.redeemUtxoSplitPsbt = void 0; 30 | const Bitcoin = __importStar(require("bitcoinjs-lib")); 31 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 32 | const network_config_1 = require("../../config/network.config"); 33 | const network_config_2 = require("../../config/network.config"); 34 | Bitcoin.initEccLib(secp256k1_1.default); 35 | const redeemUtxoSplitPsbt = (wallet, inputUtxoArray, networkType) => { 36 | const psbt = new Bitcoin.Psbt({ 37 | network: networkType == network_config_1.TESTNET 38 | ? Bitcoin.networks.testnet 39 | : Bitcoin.networks.bitcoin, 40 | }); 41 | let inputUtxoSumValue = inputUtxoArray.reduce((accumulator, currentValue) => accumulator + currentValue.value, 0); 42 | let outputSize = Math.floor(inputUtxoSumValue / network_config_2.SEND_UTXO_FEE_LIMIT) - 1; 43 | inputUtxoArray.forEach((utxo) => { 44 | psbt.addInput({ 45 | hash: utxo.txid, 46 | index: utxo.vout, 47 | witnessUtxo: { 48 | value: utxo.value, 49 | script: wallet.output, 50 | }, 51 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 52 | }); 53 | }); 54 | for (let i = 0; i < outputSize; i++) { 55 | psbt.addOutput({ 56 | address: wallet.address, 57 | value: network_config_2.SEND_UTXO_FEE_LIMIT, 58 | }); 59 | } 60 | psbt.addOutput({ 61 | address: wallet.address, 62 | value: inputUtxoSumValue - (outputSize + 1) * network_config_2.SEND_UTXO_FEE_LIMIT, 63 | }); 64 | return psbt; 65 | }; 66 | exports.redeemUtxoSplitPsbt = redeemUtxoSplitPsbt; 67 | const utxoSplitPsbt = (wallet, inputUtxoArray, networkType, redeemFee) => { 68 | const psbt = new Bitcoin.Psbt({ 69 | network: networkType == network_config_1.TESTNET 70 | ? Bitcoin.networks.testnet 71 | : Bitcoin.networks.bitcoin, 72 | }); 73 | let inputUtxoSumValue = inputUtxoArray.reduce((accumulator, currentValue) => accumulator + currentValue.value, 0); 74 | let outputSize = Math.floor((inputUtxoSumValue - redeemFee) / network_config_2.SEND_UTXO_FEE_LIMIT); 75 | inputUtxoArray.forEach((utxo) => { 76 | psbt.addInput({ 77 | hash: utxo.txid, 78 | index: utxo.vout, 79 | witnessUtxo: { 80 | value: utxo.value, 81 | script: wallet.output, 82 | }, 83 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 84 | }); 85 | }); 86 | for (let i = 0; i < outputSize; i++) { 87 | psbt.addOutput({ 88 | address: wallet.address, 89 | value: network_config_2.SEND_UTXO_FEE_LIMIT, 90 | }); 91 | } 92 | psbt.addOutput({ 93 | address: wallet.address, 94 | value: Math.floor(inputUtxoSumValue - redeemFee - outputSize * network_config_2.SEND_UTXO_FEE_LIMIT), 95 | }); 96 | return psbt; 97 | }; 98 | exports.utxoSplitPsbt = utxoSplitPsbt; 99 | -------------------------------------------------------------------------------- /dist/src/services/psbt/TapLeafPsbt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.tapleafPsbt = void 0; 39 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 40 | const buffer_1 = require("../../utils/buffer"); 41 | const network_config_1 = __importStar(require("../../config/network.config")); 42 | const ecc = __importStar(require("tiny-secp256k1")); 43 | const initializeWallet_1 = __importDefault(require("../wallet/initializeWallet")); 44 | const utxo_reinscribe_singleSend_1 = require("../utxo/utxo.reinscribe.singleSend"); 45 | const utxo_singleSend_1 = require("../utxo/utxo.singleSend"); 46 | (0, bitcoinjs_lib_1.initEccLib)(ecc); 47 | const network = network_config_1.default.networkType == network_config_1.TESTNET ? bitcoinjs_lib_1.networks.testnet : bitcoinjs_lib_1.networks.bitcoin; 48 | const keyPair = initializeWallet_1.default.ecPair; 49 | const tapleafPsbt = (contentType, inscriptionData, tapScript, sendUTXOSize) => __awaiter(void 0, void 0, void 0, function* () { 50 | var _a; 51 | const ordinal_script = bitcoinjs_lib_1.script.compile(tapScript); 52 | const scriptTree = { 53 | output: ordinal_script, 54 | }; 55 | const redeem = { 56 | output: ordinal_script, 57 | redeemVersion: 192, 58 | }; 59 | const ordinal_p2tr = bitcoinjs_lib_1.payments.p2tr({ 60 | internalPubkey: (0, buffer_1.toXOnly)(keyPair.publicKey), 61 | network, 62 | scriptTree, 63 | redeem, 64 | }); 65 | const address = (_a = ordinal_p2tr.address) !== null && _a !== void 0 ? _a : ""; 66 | let res = {}; 67 | let inscriptionAmount = 0; 68 | if (contentType == network_config_1.TEXT_CONTENT) 69 | inscriptionAmount = inscriptionData.contents.length; 70 | if (contentType == network_config_1.FILE_CONTENT) 71 | inscriptionAmount = inscriptionData.files.length; 72 | if (contentType == network_config_1.DELEGATE_CONTENT) 73 | inscriptionAmount = inscriptionData.delegateIds.length; 74 | if (inscriptionData.reinscriptionId && inscriptionAmount == 1) { 75 | res = yield (0, utxo_reinscribe_singleSend_1.reinscriptionAndUTXOSend)(inscriptionData.reinscriptionId, address, inscriptionData.feeRate, sendUTXOSize); 76 | } 77 | else { 78 | res = yield (0, utxo_singleSend_1.singleSendUTXO)(address, inscriptionData.feeRate, sendUTXOSize); 79 | } 80 | if (!res.isSuccess) { 81 | console.log(res.data); 82 | } 83 | return res.data; 84 | }); 85 | exports.tapleafPsbt = tapleafPsbt; 86 | exports.default = exports.tapleafPsbt; 87 | -------------------------------------------------------------------------------- /dist/src/services/utxo/utxo.reinscribe.singleSendPsbt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __importDefault = (this && this.__importDefault) || function (mod) { 26 | return (mod && mod.__esModule) ? mod : { "default": mod }; 27 | }; 28 | Object.defineProperty(exports, "__esModule", { value: true }); 29 | exports.ReinscribeAndUtxoSendPsbt = exports.redeemReinscribeAndUtxoSendPsbt = void 0; 30 | const Bitcoin = __importStar(require("bitcoinjs-lib")); 31 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 32 | const network_config_1 = require("../../config/network.config"); 33 | Bitcoin.initEccLib(secp256k1_1.default); 34 | const redeemReinscribeAndUtxoSendPsbt = (wallet, userUtxo, networkType, amount, reinscriptionUTXO, fee, holderStatus) => { 35 | const psbt = new Bitcoin.Psbt({ 36 | network: networkType == network_config_1.TESTNET 37 | ? Bitcoin.networks.testnet 38 | : Bitcoin.networks.bitcoin, 39 | }); 40 | psbt.addInput({ 41 | hash: reinscriptionUTXO.txid, 42 | index: reinscriptionUTXO.vout, 43 | witnessUtxo: { 44 | value: reinscriptionUTXO.value, 45 | script: wallet.output, 46 | }, 47 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 48 | }); 49 | psbt.addInput({ 50 | hash: userUtxo.txid, 51 | index: userUtxo.vout, 52 | witnessUtxo: { 53 | value: userUtxo.value, 54 | script: wallet.output, 55 | }, 56 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 57 | }); 58 | psbt.addOutput({ 59 | address: wallet.address, 60 | value: amount, 61 | }); 62 | if (!holderStatus) { 63 | psbt.addOutput({ 64 | address: wallet.address, 65 | value: userUtxo.value + reinscriptionUTXO.value - fee - amount, 66 | }); 67 | } 68 | return psbt; 69 | }; 70 | exports.redeemReinscribeAndUtxoSendPsbt = redeemReinscribeAndUtxoSendPsbt; 71 | const ReinscribeAndUtxoSendPsbt = (wallet, userUtxo, networkType, fee, address, amount, reinscriptionUTXO, holderStatus) => { 72 | const psbt = new Bitcoin.Psbt({ 73 | network: networkType == network_config_1.TESTNET 74 | ? Bitcoin.networks.testnet 75 | : Bitcoin.networks.bitcoin, 76 | }); 77 | psbt.addInput({ 78 | hash: reinscriptionUTXO.txid, 79 | index: reinscriptionUTXO.vout, 80 | witnessUtxo: { 81 | value: reinscriptionUTXO.value, 82 | script: wallet.output, 83 | }, 84 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 85 | }); 86 | psbt.addInput({ 87 | hash: userUtxo.txid, 88 | index: userUtxo.vout, 89 | witnessUtxo: { 90 | value: userUtxo.value, 91 | script: wallet.output, 92 | }, 93 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 94 | }); 95 | psbt.addOutput({ 96 | address: address, 97 | value: amount, 98 | }); 99 | if (!holderStatus) { 100 | psbt.addOutput({ 101 | address: wallet.address, 102 | value: userUtxo.value + reinscriptionUTXO.value - fee - amount, 103 | }); 104 | } 105 | return psbt; 106 | }; 107 | exports.ReinscribeAndUtxoSendPsbt = ReinscribeAndUtxoSendPsbt; 108 | -------------------------------------------------------------------------------- /dist/src/services/psbt/TapLeafPsbtCreate.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.tapleafPsbt = void 0; 39 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 40 | const buffer_1 = require("../../utils/buffer"); 41 | const network_config_1 = __importStar(require("../../config/network.config")); 42 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 43 | const initializeWallet_1 = __importDefault(require("../wallet/initializeWallet")); 44 | const utxo_reinscribe_singleSend_1 = require("../utxo/utxo.reinscribe.singleSend"); 45 | const utxo_singleSend_1 = require("../utxo/utxo.singleSend"); 46 | (0, bitcoinjs_lib_1.initEccLib)(secp256k1_1.default); 47 | const network = network_config_1.default.networkType == network_config_1.TESTNET ? bitcoinjs_lib_1.networks.testnet : bitcoinjs_lib_1.networks.bitcoin; 48 | const keyPair = initializeWallet_1.default.ecPair; 49 | const tapleafPsbt = (contentType, inscriptionData, tapScript, userUtxo, amount) => __awaiter(void 0, void 0, void 0, function* () { 50 | var _a; 51 | const ordinal_script = bitcoinjs_lib_1.script.compile(tapScript); 52 | const scriptTree = { 53 | output: ordinal_script, 54 | }; 55 | const redeem = { 56 | output: ordinal_script, 57 | redeemVersion: 192, 58 | }; 59 | const ordinal_p2tr = bitcoinjs_lib_1.payments.p2tr({ 60 | internalPubkey: (0, buffer_1.toXOnly)(keyPair.publicKey), 61 | network, 62 | scriptTree, 63 | redeem, 64 | }); 65 | const address = (_a = ordinal_p2tr.address) !== null && _a !== void 0 ? _a : ""; 66 | let res = {}; 67 | let inscriptionAmount = 0; 68 | if (contentType == network_config_1.TEXT_CONTENT) 69 | inscriptionAmount = inscriptionData.contents.length; 70 | if (contentType == network_config_1.FILE_CONTENT) 71 | inscriptionAmount = inscriptionData.files.length; 72 | if (contentType == network_config_1.DELEGATE_CONTENT) 73 | inscriptionAmount = inscriptionData.delegateIds.length; 74 | if (inscriptionData.reinscriptionId && inscriptionAmount == 1) { 75 | res = yield (0, utxo_reinscribe_singleSend_1.reinscriptionAndUTXOSend)(inscriptionData.reinscriptionId, address, inscriptionData.feeRate, userUtxo, amount, inscriptionData.holderStatus); 76 | } 77 | else { 78 | res = yield (0, utxo_singleSend_1.singleSendUTXO)(address, inscriptionData.feeRate, userUtxo, amount, inscriptionData.holderStatus); 79 | } 80 | if (!res.isSuccess) { 81 | console.log(res.data); 82 | } 83 | return res.data; 84 | }); 85 | exports.tapleafPsbt = tapleafPsbt; 86 | exports.default = exports.tapleafPsbt; 87 | -------------------------------------------------------------------------------- /dist/src/services/tapscript/delegateTapScript.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | exports.delegateTapScript = void 0; 16 | const initializeWallet_1 = __importDefault(require("../wallet/initializeWallet")); 17 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 18 | const unisat_api_1 = require("../../utils/unisat.api"); 19 | const network_config_1 = __importDefault(require("../../config/network.config")); 20 | const cbor_1 = __importDefault(require("cbor")); 21 | const buffer_1 = require("../../utils/buffer"); 22 | const keyPair = initializeWallet_1.default.ecPair; 23 | const delegateTapScript = (inscriptionData) => __awaiter(void 0, void 0, void 0, function* () { 24 | let tapScript = [(0, buffer_1.toXOnly)(keyPair.publicKey), bitcoinjs_lib_1.opcodes.OP_CHECKSIG]; 25 | let pointers = []; 26 | inscriptionData.delegateIds.forEach((item, index) => { 27 | pointers.push(index * inscriptionData.padding); 28 | }); 29 | if (inscriptionData.parentId) { 30 | let parentInscriptionUTXO = yield (0, unisat_api_1.getInscriptionInfo)(inscriptionData.parentId, network_config_1.default.networkType); 31 | pointers = pointers.map((pointer, index) => { 32 | return pointer + parentInscriptionUTXO.value; 33 | }); 34 | } 35 | let pointerBuffer = []; 36 | pointerBuffer = pointers.map((pointer, index) => { 37 | return Buffer.from(pointer.toString(16).padStart(4, "0"), "hex").reverse(); 38 | }); 39 | const parts = inscriptionData.parentId.split("i"); 40 | const parentInscriptionTransactionID = parts[0]; 41 | const inscriptionTransactionBuffer = Buffer.from(parentInscriptionTransactionID, "hex").reverse(); 42 | let parentInscriptionBuffer; 43 | const index = parts[1]; 44 | if (parseInt(index, 10) != 0) { 45 | const indexBuffer = Buffer.from(parseInt(index, 10).toString(16).padStart(2, "0"), "hex").reverse(); 46 | parentInscriptionBuffer = Buffer.concat([ 47 | inscriptionTransactionBuffer, 48 | indexBuffer, 49 | ]); 50 | } 51 | else { 52 | parentInscriptionBuffer = inscriptionTransactionBuffer; 53 | } 54 | const DelegateIDparts = inscriptionData.delegateIds[0].split("i"); 55 | const delegateInscriptionTransactionID = DelegateIDparts[0]; 56 | const DelegateinscriptionTransactionBuffer = Buffer.from(delegateInscriptionTransactionID, "hex").reverse(); 57 | let DelegateInscriptionBuffer; 58 | const DelegateIndex = DelegateIDparts[1]; 59 | if (parseInt(DelegateIndex, 10) != 0) { 60 | const DelegateIndexBuffer = Buffer.from(parseInt(DelegateIndex, 10).toString(16).padStart(2, "0"), "hex").reverse(); 61 | DelegateInscriptionBuffer = Buffer.concat([ 62 | DelegateinscriptionTransactionBuffer, 63 | DelegateIndexBuffer, 64 | ]); 65 | } 66 | else { 67 | DelegateInscriptionBuffer = DelegateinscriptionTransactionBuffer; 68 | } 69 | for (let i = 0; i < inscriptionData.delegateIds.length; i++) { 70 | let subScript = []; 71 | subScript.push(bitcoinjs_lib_1.opcodes.OP_FALSE, bitcoinjs_lib_1.opcodes.OP_IF, Buffer.from("ord", "utf8")); 72 | subScript.push(1, 2, pointerBuffer[i]); 73 | subScript.push(1, 3, parentInscriptionBuffer); 74 | if (inscriptionData.metadata) { 75 | subScript.push(1, 5, cbor_1.default.encode(JSON.parse(inscriptionData.metadata))); 76 | } 77 | if (inscriptionData.metaprotocol) { 78 | subScript.push(1, 7, Buffer.from(inscriptionData.metaprotocol, "utf8")); 79 | } 80 | subScript.push(1, 11, DelegateInscriptionBuffer); 81 | subScript.push(bitcoinjs_lib_1.opcodes.OP_ENDIF); 82 | tapScript.push(...subScript); 83 | } 84 | return tapScript; 85 | }); 86 | exports.delegateTapScript = delegateTapScript; 87 | -------------------------------------------------------------------------------- /src/services/psbt/inscriptionPsbt.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Psbt, 3 | networks, 4 | payments, 5 | script, 6 | initEccLib, 7 | crypto, 8 | } from "bitcoinjs-lib"; 9 | import { IUtxo } from "../../utils/types"; 10 | import { Taptree } from "bitcoinjs-lib/src/types"; 11 | import { toXOnly } from "../../utils/buffer"; 12 | import networkConfig, { 13 | DELEGATE_CONTENT, 14 | FILE_CONTENT, 15 | TESTNET, 16 | TEXT_CONTENT, 17 | } from "../../config/network.config"; 18 | import wallet from "../wallet/initializeWallet"; 19 | import { getInscriptionInfo } from "../../utils/unisat.api"; 20 | import ecc from "@bitcoinerlab/secp256k1"; 21 | import { ECPairFactory, ECPairAPI } from "ecpair"; 22 | 23 | initEccLib(ecc as any); 24 | const ECPair: ECPairAPI = ECPairFactory(ecc); 25 | 26 | export const inscriptionPsbt = async ( 27 | contentType: string, 28 | inscriptionData: any, 29 | tapScript: Array, 30 | sentUtxo: any 31 | ): Promise => { 32 | const network = 33 | networkConfig.networkType == TESTNET ? networks.testnet : networks.bitcoin; 34 | const keyPair = wallet.ecPair; 35 | 36 | const ordinal_script = script.compile(tapScript); 37 | 38 | const scriptTree: Taptree = { 39 | output: ordinal_script, 40 | }; 41 | 42 | const redeem = { 43 | output: ordinal_script, 44 | redeemVersion: 192, 45 | }; 46 | 47 | const ordinal_p2tr = payments.p2tr({ 48 | internalPubkey: toXOnly(keyPair.publicKey), 49 | network, 50 | scriptTree, 51 | redeem, 52 | }); 53 | 54 | const psbt = new Psbt({ network }); 55 | if (inscriptionData.parentId) { 56 | let parentInscriptionUTXO: IUtxo = await getInscriptionInfo( 57 | inscriptionData.parentId, 58 | networkConfig.networkType 59 | ); 60 | 61 | psbt.addInput({ 62 | hash: parentInscriptionUTXO.txid, 63 | index: parentInscriptionUTXO.vout, 64 | witnessUtxo: { 65 | value: parentInscriptionUTXO.value, 66 | script: wallet.output, 67 | }, 68 | tapInternalKey: toXOnly(keyPair.publicKey), 69 | }); 70 | } 71 | 72 | psbt.addInput({ 73 | hash: sentUtxo.txid, 74 | index: sentUtxo.vout, 75 | tapInternalKey: toXOnly(keyPair.publicKey), 76 | witnessUtxo: { value: sentUtxo.value, script: ordinal_p2tr.output! }, 77 | tapLeafScript: [ 78 | { 79 | leafVersion: redeem.redeemVersion, 80 | script: redeem.output, 81 | controlBlock: ordinal_p2tr.witness![ordinal_p2tr.witness!.length - 1], 82 | }, 83 | ], 84 | }); 85 | if (inscriptionData.parentId) { 86 | psbt.addOutput({ 87 | address: inscriptionData.ordinalsAddress, 88 | value: inscriptionData.padding, 89 | }); 90 | } 91 | if (contentType == TEXT_CONTENT) { 92 | inscriptionData.contents.forEach((content: string) => { 93 | psbt.addOutput({ 94 | address: inscriptionData.receiveAddress, 95 | value: inscriptionData.padding, 96 | }); 97 | }); 98 | } else if (contentType == FILE_CONTENT) { 99 | inscriptionData.files.forEach((content: string) => { 100 | psbt.addOutput({ 101 | address: inscriptionData.receiveAddress, 102 | value: inscriptionData.padding, 103 | }); 104 | }); 105 | } else if (contentType == DELEGATE_CONTENT) { 106 | inscriptionData.delegateIds.forEach((content: string) => { 107 | psbt.addOutput({ 108 | address: inscriptionData.receiveAddress, 109 | value: inscriptionData.padding, 110 | }); 111 | }); 112 | } 113 | //Sign psbt 114 | if (inscriptionData.parentId) { 115 | const signer = tweakSigner(keyPair, { network }); 116 | psbt.signInput(0, signer); 117 | psbt.signInput(1, keyPair); 118 | } else { 119 | psbt.signInput(0, keyPair); 120 | } 121 | 122 | psbt.finalizeAllInputs(); 123 | const tx = psbt.extractTransaction(true); 124 | 125 | return tx; 126 | }; 127 | 128 | function tweakSigner(signer: any, opts: any = {}) { 129 | let privateKey = signer.privateKey; 130 | if (!privateKey) { 131 | throw new Error("Private key is required for tweaking signer!"); 132 | } 133 | if (signer.publicKey[0] === 3) { 134 | privateKey = ecc.privateNegate(privateKey); 135 | } 136 | const tweakedPrivateKey = ecc.privateAdd( 137 | privateKey, 138 | tapTweakHash(toXOnly(signer.publicKey), opts.tweakHash) 139 | ); 140 | if (!tweakedPrivateKey) { 141 | throw new Error("Invalid tweaked private key!"); 142 | } 143 | return ECPair.fromPrivateKey(Buffer.from(tweakedPrivateKey), { 144 | network: opts.network, 145 | }); 146 | } 147 | 148 | function tapTweakHash(pubKey: Buffer, h: Buffer | undefined): Buffer { 149 | return crypto.taggedHash( 150 | "TapTweak", 151 | Buffer.concat(h ? [pubKey, h] : [pubKey]) 152 | ); 153 | } 154 | -------------------------------------------------------------------------------- /src/routes/send.ordinals.route.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, Router } from "express"; 2 | import { ISendingOrdinalData } from "../utils/types"; 3 | import { SendingOrdinalController } from "../controller/inscribe.controller"; 4 | import { isValidBitcoinAddress } from "../utils/validationAddress"; 5 | import { isContainOrdinal, testUnisatAPI } from "../utils/unisat.api"; 6 | import networkConfig, { TESTNET } from "../config/network.config"; 7 | 8 | // Create a new instance of the Inscription Router 9 | export const SendOrdinalRouter = Router(); 10 | 11 | // @route POST api/inscribe/getSendingOrdinalBtcPsbt 12 | // @desc Inscribe Text Inscription 13 | // @access Private 14 | SendOrdinalRouter.post( 15 | "/getSendingOrdinalBtcPsbt", 16 | async (req: Request, res: Response) => { 17 | try { 18 | if ( 19 | !( 20 | req.body.receiveAddress && 21 | req.body.networkFee && 22 | req.body.paymentAddress && 23 | req.body.paymentPublicKey && 24 | req.body.ordinalsAddress && 25 | req.body.ordinalsPublicKey && 26 | req.body.btcAmount 27 | ) 28 | ) { 29 | let error = []; 30 | if (!req.body.receiveAddress) { 31 | error.push({ receiveAddress: "ReceiveAddress is required" }); 32 | } 33 | if (!req.body.networkFee) { 34 | error.push({ feeRate: "FeeRate is required" }); 35 | } 36 | if (!req.body.btcAmount) { 37 | error.push({ btcAmount: "btcAmount is required" }); 38 | } 39 | if (!req.body.paymentAddress) { 40 | error.push({ publicKey: "Payment Address is required" }); 41 | } 42 | 43 | if (!req.body.paymentPublicKey) { 44 | error.push({ publicKey: "Payment PublicKey is required" }); 45 | } 46 | 47 | if (!req.body.ordinalsAddress) { 48 | error.push({ publicKey: "Ordinals Address is required" }); 49 | } 50 | 51 | if (!req.body.ordinalsPublicKey) { 52 | error.push({ publicKey: "Ordinals Public Key is required" }); 53 | } 54 | 55 | res.status(400).send({ error: { type: 0, data: error } }); 56 | } else { 57 | if (!isValidBitcoinAddress(req.body.receiveAddress)) { 58 | res 59 | .status(400) 60 | .send({ type: 2, data: "This address is not valid address." }); 61 | } else { 62 | const feeRate: number = +req.body.networkFee; 63 | let parentId: string = ""; 64 | let reinscriptionId: string = ""; 65 | 66 | if (req.body.parentId) { 67 | const isContainOrdinalStatus = await isContainOrdinal( 68 | req.body.parentId, 69 | req.body.ordinalsAddress, 70 | networkConfig.networkType 71 | ); 72 | 73 | if (!isContainOrdinalStatus) { 74 | return res.status(400).send({ 75 | type: 5, 76 | data: `Parent Id does not contain on ${req.body.ordinalsAddress}`, 77 | }); 78 | } else { 79 | parentId = req.body.parentId; 80 | } 81 | } 82 | if (req.body.reinscriptionId) { 83 | const isContainOrdinalStatus = await isContainOrdinal( 84 | req.body.reinscriptionId, 85 | req.body.ordinalsAddress, 86 | networkConfig.networkType 87 | ); 88 | 89 | if (!isContainOrdinalStatus) { 90 | return res.status(400).send({ 91 | type: 5, 92 | data: `Reinscription Id does not contain on ${req.body.ordinalsAddress}`, 93 | }); 94 | } else { 95 | reinscriptionId = req.body.reinscriptionId; 96 | } 97 | } 98 | 99 | const sendOrdinalRequestData: ISendingOrdinalData = { 100 | paymentAddress: req.body.paymentAddress, 101 | paymentPublicKey: req.body.paymentPublicKey, 102 | ordinalsAddress: req.body.ordinalsAddress, 103 | ordinalsPublicKey: req.body.ordinalsPublicKey, 104 | receiveAddress: req.body.receiveAddress, 105 | parentId: parentId, 106 | reinscriptionId: reinscriptionId, 107 | feeRate: feeRate, 108 | btcAmount: req.body.btcAmount, 109 | }; 110 | await SendingOrdinalController(sendOrdinalRequestData, res); 111 | } 112 | } 113 | } catch (error: any) { 114 | console.error(error); 115 | return res.status(400).send({ error }); 116 | } 117 | } 118 | ); 119 | 120 | SendOrdinalRouter.get("/test", async (req: Request, res: Response) => { 121 | try { 122 | const data = await testUnisatAPI( 123 | "tb1pymgsee4syh7ez4g9pm7gu0ax8wfj4wukwlxykfwnn6gx2tcr4r7quhsdlh", 124 | TESTNET 125 | ); 126 | 127 | res.status(200).send({ 128 | data: data, 129 | }); 130 | } catch (error: any) { 131 | console.log({ error }); 132 | } 133 | }); 134 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Ordinal Inscribing service 2 | 3 | ## Table of Contents 4 | 5 | - [Introduction](#introduction) 6 | - [Features](#features) 7 | - [Getting Started](#getting-started) 8 | - [Installation](#installation) 9 | - [Configuration](#configuration) 10 | - [Running the Application](#running-the-application) 11 | - [API Documentation](#api-documentation) 12 | - [Overview](#overview) 13 | - [Base URLs](#base-urls) 14 | - [Available Endpoints](#available-endpoints) 15 | - [Status](#status) 16 | - [Get Current Bitcoin Price](#get-current-bitcoin-price) 17 | - [Last 15 Blocks Average Fee Rate](#last-15-blocks-average-fee-rate) 18 | - [Recommended Block Fee Rate](#recommended-block-fee-rate) 19 | - [Split Large UTXO of Admin Wallet](#split-large-utxo-of-admin-wallet) 20 | - [Inscribe](#inscribe) 21 | - [Create New Text Inscription](#create-new-text-inscription) 22 | - [Create New File Inscription](#create-new-file-inscription) 23 | - [Create New Delegate Inscription](#create-new-delegate-inscription) 24 | - [Get New Sending Ordinals PSBT](#get-new-sending-ordinals-psbt) 25 | - [Estimate](#estimate) 26 | - [Estimate Text Inscription](#estimate-text-inscription) 27 | - [Estimate File Inscription](#estimate-file-inscription) 28 | - [Estimate Delegate Inscription](#estimate-delegate-inscription) 29 | - [Schema Definitions](#schema-definitions) 30 | - [Error Handling](#error-handling) 31 | - [Contributing](#contributing) 32 | - [License](#license) 33 | 34 | ## Introduction 35 | 36 | A brief introduction to the Luminex Ordinal/Iscription Express Backend, highlighting its purpose and significance within the bitcoin ecosystem. 37 | 38 | ## Features 39 | 40 | A section detailing the core features of the backend service, including parent/child provenance inscription minting, reinscription, delegate inscription, and metadata/metaprotocol integration Batch Inscription (Bulk text inscribing). 41 | 42 | ## Getting Started 43 | 44 | Instructions on how to get started with the project, including installation, configuration, and running the application. 45 | 46 | ### Installation 47 | 48 | Steps to clone the repository and install dependencies. 49 | 50 | ### Configuration 51 | 52 | Guidelines on setting up environment variables and configuring the application for both development and production environments. 53 | 54 | ### Running the Application 55 | 56 | Commands to start the application and access the server. 57 | 58 | ## API Documentation 59 | 60 | An overview of the API documentation, including base URLs and available endpoints categorized by functionality. 61 | 62 | ### Overview 63 | 64 | A high-level description of what the API does and who it's for. 65 | 66 | ### Base URLs 67 | 68 | Information on the base URLs for accessing the API in different environments. 69 | 70 | ### Available Endpoints 71 | 72 | Detailed descriptions of each endpoint, including method, endpoint path, description, request body (if applicable), and expected response. 73 | 74 | #### Status 75 | 76 | Endpoints related to fetching status information about the system, such as Bitcoin prices and fee rates. 77 | 78 | ##### Get Current Bitcoin Price 79 | 80 | Details on the endpoint for retrieving the current Bitcoin price. 81 | 82 | ##### Last 15 Blocks Average Fee Rate 83 | 84 | Details on the endpoint for fetching the average fee rate of the last 15 blocks. 85 | 86 | ##### Recommended Block Fee Rate 87 | 88 | Details on the endpoint for getting a recommendation for the optimal block fee rate. 89 | 90 | ##### Split Large UTXO of Admin Wallet 91 | 92 | Details on the endpoint for splitting a large UTXO from the admin wallet. 93 | 94 | #### Inscribe 95 | 96 | Endpoints for creating new inscriptions, including text, file, and delegate inscriptions, and generating sending ordinals PSBTs. 97 | 98 | ##### Create New Text Inscription 99 | 100 | Details on the endpoint for creating a new text inscription. 101 | 102 | ##### Create New File Inscription 103 | 104 | Details on the endpoint for creating a new file inscription. 105 | 106 | ##### Create New Delegate Inscription 107 | 108 | Details on the endpoint for creating a new delegate inscription. 109 | 110 | ##### Get New Sending Ordinals PSBT 111 | 112 | Details on the endpoint for generating a new Bitcoin transaction PSBT for sending ordinals. 113 | 114 | #### Estimate 115 | 116 | Endpoints for estimating the costs associated with creating inscriptions. 117 | 118 | ##### Estimate Text Inscription 119 | 120 | Details on the endpoint for estimating the cost of creating a text inscription. 121 | 122 | ##### Estimate File Inscription 123 | 124 | Details on the endpoint for estimating the cost of creating a file inscription. 125 | 126 | ##### Estimate Delegate Inscription 127 | 128 | Details on the endpoint for estimating the cost of creating a delegate inscription. 129 | 130 | ## Schema Definitions 131 | 132 | Descriptions of the schemas used in the API, including `TextInscription`, `FileInscription`, `DelegateInscription`, and `SendingOrdialBtcPsbt`. 133 | 134 | -------------------------------------------------------------------------------- /src/utils/unisat.api.ts: -------------------------------------------------------------------------------- 1 | import axios, { type AxiosError } from "axios"; 2 | import { TESTNET } from "../config/network.config"; 3 | import dotenv from "dotenv"; 4 | import { setApiIterator, setUtxoFlag, waitUtxoFlag } from "./mutex"; 5 | import { app } from "../.."; 6 | 7 | interface IUtxo { 8 | txid: string; 9 | vout: number; 10 | value: number; 11 | } 12 | 13 | dotenv.config(); 14 | 15 | const apiArray = JSON.parse(process.env.OPENAPI_UNISAT_TOKEN ?? ""); 16 | 17 | export function delay(ms: number): Promise { 18 | return new Promise((resolve) => setTimeout(resolve, ms)); 19 | } 20 | 21 | export const getInscriptionInfo = async ( 22 | inscriptionid: string, 23 | networkType: string 24 | ): Promise => { 25 | try { 26 | await waitUtxoFlag(); 27 | await setUtxoFlag(1); 28 | 29 | if (app.locals.iterator >= apiArray.length) { 30 | await setApiIterator(0); 31 | } 32 | 33 | const url = `https://open-api${ 34 | networkType == TESTNET ? "-testnet" : "" 35 | }.unisat.io/v1/indexer/inscription/info/${inscriptionid}`; 36 | 37 | const config = { 38 | headers: { 39 | Authorization: `Bearer ${apiArray[app.locals.iterator] as string}`, 40 | }, 41 | }; 42 | let res = await axios.get(url, config); 43 | 44 | let iterator = app.locals.iterator + 1; 45 | await setApiIterator(iterator); 46 | 47 | await setUtxoFlag(0); 48 | 49 | const inscriptionInfo = res.data; 50 | const info: IUtxo = { 51 | txid: inscriptionInfo.data.utxo.txid, 52 | vout: inscriptionInfo.data.utxo.vout, 53 | value: inscriptionInfo.data.utxo.satoshi, 54 | }; 55 | 56 | return info; 57 | } catch (err: any) { 58 | await setUtxoFlag(0); 59 | 60 | console.log("Get Inscription Utxo Error"); 61 | } 62 | }; 63 | 64 | export const isContainOrdinal = async ( 65 | inscriptionid: string, 66 | address: string, 67 | networkType: string 68 | ): Promise => { 69 | try { 70 | await waitUtxoFlag(); 71 | await setUtxoFlag(1); 72 | 73 | if (app.locals.iterator >= apiArray.length) { 74 | await setApiIterator(0); 75 | } 76 | 77 | const url = `https://open-api${ 78 | networkType == TESTNET ? "-testnet" : "" 79 | }.unisat.io/v1/indexer/inscription/info/${inscriptionid}`; 80 | 81 | const config = { 82 | headers: { 83 | Authorization: `Bearer ${apiArray[app.locals.iterator] as string}`, 84 | }, 85 | }; 86 | 87 | const res = await axios.get(url, config); 88 | 89 | let iterator = app.locals.iterator + 1; 90 | await setApiIterator(iterator); 91 | 92 | await setUtxoFlag(0); 93 | 94 | const inscriptionInfo = res.data; 95 | 96 | if (address == inscriptionInfo.data.utxo.address) { 97 | return true; 98 | } 99 | return false; 100 | } catch (err: any) { 101 | await setUtxoFlag(0); 102 | 103 | console.log("Get Inscription Utxo Error"); 104 | } 105 | }; 106 | 107 | // Get BTC UTXO 108 | export const getBtcUtxoInfo = async (address: string, networkType: string) => { 109 | await waitUtxoFlag(); 110 | await setUtxoFlag(1); 111 | 112 | if (app.locals.iterator >= apiArray.length) { 113 | await setApiIterator(0); 114 | } 115 | 116 | const url = `https://open-api${ 117 | networkType == TESTNET ? "-testnet" : "" 118 | }.unisat.io/v1/indexer/address/${address}/utxo-data`; 119 | 120 | const config = { 121 | headers: { 122 | Authorization: `Bearer ${apiArray[app.locals.iterator] as string}`, 123 | }, 124 | }; 125 | 126 | let iterator = app.locals.iterator + 1; 127 | await setApiIterator(iterator); 128 | 129 | let cursor = 0; 130 | const size = 5000; 131 | let utxos: IUtxo[] = []; 132 | 133 | while (1) { 134 | const res = await axios.get(url, { ...config, params: { cursor, size } }); 135 | if (res.data.code === -1) throw "Invalid Address"; 136 | let fetchUtxos = res.data.data.utxo.reverse(); 137 | utxos.push( 138 | ...(fetchUtxos as any[]).map((utxo) => { 139 | return { 140 | txid: utxo.txid, 141 | value: utxo.satoshi, 142 | vout: utxo.vout, 143 | }; 144 | }) 145 | ); 146 | cursor += fetchUtxos.length; 147 | 148 | if (cursor >= res.data.data.total - res.data.data.totalRunes) break; 149 | } 150 | 151 | await setUtxoFlag(0); 152 | return utxos; 153 | }; 154 | 155 | export const testUnisatAPI = async (address: string, networkType: string) => { 156 | await waitUtxoFlag(); 157 | await setUtxoFlag(1); 158 | 159 | if (app.locals.iterator >= apiArray.length) { 160 | await setApiIterator(0); 161 | } 162 | 163 | const url = `https://open-api${ 164 | networkType == TESTNET ? "-testnet" : "" 165 | }.unisat.io/v1/indexer/address/${address}/utxo-data`; 166 | 167 | const config = { 168 | headers: { 169 | Authorization: `Bearer ${apiArray[app.locals.iterator] as string}`, 170 | }, 171 | }; 172 | 173 | let iterator = app.locals.iterator + 1; 174 | await setApiIterator(iterator); 175 | 176 | let cursor = 0; 177 | const size = 5000; 178 | let utxos: IUtxo[] = []; 179 | 180 | while (1) { 181 | const res = await axios.get(url, { ...config, params: { cursor, size } }); 182 | if (res.data.code === -1) throw "Invalid Address"; 183 | let fetchUtxos = res.data.data.utxo.reverse(); 184 | utxos.push( 185 | ...(fetchUtxos as any[]).map((utxo) => { 186 | return { 187 | txid: utxo.txid, 188 | value: utxo.satoshi, 189 | vout: utxo.vout, 190 | }; 191 | }) 192 | ); 193 | cursor += fetchUtxos.length; 194 | 195 | if (cursor >= res.data.data.total - res.data.data.totalRunes) break; 196 | } 197 | 198 | await setUtxoFlag(0); 199 | 200 | return utxos; 201 | }; 202 | -------------------------------------------------------------------------------- /dist/src/utils/mempool.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | exports.getTxHex = exports.getRecommendedFeeRate = exports.getFeeRate = exports.getBlockHeight = exports.getPrice = exports.pushBTCpmt = exports.getUtxos = void 0; 16 | const axios_1 = __importDefault(require("axios")); 17 | const network_config_1 = require("../config/network.config"); 18 | const getUtxos = (address, networkType) => __awaiter(void 0, void 0, void 0, function* () { 19 | try { 20 | const url = `https://mempool.space/${networkType == network_config_1.TESTNET ? "testnet/" : ""}api/address/${address}/utxo`; 21 | const res = yield axios_1.default.get(url); 22 | const confirmedUtxos = []; 23 | const unConfirmedUtxos = []; 24 | res.data.forEach((utxoData) => { 25 | if (utxoData.status.confirmed) { 26 | confirmedUtxos.push({ 27 | txid: utxoData.txid, 28 | vout: utxoData.vout, 29 | value: utxoData.value, 30 | }); 31 | } 32 | else { 33 | unConfirmedUtxos.push({ 34 | txid: utxoData.txid, 35 | vout: utxoData.vout, 36 | value: utxoData.value, 37 | }); 38 | } 39 | }); 40 | return [...confirmedUtxos, ...unConfirmedUtxos]; 41 | } 42 | catch (err) { 43 | console.log("Get Utxos Error"); 44 | } 45 | }); 46 | exports.getUtxos = getUtxos; 47 | const pushBTCpmt = (rawtx, networkType) => __awaiter(void 0, void 0, void 0, function* () { 48 | const txid = yield postData(`https://mempool.space/${networkType == network_config_1.TESTNET ? "testnet/" : ""}api/tx`, rawtx); 49 | return txid; 50 | }); 51 | exports.pushBTCpmt = pushBTCpmt; 52 | const postData = (url, json, content_type = "text/plain", apikey = "") => __awaiter(void 0, void 0, void 0, function* () { 53 | try { 54 | const headers = {}; 55 | if (content_type) 56 | headers["Content-Type"] = content_type; 57 | if (apikey) 58 | headers["X-Api-Key"] = apikey; 59 | const res = yield axios_1.default.post(url, json, { 60 | headers, 61 | }); 62 | return res.data; 63 | } 64 | catch (err) { 65 | console.log("Push Transaction Error"); 66 | console.log(err.response.data); 67 | } 68 | }); 69 | const getPrice = (networkType) => __awaiter(void 0, void 0, void 0, function* () { 70 | try { 71 | const url = `https://mempool.space/${networkType == network_config_1.TESTNET ? "testnet/" : ""}api/v1/prices`; 72 | const res = yield axios_1.default.get(url); 73 | return res.data; 74 | } 75 | catch (error) { 76 | console.log("Get Price Error!"); 77 | } 78 | }); 79 | exports.getPrice = getPrice; 80 | const getBlockHeight = (networkType) => __awaiter(void 0, void 0, void 0, function* () { 81 | try { 82 | const url = `https://mempool.space/${networkType == network_config_1.TESTNET ? "testnet/" : ""}/api/blocks/tip/height`; 83 | const res = yield axios_1.default.get(url); 84 | return res.data; 85 | } 86 | catch (error) { 87 | console.log("Get Price Error!"); 88 | } 89 | }); 90 | exports.getBlockHeight = getBlockHeight; 91 | const getFeeRate = (networkType, response) => __awaiter(void 0, void 0, void 0, function* () { 92 | try { 93 | const height = yield (0, exports.getBlockHeight)(networkType); 94 | const url = `https://mempool.space/${networkType == network_config_1.TESTNET ? "testnet/" : ""}api/v1/blocks/${height}`; 95 | const blockData = yield axios_1.default.get(url); 96 | const feeRateData = blockData.data.map((item) => { 97 | return { timestamp: item.timestamp, avgFeeRate: item.extras.avgFeeRate }; 98 | }); 99 | return response.status(200).send({ feeRateData }); 100 | } 101 | catch (error) { 102 | return response.status(400).send({ 103 | type: 1, 104 | data: "Get Fee Rate Error!", 105 | }); 106 | } 107 | }); 108 | exports.getFeeRate = getFeeRate; 109 | const getRecommendedFeeRate = (networkType) => __awaiter(void 0, void 0, void 0, function* () { 110 | try { 111 | const url = `https://mempool.space/${networkType == network_config_1.TESTNET ? "testnet/" : ""}api/v1/fees/recommended`; 112 | const response = yield axios_1.default.get(url); 113 | const recommendFeeRate = response.data; 114 | return recommendFeeRate; 115 | } 116 | catch (error) { 117 | console.log("Get Recommend Fee Rate Error!"); 118 | } 119 | }); 120 | exports.getRecommendedFeeRate = getRecommendedFeeRate; 121 | const getTxHex = (txid, networkType) => __awaiter(void 0, void 0, void 0, function* () { 122 | try { 123 | const url = `https://mempool.space/${networkType == network_config_1.TESTNET ? "testnet/" : ""}api/tx/${txid}/hex`; 124 | const res = yield axios_1.default.get(url); 125 | const data = res.data; 126 | return data; 127 | } 128 | catch (err) { 129 | console.log("Get Tx Hex Error"); 130 | } 131 | }); 132 | exports.getTxHex = getTxHex; 133 | -------------------------------------------------------------------------------- /dist/src/services/psbt/inscriptionPsbt.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.inscriptionPsbt = void 0; 39 | const bitcoinjs_lib_1 = require("bitcoinjs-lib"); 40 | const buffer_1 = require("../../utils/buffer"); 41 | const network_config_1 = __importStar(require("../../config/network.config")); 42 | const initializeWallet_1 = __importDefault(require("../wallet/initializeWallet")); 43 | const unisat_api_1 = require("../../utils/unisat.api"); 44 | const secp256k1_1 = __importDefault(require("@bitcoinerlab/secp256k1")); 45 | const ecpair_1 = require("ecpair"); 46 | (0, bitcoinjs_lib_1.initEccLib)(secp256k1_1.default); 47 | const ECPair = (0, ecpair_1.ECPairFactory)(secp256k1_1.default); 48 | const inscriptionPsbt = (contentType, inscriptionData, tapScript, sentUtxo) => __awaiter(void 0, void 0, void 0, function* () { 49 | const network = network_config_1.default.networkType == network_config_1.TESTNET ? bitcoinjs_lib_1.networks.testnet : bitcoinjs_lib_1.networks.bitcoin; 50 | const keyPair = initializeWallet_1.default.ecPair; 51 | const ordinal_script = bitcoinjs_lib_1.script.compile(tapScript); 52 | const scriptTree = { 53 | output: ordinal_script, 54 | }; 55 | const redeem = { 56 | output: ordinal_script, 57 | redeemVersion: 192, 58 | }; 59 | const ordinal_p2tr = bitcoinjs_lib_1.payments.p2tr({ 60 | internalPubkey: (0, buffer_1.toXOnly)(keyPair.publicKey), 61 | network, 62 | scriptTree, 63 | redeem, 64 | }); 65 | const psbt = new bitcoinjs_lib_1.Psbt({ network }); 66 | if (inscriptionData.parentId) { 67 | let parentInscriptionUTXO = yield (0, unisat_api_1.getInscriptionInfo)(inscriptionData.parentId, network_config_1.default.networkType); 68 | psbt.addInput({ 69 | hash: parentInscriptionUTXO.txid, 70 | index: parentInscriptionUTXO.vout, 71 | witnessUtxo: { 72 | value: parentInscriptionUTXO.value, 73 | script: initializeWallet_1.default.output, 74 | }, 75 | tapInternalKey: (0, buffer_1.toXOnly)(keyPair.publicKey), 76 | }); 77 | } 78 | psbt.addInput({ 79 | hash: sentUtxo.txid, 80 | index: sentUtxo.vout, 81 | tapInternalKey: (0, buffer_1.toXOnly)(keyPair.publicKey), 82 | witnessUtxo: { value: sentUtxo.value, script: ordinal_p2tr.output }, 83 | tapLeafScript: [ 84 | { 85 | leafVersion: redeem.redeemVersion, 86 | script: redeem.output, 87 | controlBlock: ordinal_p2tr.witness[ordinal_p2tr.witness.length - 1], 88 | }, 89 | ], 90 | }); 91 | if (inscriptionData.parentId) { 92 | psbt.addOutput({ 93 | address: inscriptionData.ordinalsAddress, 94 | value: inscriptionData.padding, 95 | }); 96 | } 97 | if (contentType == network_config_1.TEXT_CONTENT) { 98 | inscriptionData.contents.forEach((content) => { 99 | psbt.addOutput({ 100 | address: inscriptionData.receiveAddress, 101 | value: inscriptionData.padding, 102 | }); 103 | }); 104 | } 105 | else if (contentType == network_config_1.FILE_CONTENT) { 106 | inscriptionData.files.forEach((content) => { 107 | psbt.addOutput({ 108 | address: inscriptionData.receiveAddress, 109 | value: inscriptionData.padding, 110 | }); 111 | }); 112 | } 113 | else if (contentType == network_config_1.DELEGATE_CONTENT) { 114 | inscriptionData.delegateIds.forEach((content) => { 115 | psbt.addOutput({ 116 | address: inscriptionData.receiveAddress, 117 | value: inscriptionData.padding, 118 | }); 119 | }); 120 | } 121 | //Sign psbt 122 | if (inscriptionData.parentId) { 123 | const signer = tweakSigner(keyPair, { network }); 124 | psbt.signInput(0, signer); 125 | psbt.signInput(1, keyPair); 126 | } 127 | else { 128 | psbt.signInput(0, keyPair); 129 | } 130 | psbt.finalizeAllInputs(); 131 | const tx = psbt.extractTransaction(true); 132 | return tx; 133 | }); 134 | exports.inscriptionPsbt = inscriptionPsbt; 135 | function tweakSigner(signer, opts = {}) { 136 | let privateKey = signer.privateKey; 137 | if (!privateKey) { 138 | throw new Error("Private key is required for tweaking signer!"); 139 | } 140 | if (signer.publicKey[0] === 3) { 141 | privateKey = secp256k1_1.default.privateNegate(privateKey); 142 | } 143 | const tweakedPrivateKey = secp256k1_1.default.privateAdd(privateKey, tapTweakHash((0, buffer_1.toXOnly)(signer.publicKey), opts.tweakHash)); 144 | if (!tweakedPrivateKey) { 145 | throw new Error("Invalid tweaked private key!"); 146 | } 147 | return ECPair.fromPrivateKey(Buffer.from(tweakedPrivateKey), { 148 | network: opts.network, 149 | }); 150 | } 151 | function tapTweakHash(pubKey, h) { 152 | return bitcoinjs_lib_1.crypto.taggedHash("TapTweak", Buffer.concat(h ? [pubKey, h] : [pubKey])); 153 | } 154 | -------------------------------------------------------------------------------- /dist/src/routes/send.ordinals.route.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | Object.defineProperty(exports, "__esModule", { value: true }); 35 | exports.SendOrdinalRouter = void 0; 36 | const express_1 = require("express"); 37 | const inscribe_controller_1 = require("../controller/inscribe.controller"); 38 | const validationAddress_1 = require("../utils/validationAddress"); 39 | const unisat_api_1 = require("../utils/unisat.api"); 40 | const network_config_1 = __importStar(require("../config/network.config")); 41 | // Create a new instance of the Inscription Router 42 | exports.SendOrdinalRouter = (0, express_1.Router)(); 43 | // @route POST api/inscribe/getSendingOrdinalBtcPsbt 44 | // @desc Inscribe Text Inscription 45 | // @access Private 46 | exports.SendOrdinalRouter.post("/getSendingOrdinalBtcPsbt", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 47 | try { 48 | if (!(req.body.receiveAddress && 49 | req.body.networkFee && 50 | req.body.paymentAddress && 51 | req.body.paymentPublicKey && 52 | req.body.ordinalsAddress && 53 | req.body.ordinalsPublicKey && 54 | req.body.btcAmount)) { 55 | let error = []; 56 | if (!req.body.receiveAddress) { 57 | error.push({ receiveAddress: "ReceiveAddress is required" }); 58 | } 59 | if (!req.body.networkFee) { 60 | error.push({ feeRate: "FeeRate is required" }); 61 | } 62 | if (!req.body.btcAmount) { 63 | error.push({ btcAmount: "btcAmount is required" }); 64 | } 65 | if (!req.body.paymentAddress) { 66 | error.push({ publicKey: "Payment Address is required" }); 67 | } 68 | if (!req.body.paymentPublicKey) { 69 | error.push({ publicKey: "Payment PublicKey is required" }); 70 | } 71 | if (!req.body.ordinalsAddress) { 72 | error.push({ publicKey: "Ordinals Address is required" }); 73 | } 74 | if (!req.body.ordinalsPublicKey) { 75 | error.push({ publicKey: "Ordinals Public Key is required" }); 76 | } 77 | res.status(400).send({ error: { type: 0, data: error } }); 78 | } 79 | else { 80 | if (!(0, validationAddress_1.isValidBitcoinAddress)(req.body.receiveAddress)) { 81 | res 82 | .status(400) 83 | .send({ type: 2, data: "This address is not valid address." }); 84 | } 85 | else { 86 | const feeRate = +req.body.networkFee; 87 | let parentId = ""; 88 | let reinscriptionId = ""; 89 | if (req.body.parentId) { 90 | const isContainOrdinalStatus = yield (0, unisat_api_1.isContainOrdinal)(req.body.parentId, req.body.ordinalsAddress, network_config_1.default.networkType); 91 | if (!isContainOrdinalStatus) { 92 | return res.status(400).send({ 93 | type: 5, 94 | data: `Parent Id does not contain on ${req.body.ordinalsAddress}`, 95 | }); 96 | } 97 | else { 98 | parentId = req.body.parentId; 99 | } 100 | } 101 | if (req.body.reinscriptionId) { 102 | const isContainOrdinalStatus = yield (0, unisat_api_1.isContainOrdinal)(req.body.reinscriptionId, req.body.ordinalsAddress, network_config_1.default.networkType); 103 | if (!isContainOrdinalStatus) { 104 | return res.status(400).send({ 105 | type: 5, 106 | data: `Reinscription Id does not contain on ${req.body.ordinalsAddress}`, 107 | }); 108 | } 109 | else { 110 | reinscriptionId = req.body.reinscriptionId; 111 | } 112 | } 113 | const sendOrdinalRequestData = { 114 | paymentAddress: req.body.paymentAddress, 115 | paymentPublicKey: req.body.paymentPublicKey, 116 | ordinalsAddress: req.body.ordinalsAddress, 117 | ordinalsPublicKey: req.body.ordinalsPublicKey, 118 | receiveAddress: req.body.receiveAddress, 119 | parentId: parentId, 120 | reinscriptionId: reinscriptionId, 121 | feeRate: feeRate, 122 | btcAmount: req.body.btcAmount, 123 | }; 124 | yield (0, inscribe_controller_1.SendingOrdinalController)(sendOrdinalRequestData, res); 125 | } 126 | } 127 | } 128 | catch (error) { 129 | console.error(error); 130 | return res.status(400).send({ error }); 131 | } 132 | })); 133 | exports.SendOrdinalRouter.get("/test", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 134 | try { 135 | const data = yield (0, unisat_api_1.testUnisatAPI)("tb1pymgsee4syh7ez4g9pm7gu0ax8wfj4wukwlxykfwnn6gx2tcr4r7quhsdlh", network_config_1.TESTNET); 136 | res.status(200).send({ 137 | data: data, 138 | }); 139 | } 140 | catch (error) { 141 | console.log({ error }); 142 | } 143 | })); 144 | -------------------------------------------------------------------------------- /src/routes/inscription.route.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, Router } from "express"; 2 | import { 3 | IFile, 4 | ITextInscription, 5 | IFileInscription, 6 | IDelegateInscription, 7 | } from "../utils/types"; 8 | import { 9 | TextInscribeController, 10 | DelegateInscribeController, 11 | FileInscribeController, 12 | } from "../controller/inscribe.controller"; 13 | import { isValidBitcoinAddress } from "../utils/validationAddress"; 14 | 15 | // Create a new instance of the Inscription Router 16 | export const InscriptionRouter = Router(); 17 | 18 | // @route POST api/inscribe/text 19 | // @desc Inscribe Text Inscription 20 | // @access Private 21 | InscriptionRouter.post("/text", async (req: Request, res: Response) => { 22 | try { 23 | if ( 24 | !( 25 | req.body.receiveAddress && 26 | req.body.textContent && 27 | req.body.networkFee && 28 | req.body.padding 29 | ) 30 | ) { 31 | let error = []; 32 | if (!req.body.receiveAddress) { 33 | error.push({ receiveAddress: "ReceiveAddress is required" }); 34 | } 35 | if (!req.body.textContent) { 36 | error.push({ contents: "Content is required" }); 37 | } 38 | if (!req.body.networkFee) { 39 | error.push({ feeRate: "FeeRate is required" }); 40 | } 41 | if (!req.body.padding) { 42 | error.push({ padding: "Padding is required" }); 43 | } 44 | 45 | res.status(400).send({ error: { type: 0, data: error } }); 46 | } else { 47 | if (!isValidBitcoinAddress(req.body.receiveAddress)) { 48 | res 49 | .status(400) 50 | .send({ type: 2, data: "This address is not valid address." }); 51 | } else { 52 | const feeRate: number = +req.body.networkFee; 53 | const padding: number = +req.body.padding; 54 | const metadata: string = req.body.metadata; 55 | const contents: Array = req.body.textContent.split("\n"); 56 | 57 | let txIndex = 0; 58 | if (req.body.parentId) { 59 | txIndex++; 60 | } 61 | if (req.body.reinscriptionId) { 62 | txIndex++; 63 | } 64 | 65 | const holderStatus: boolean = req.body.holderStatus ?? false; 66 | 67 | const textInscriptionData: ITextInscription = { 68 | ...req.body, 69 | feeRate: feeRate, 70 | padding: padding, 71 | metadata: metadata, 72 | contents: contents, 73 | txIndex: txIndex, 74 | holderStatus: holderStatus, 75 | }; 76 | 77 | await TextInscribeController(textInscriptionData, res); 78 | } 79 | } 80 | } catch (error: any) { 81 | console.error(error); 82 | return res.status(400).send({ error }); 83 | } 84 | }); 85 | 86 | // @route POST api/inscribe/file 87 | // @desc Inscribe File Inscription 88 | // @access Private 89 | InscriptionRouter.post("/file", async (req: Request, res: Response) => { 90 | try { 91 | if (!(req.body.receiveAddress && req.body.networkFee && req.body.padding)) { 92 | let error = []; 93 | if (!req.body.receiveAddress) { 94 | error.push({ receiveAddress: "ReceiveAddress is required" }); 95 | } 96 | if (!req.body.networkFee) { 97 | error.push({ feeRate: "FeeRate is required" }); 98 | } 99 | if (!req.body.padding) { 100 | error.push({ padding: "Padding is required" }); 101 | } 102 | 103 | res.status(400).send({ error: { type: 0, data: error } }); 104 | } else { 105 | if (!isValidBitcoinAddress(req.body.receiveAddress)) { 106 | res 107 | .status(400) 108 | .send({ type: 2, data: "This address is not valid address." }); 109 | } else { 110 | let files = req.files as any; 111 | let fileData = files["files[]"]; 112 | 113 | if (!Array.isArray(fileData)) { 114 | fileData = [fileData]; 115 | } 116 | const fileArray: Array = fileData.map((item: any) => { 117 | return { 118 | mimetype: item.mimetype, 119 | data: item.data, 120 | }; 121 | }); 122 | const feeRate: number = +req.body.networkFee; 123 | const padding: number = +req.body.padding; 124 | const metadata: string = req.body.metadata; 125 | 126 | let txIndex = 0; 127 | if (req.body.parentId) { 128 | txIndex++; 129 | } 130 | if (req.body.reinscriptionId) { 131 | txIndex++; 132 | } 133 | 134 | const holderStatus: boolean = req.body.holderStatus ?? false; 135 | const fileInscriptionData: IFileInscription = { 136 | ...req.body, 137 | feeRate: feeRate, 138 | padding: padding, 139 | files: fileArray, 140 | metadata: metadata, 141 | txIndex: txIndex, 142 | holderStatus: holderStatus, 143 | }; 144 | 145 | await FileInscribeController(fileInscriptionData, res); 146 | } 147 | } 148 | } catch (error: any) { 149 | console.error(error); 150 | return res.status(400).send({ error }); 151 | } 152 | }); 153 | 154 | // @route POST api/inscribe/delegate 155 | // @desc Inscribe Delegate Inscription Fee 156 | // @access Private 157 | InscriptionRouter.post("/delegate", async (req: Request, res: Response) => { 158 | try { 159 | if ( 160 | !( 161 | req.body.receiveAddress && 162 | req.body.delegateId && 163 | req.body.networkFee && 164 | req.body.padding 165 | ) 166 | ) { 167 | let error = []; 168 | if (!req.body.receiveAddress) { 169 | error.push({ receiveAddress: "ReceiveAddress is required" }); 170 | } 171 | if (!req.body.delegateId) { 172 | error.push({ delegateId: "DelegateId is required" }); 173 | } 174 | if (!req.body.networkFee) { 175 | error.push({ feeRate: "FeeRate is required" }); 176 | } 177 | if (!req.body.padding) { 178 | error.push({ padding: "Padding is required" }); 179 | } 180 | 181 | res.status(400).send({ error: { type: 0, data: error } }); 182 | } else { 183 | if (!isValidBitcoinAddress(req.body.receiveAddress)) { 184 | res 185 | .status(400) 186 | .send({ type: 2, data: "This address is not valid address." }); 187 | } else { 188 | const feeRate: number = +req.body.networkFee; 189 | const padding: number = +req.body.padding; 190 | const metadata: string = req.body.metadata; 191 | const delegateIds: Array = req.body.delegateId.split(","); 192 | 193 | let txIndex = 0; 194 | if (req.body.parentId) { 195 | txIndex++; 196 | } 197 | if (req.body.reinscriptionId) { 198 | txIndex++; 199 | } 200 | 201 | const holderStatus: boolean = req.body.holderStatus ?? false; 202 | 203 | const delegateInscriptionData: IDelegateInscription = { 204 | ...req.body, 205 | feeRate: feeRate, 206 | padding: padding, 207 | metadata: metadata, 208 | delegateIds: delegateIds, 209 | txIndex: txIndex, 210 | holderStatus: holderStatus, 211 | }; 212 | 213 | await DelegateInscribeController(delegateInscriptionData, res); 214 | } 215 | } 216 | } catch (error: any) { 217 | console.error(error); 218 | return res.status(400).send({ error: error }); 219 | } 220 | }); 221 | -------------------------------------------------------------------------------- /dist/src/utils/unisat.api.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | var _a; 15 | Object.defineProperty(exports, "__esModule", { value: true }); 16 | exports.testUnisatAPI = exports.getBtcUtxoInfo = exports.isContainOrdinal = exports.getInscriptionInfo = exports.delay = void 0; 17 | const axios_1 = __importDefault(require("axios")); 18 | const network_config_1 = require("../config/network.config"); 19 | const dotenv_1 = __importDefault(require("dotenv")); 20 | const mutex_1 = require("./mutex"); 21 | const __1 = require("../.."); 22 | dotenv_1.default.config(); 23 | const apiArray = JSON.parse((_a = process.env.OPENAPI_UNISAT_TOKEN) !== null && _a !== void 0 ? _a : ""); 24 | function delay(ms) { 25 | return new Promise((resolve) => setTimeout(resolve, ms)); 26 | } 27 | exports.delay = delay; 28 | const getInscriptionInfo = (inscriptionid, networkType) => __awaiter(void 0, void 0, void 0, function* () { 29 | try { 30 | yield (0, mutex_1.waitUtxoFlag)(); 31 | yield (0, mutex_1.setUtxoFlag)(1); 32 | if (__1.app.locals.iterator >= apiArray.length) { 33 | yield (0, mutex_1.setApiIterator)(0); 34 | } 35 | const url = `https://open-api${networkType == network_config_1.TESTNET ? "-testnet" : ""}.unisat.io/v1/indexer/inscription/info/${inscriptionid}`; 36 | const config = { 37 | headers: { 38 | Authorization: `Bearer ${apiArray[__1.app.locals.iterator]}`, 39 | }, 40 | }; 41 | let res = yield axios_1.default.get(url, config); 42 | let iterator = __1.app.locals.iterator + 1; 43 | yield (0, mutex_1.setApiIterator)(iterator); 44 | yield (0, mutex_1.setUtxoFlag)(0); 45 | const inscriptionInfo = res.data; 46 | const info = { 47 | txid: inscriptionInfo.data.utxo.txid, 48 | vout: inscriptionInfo.data.utxo.vout, 49 | value: inscriptionInfo.data.utxo.satoshi, 50 | }; 51 | return info; 52 | } 53 | catch (err) { 54 | yield (0, mutex_1.setUtxoFlag)(0); 55 | console.log("Get Inscription Utxo Error"); 56 | } 57 | }); 58 | exports.getInscriptionInfo = getInscriptionInfo; 59 | const isContainOrdinal = (inscriptionid, address, networkType) => __awaiter(void 0, void 0, void 0, function* () { 60 | try { 61 | yield (0, mutex_1.waitUtxoFlag)(); 62 | yield (0, mutex_1.setUtxoFlag)(1); 63 | if (__1.app.locals.iterator >= apiArray.length) { 64 | yield (0, mutex_1.setApiIterator)(0); 65 | } 66 | const url = `https://open-api${networkType == network_config_1.TESTNET ? "-testnet" : ""}.unisat.io/v1/indexer/inscription/info/${inscriptionid}`; 67 | const config = { 68 | headers: { 69 | Authorization: `Bearer ${apiArray[__1.app.locals.iterator]}`, 70 | }, 71 | }; 72 | const res = yield axios_1.default.get(url, config); 73 | let iterator = __1.app.locals.iterator + 1; 74 | yield (0, mutex_1.setApiIterator)(iterator); 75 | yield (0, mutex_1.setUtxoFlag)(0); 76 | const inscriptionInfo = res.data; 77 | if (address == inscriptionInfo.data.utxo.address) { 78 | return true; 79 | } 80 | return false; 81 | } 82 | catch (err) { 83 | yield (0, mutex_1.setUtxoFlag)(0); 84 | console.log("Get Inscription Utxo Error"); 85 | } 86 | }); 87 | exports.isContainOrdinal = isContainOrdinal; 88 | // Get BTC UTXO 89 | const getBtcUtxoInfo = (address, networkType) => __awaiter(void 0, void 0, void 0, function* () { 90 | yield (0, mutex_1.waitUtxoFlag)(); 91 | yield (0, mutex_1.setUtxoFlag)(1); 92 | if (__1.app.locals.iterator >= apiArray.length) { 93 | yield (0, mutex_1.setApiIterator)(0); 94 | } 95 | const url = `https://open-api${networkType == network_config_1.TESTNET ? "-testnet" : ""}.unisat.io/v1/indexer/address/${address}/utxo-data`; 96 | const config = { 97 | headers: { 98 | Authorization: `Bearer ${apiArray[__1.app.locals.iterator]}`, 99 | }, 100 | }; 101 | let iterator = __1.app.locals.iterator + 1; 102 | yield (0, mutex_1.setApiIterator)(iterator); 103 | let cursor = 0; 104 | const size = 5000; 105 | let utxos = []; 106 | while (1) { 107 | const res = yield axios_1.default.get(url, Object.assign(Object.assign({}, config), { params: { cursor, size } })); 108 | if (res.data.code === -1) 109 | throw "Invalid Address"; 110 | let fetchUtxos = res.data.data.utxo.reverse(); 111 | utxos.push(...fetchUtxos.map((utxo) => { 112 | return { 113 | txid: utxo.txid, 114 | value: utxo.satoshi, 115 | vout: utxo.vout, 116 | }; 117 | })); 118 | cursor += fetchUtxos.length; 119 | if (cursor >= res.data.data.total - res.data.data.totalRunes) 120 | break; 121 | } 122 | yield (0, mutex_1.setUtxoFlag)(0); 123 | return utxos; 124 | }); 125 | exports.getBtcUtxoInfo = getBtcUtxoInfo; 126 | const testUnisatAPI = (address, networkType) => __awaiter(void 0, void 0, void 0, function* () { 127 | yield (0, mutex_1.waitUtxoFlag)(); 128 | yield (0, mutex_1.setUtxoFlag)(1); 129 | if (__1.app.locals.iterator >= apiArray.length) { 130 | yield (0, mutex_1.setApiIterator)(0); 131 | } 132 | const url = `https://open-api${networkType == network_config_1.TESTNET ? "-testnet" : ""}.unisat.io/v1/indexer/address/${address}/utxo-data`; 133 | const config = { 134 | headers: { 135 | Authorization: `Bearer ${apiArray[__1.app.locals.iterator]}`, 136 | }, 137 | }; 138 | let iterator = __1.app.locals.iterator + 1; 139 | yield (0, mutex_1.setApiIterator)(iterator); 140 | let cursor = 0; 141 | const size = 5000; 142 | let utxos = []; 143 | while (1) { 144 | const res = yield axios_1.default.get(url, Object.assign(Object.assign({}, config), { params: { cursor, size } })); 145 | if (res.data.code === -1) 146 | throw "Invalid Address"; 147 | let fetchUtxos = res.data.data.utxo.reverse(); 148 | utxos.push(...fetchUtxos.map((utxo) => { 149 | return { 150 | txid: utxo.txid, 151 | value: utxo.satoshi, 152 | vout: utxo.vout, 153 | }; 154 | })); 155 | cursor += fetchUtxos.length; 156 | if (cursor >= res.data.data.total - res.data.data.totalRunes) 157 | break; 158 | } 159 | yield (0, mutex_1.setUtxoFlag)(0); 160 | return utxos; 161 | }); 162 | exports.testUnisatAPI = testUnisatAPI; 163 | -------------------------------------------------------------------------------- /src/services/utxo/utxo.ordinalsSendPsbt.ts: -------------------------------------------------------------------------------- 1 | import * as Bitcoin from "bitcoinjs-lib"; 2 | import ecc from "@bitcoinerlab/secp256k1"; 3 | import networkConfig, { TESTNET } from "../../config/network.config"; 4 | import { ISendingOrdinalData } from "../../utils/types"; 5 | import { getInscriptionInfo } from "../../utils/unisat.api"; 6 | import wallet from "../wallet/initializeWallet"; 7 | import { getTxHex } from "../../utils/mempool"; 8 | 9 | Bitcoin.initEccLib(ecc); 10 | 11 | interface IUtxo { 12 | txid: string; 13 | vout: number; 14 | value: number; 15 | } 16 | 17 | export const RedeemOrdinalsUtxoSendPsbt = async ( 18 | selectedUtxos: Array, 19 | networkType: string, 20 | sendingOrdinalData: ISendingOrdinalData, 21 | redeemFee: number 22 | ): Promise => { 23 | const psbt = new Bitcoin.Psbt({ 24 | network: 25 | networkType == TESTNET 26 | ? Bitcoin.networks.testnet 27 | : Bitcoin.networks.bitcoin, 28 | }); 29 | 30 | const network: Bitcoin.Network = 31 | networkType == TESTNET 32 | ? Bitcoin.networks.testnet 33 | : Bitcoin.networks.bitcoin; 34 | 35 | let inputUtxoSumValue: number = selectedUtxos.reduce( 36 | (accumulator: number, currentValue: IUtxo) => 37 | accumulator + currentValue.value, 38 | 0 39 | ); 40 | 41 | let parentInscriptionUTXO: IUtxo = { 42 | value: 0, 43 | txid: "", 44 | vout: 0, 45 | }; 46 | let reinscriptionUTXO: IUtxo = { 47 | value: 0, 48 | txid: "", 49 | vout: 0, 50 | }; 51 | 52 | if (sendingOrdinalData.parentId) { 53 | parentInscriptionUTXO = await getInscriptionInfo( 54 | sendingOrdinalData.parentId, 55 | networkConfig.networkType 56 | ); 57 | psbt.addInput({ 58 | hash: parentInscriptionUTXO.txid, 59 | index: parentInscriptionUTXO.vout, 60 | witnessUtxo: { 61 | value: parentInscriptionUTXO.value, 62 | script: wallet.output, 63 | }, 64 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 65 | }); 66 | } 67 | if (sendingOrdinalData.reinscriptionId) { 68 | reinscriptionUTXO = await getInscriptionInfo( 69 | sendingOrdinalData.reinscriptionId, 70 | networkConfig.networkType 71 | ); 72 | psbt.addInput({ 73 | hash: reinscriptionUTXO.txid, 74 | index: reinscriptionUTXO.vout, 75 | witnessUtxo: { 76 | value: reinscriptionUTXO.value, 77 | script: wallet.output, 78 | }, 79 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 80 | }); 81 | } 82 | 83 | selectedUtxos.forEach((utxo) => { 84 | psbt.addInput({ 85 | hash: utxo.txid, 86 | index: utxo.vout, 87 | witnessUtxo: { 88 | value: utxo.value, 89 | script: wallet.output, 90 | }, 91 | tapInternalKey: Buffer.from(wallet.publicKey, "hex").subarray(1, 33), 92 | }); 93 | }); 94 | 95 | if (sendingOrdinalData.parentId) { 96 | psbt.addOutput({ 97 | address: wallet.address, 98 | value: parentInscriptionUTXO.value, 99 | }); 100 | } 101 | 102 | if (sendingOrdinalData.reinscriptionId) { 103 | psbt.addOutput({ 104 | address: wallet.address, 105 | value: reinscriptionUTXO.value, 106 | }); 107 | } 108 | 109 | psbt.addOutput({ 110 | address: wallet.address, 111 | value: sendingOrdinalData.btcAmount, 112 | }); 113 | 114 | psbt.addOutput({ 115 | address: wallet.address, 116 | value: inputUtxoSumValue - redeemFee - sendingOrdinalData.btcAmount, 117 | }); 118 | 119 | return psbt; 120 | }; 121 | 122 | export const OrdinalsUtxoSendPsbt = async ( 123 | selectedUtxos: Array, 124 | networkType: string, 125 | sendingOrdinalData: ISendingOrdinalData, 126 | redeemFee: number 127 | ): Promise => { 128 | const psbt = new Bitcoin.Psbt({ 129 | network: 130 | networkType == TESTNET 131 | ? Bitcoin.networks.testnet 132 | : Bitcoin.networks.bitcoin, 133 | }); 134 | 135 | const network: Bitcoin.Network = 136 | networkType == TESTNET 137 | ? Bitcoin.networks.testnet 138 | : Bitcoin.networks.bitcoin; 139 | 140 | let inputUtxoSumValue: number = selectedUtxos.reduce( 141 | (accumulator: number, currentValue: IUtxo) => 142 | accumulator + currentValue.value, 143 | 0 144 | ); 145 | let parentInscriptionUTXO: IUtxo = { 146 | txid: "", 147 | vout: 0, 148 | value: 0, 149 | }; 150 | let reInscriptionUTXO: IUtxo = { 151 | txid: "", 152 | vout: 0, 153 | value: 0, 154 | }; 155 | 156 | if (sendingOrdinalData.parentId) { 157 | parentInscriptionUTXO = await getInscriptionInfo( 158 | sendingOrdinalData.parentId, 159 | networkConfig.networkType 160 | ); 161 | psbt.addInput({ 162 | hash: parentInscriptionUTXO.txid, 163 | index: parentInscriptionUTXO.vout, 164 | witnessUtxo: { 165 | value: parentInscriptionUTXO.value, 166 | script: Bitcoin.address.toOutputScript( 167 | sendingOrdinalData.ordinalsAddress as string, 168 | network 169 | ), 170 | }, 171 | tapInternalKey: Buffer.from(sendingOrdinalData.ordinalsPublicKey, "hex"), 172 | }); 173 | } 174 | if (sendingOrdinalData.reinscriptionId) { 175 | reInscriptionUTXO = await getInscriptionInfo( 176 | sendingOrdinalData.reinscriptionId, 177 | networkConfig.networkType 178 | ); 179 | 180 | psbt.addInput({ 181 | hash: reInscriptionUTXO.txid, 182 | index: reInscriptionUTXO.vout, 183 | witnessUtxo: { 184 | value: reInscriptionUTXO.value, 185 | script: Bitcoin.address.toOutputScript( 186 | sendingOrdinalData.ordinalsAddress as string, 187 | network 188 | ), 189 | }, 190 | tapInternalKey: Buffer.from(sendingOrdinalData.ordinalsPublicKey, "hex"), 191 | }); 192 | } 193 | if (sendingOrdinalData.ordinalsAddress == sendingOrdinalData.paymentAddress) { 194 | selectedUtxos.forEach((utxo) => { 195 | psbt.addInput({ 196 | hash: utxo.txid, 197 | index: utxo.vout, 198 | witnessUtxo: { 199 | value: utxo.value, 200 | script: Bitcoin.address.toOutputScript( 201 | sendingOrdinalData.paymentAddress as string, 202 | network 203 | ), 204 | }, 205 | tapInternalKey: Buffer.from(sendingOrdinalData.paymentPublicKey, "hex"), 206 | }); 207 | }); 208 | } else { 209 | // Create a Pay-to-Public-Key-Hash (P2PKH) script 210 | const p2pkhScript = Bitcoin.script.compile([ 211 | Bitcoin.opcodes.OP_0, // OP_0 indicates a P2PKH script 212 | Bitcoin.crypto.hash160( 213 | Buffer.from(sendingOrdinalData.paymentPublicKey, "hex") 214 | ), // Hash160 of the public key 215 | ]); 216 | 217 | for (let i = 0; i < selectedUtxos.length; i++) { 218 | const txHex = await getTxHex(selectedUtxos[i].txid, networkType); 219 | psbt.addInput({ 220 | hash: selectedUtxos[i].txid, 221 | index: selectedUtxos[i].vout, 222 | nonWitnessUtxo: Buffer.from(txHex, "hex"), 223 | redeemScript: p2pkhScript, 224 | }); 225 | } 226 | } 227 | 228 | if (sendingOrdinalData.parentId) { 229 | psbt.addOutput({ 230 | address: wallet.address, 231 | value: parentInscriptionUTXO.value, 232 | }); 233 | } 234 | 235 | if (sendingOrdinalData.reinscriptionId) { 236 | psbt.addOutput({ 237 | address: wallet.address, 238 | value: reInscriptionUTXO.value, 239 | }); 240 | } 241 | 242 | psbt.addOutput({ 243 | address: wallet.address, 244 | value: sendingOrdinalData.btcAmount, 245 | }); 246 | 247 | psbt.addOutput({ 248 | address: sendingOrdinalData.paymentAddress, 249 | value: inputUtxoSumValue - redeemFee - sendingOrdinalData.btcAmount, 250 | }); 251 | 252 | return psbt; 253 | }; 254 | -------------------------------------------------------------------------------- /src/controller/estimate.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ITextInscription, 3 | IFileInscription, 4 | IDelegateInscription, 5 | } from "../utils/types"; 6 | import { Response } from "express"; 7 | import { fileTapScript } from "../services/tapscript/fileTapScript"; 8 | import { textTapScript } from "../services/tapscript/textTapScript"; 9 | import { inscriptionPsbt } from "../services/psbt/inscriptionPsbt"; 10 | import { 11 | DELEGATE_CONTENT, 12 | FILE_CONTENT, 13 | TEXT_CONTENT, 14 | } from "../config/network.config"; 15 | import { Transaction } from "bitcoinjs-lib"; 16 | import tapleafPsbt from "../services/psbt/TapLeafPsbtCreate"; 17 | import { toInteger } from "../utils/math"; 18 | import { delegateTapScript } from "../services/tapscript/delegateTapScript"; 19 | 20 | export const TextEstimateFeeController = async ( 21 | inscriptionData: ITextInscription, 22 | res: Response 23 | ) => { 24 | try { 25 | const tapScript = await textTapScript(inscriptionData); 26 | 27 | const sentUtxo = { 28 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 29 | vout: 0, 30 | value: 1000000, 31 | }; 32 | const contentType = TEXT_CONTENT; 33 | 34 | const redeemInscriptionData = { 35 | ...inscriptionData, 36 | ordinalsAddress: 37 | "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", 38 | ordinalsPublicKey: 39 | "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", 40 | paymentAddress: 41 | "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", 42 | paymentPublicKey: 43 | "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", 44 | }; 45 | const inscriptionTxData: Transaction = await inscriptionPsbt( 46 | contentType, 47 | redeemInscriptionData, 48 | tapScript, 49 | sentUtxo 50 | ); 51 | 52 | const sendUTXOSize = 53 | inscriptionTxData.virtualSize() * inscriptionData.feeRate + 54 | inscriptionData.contents.length * inscriptionData.padding; 55 | 56 | const userUtxo = { 57 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 58 | vout: 0, 59 | value: 10 * 10 ** 8, 60 | }; 61 | const tapleafTxData: Transaction = await tapleafPsbt( 62 | contentType, 63 | inscriptionData, 64 | tapScript, 65 | userUtxo, 66 | sendUTXOSize 67 | ); 68 | 69 | const totalFee = 70 | tapleafTxData.virtualSize() * inscriptionData.feeRate + sendUTXOSize; 71 | const total = 72 | inscriptionData.padding * inscriptionData.contents.length + 73 | totalFee + 74 | toInteger(totalFee / 50) + 75 | toInteger(totalFee / 20); 76 | return res.status(200).send({ 77 | satsInItem: inscriptionData.padding * inscriptionData.contents.length, 78 | fee: totalFee, 79 | serviceFee: toInteger(totalFee / 50), 80 | feeBySize: toInteger(totalFee / 20), 81 | total: total, 82 | }); 83 | } catch (error) { 84 | console.log(error); 85 | return res.status(400).send({ error }); 86 | } 87 | }; 88 | 89 | export const FileEstimateFeeController = async ( 90 | inscriptionData: IFileInscription, 91 | res: Response 92 | ) => { 93 | try { 94 | const tapScript = await fileTapScript(inscriptionData); 95 | 96 | const sentUtxo = { 97 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 98 | vout: 0, 99 | value: 1000000, 100 | }; 101 | const contentType = FILE_CONTENT; 102 | 103 | const redeemInscriptionData = { 104 | ...inscriptionData, 105 | ordinalsAddress: 106 | "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", 107 | ordinalsPublicKey: 108 | "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", 109 | paymentAddress: 110 | "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", 111 | paymentPublicKey: 112 | "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", 113 | }; 114 | const inscriptionTxData: Transaction = await inscriptionPsbt( 115 | contentType, 116 | redeemInscriptionData, 117 | tapScript, 118 | sentUtxo 119 | ); 120 | 121 | const sendUTXOSize = 122 | inscriptionTxData.virtualSize() * inscriptionData.feeRate + 123 | inscriptionData.files.length * inscriptionData.padding; 124 | 125 | const userUtxo = { 126 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 127 | vout: 0, 128 | value: 10 * 10 ** 8, 129 | }; 130 | 131 | const tapleafTxData: Transaction = await tapleafPsbt( 132 | contentType, 133 | inscriptionData, 134 | tapScript, 135 | userUtxo, 136 | sendUTXOSize 137 | ); 138 | 139 | const totalFee = 140 | tapleafTxData.virtualSize() * inscriptionData.feeRate + sendUTXOSize; 141 | const total = 142 | inscriptionData.padding * inscriptionData.files.length + 143 | totalFee + 144 | toInteger(totalFee / 50) + 145 | toInteger(totalFee / 20); 146 | return res.status(200).send({ 147 | satsInItem: inscriptionData.padding * inscriptionData.files.length, 148 | fee: totalFee, 149 | serviceFee: toInteger(totalFee / 50), 150 | feeBySize: toInteger(totalFee / 20), 151 | total: total, 152 | }); 153 | } catch (error) { 154 | console.log(error); 155 | return res.status(400).send({ error }); 156 | } 157 | }; 158 | 159 | export const DelegateEstimateFeeController = async ( 160 | inscriptionData: IDelegateInscription, 161 | res: Response 162 | ) => { 163 | try { 164 | const tapScript = await delegateTapScript(inscriptionData); 165 | 166 | const sentUtxo = { 167 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 168 | vout: 0, 169 | value: 1000000, 170 | }; 171 | const contentType = DELEGATE_CONTENT; 172 | 173 | const redeemInscriptionData = { 174 | ...inscriptionData, 175 | ordinalsAddress: 176 | "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", 177 | ordinalsPublicKey: 178 | "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", 179 | paymentAddress: 180 | "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", 181 | paymentPublicKey: 182 | "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", 183 | }; 184 | const inscriptionTxData: Transaction = await inscriptionPsbt( 185 | contentType, 186 | redeemInscriptionData, 187 | tapScript, 188 | sentUtxo 189 | ); 190 | 191 | const sendUTXOSize = 192 | inscriptionTxData.virtualSize() * inscriptionData.feeRate + 193 | inscriptionData.delegateIds.length * inscriptionData.padding; 194 | 195 | const userUtxo = { 196 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 197 | vout: 0, 198 | value: 10 * 10 ** 8, 199 | }; 200 | 201 | const tapleafTxData: Transaction = await tapleafPsbt( 202 | contentType, 203 | inscriptionData, 204 | tapScript, 205 | userUtxo, 206 | sendUTXOSize 207 | ); 208 | 209 | const totalFee = 210 | tapleafTxData.virtualSize() * inscriptionData.feeRate + sendUTXOSize; 211 | const total = 212 | inscriptionData.padding * inscriptionData.delegateIds.length + 213 | totalFee + 214 | toInteger(totalFee / 50) + 215 | toInteger(totalFee / 20); 216 | return res.status(200).send({ 217 | satsInItem: inscriptionData.padding * inscriptionData.delegateIds.length, 218 | fee: totalFee, 219 | serviceFee: toInteger(totalFee / 50), 220 | feeBySize: toInteger(totalFee / 20), 221 | total: total, 222 | }); 223 | } catch (error) { 224 | console.log(error); 225 | return res.status(400).send({ error }); 226 | } 227 | }; 228 | -------------------------------------------------------------------------------- /src/controller/inscribe.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ITextInscription, 3 | IFileInscription, 4 | IDelegateInscription, 5 | ISendingOrdinalData, 6 | IUtxo, 7 | } from "../utils/types"; 8 | import e, { Response } from "express"; 9 | import { fileTapScript } from "../services/tapscript/fileTapScript"; 10 | import { textTapScript } from "../services/tapscript/textTapScript"; 11 | import { inscriptionPsbt } from "../services/psbt/inscriptionPsbt"; 12 | import networkConfig, { 13 | DELEGATE_CONTENT, 14 | FILE_CONTENT, 15 | TEXT_CONTENT, 16 | } from "../config/network.config"; 17 | import { Transaction } from "bitcoinjs-lib"; 18 | import { delegateTapScript } from "../services/tapscript/delegateTapScript"; 19 | import tapleafPsbt from "../services/psbt/TapLeafPsbtCreate"; 20 | import { sendOrdinalBTCPsbt } from "../services/psbt/sendOrdinalPsbt"; 21 | import { pushBTCpmt } from "../utils/mempool"; 22 | import { delay } from "../utils/unisat.api"; 23 | 24 | export const TextInscribeController = async ( 25 | inscriptionData: ITextInscription, 26 | res: Response 27 | ) => { 28 | try { 29 | const tapScript = await textTapScript(inscriptionData); 30 | 31 | const sentUtxo = { 32 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 33 | vout: 0, 34 | value: 1000000, 35 | }; 36 | const contentType = TEXT_CONTENT; 37 | 38 | const inscriptionTxData: Transaction = await inscriptionPsbt( 39 | contentType, 40 | inscriptionData, 41 | tapScript, 42 | sentUtxo 43 | ); 44 | 45 | const sendUTXOSize = 46 | inscriptionTxData.virtualSize() * inscriptionData.feeRate + 47 | inscriptionData.contents.length * inscriptionData.padding; 48 | 49 | const userUtxo: IUtxo = { 50 | txid: inscriptionData.sendBtcTxId, 51 | vout: inscriptionData.txIndex, 52 | value: inscriptionData.btcAmount, 53 | }; 54 | 55 | console.log("Starting Waiting 5s!"); 56 | 57 | await delay(5000); 58 | 59 | const tapleafTxData: Transaction = await tapleafPsbt( 60 | contentType, 61 | inscriptionData, 62 | tapScript, 63 | userUtxo, 64 | sendUTXOSize 65 | ); 66 | 67 | const txid = await pushBTCpmt( 68 | tapleafTxData.toHex(), 69 | networkConfig.networkType 70 | ); 71 | 72 | const sendingUtxo = { 73 | txid: txid, 74 | vout: 0, 75 | value: sendUTXOSize, 76 | }; 77 | 78 | console.log("Sent Utxo for inscribing => ", sendingUtxo); 79 | 80 | const realInscriptionTxData: Transaction = await inscriptionPsbt( 81 | contentType, 82 | inscriptionData, 83 | tapScript, 84 | sendingUtxo 85 | ); 86 | 87 | const realInscriptiontxId = await pushBTCpmt( 88 | realInscriptionTxData.toHex(), 89 | networkConfig.networkType 90 | ); 91 | 92 | console.log("Successfully Inscribed Tx Id => ", realInscriptiontxId); 93 | 94 | return res.status(200).send({ 95 | tx: realInscriptiontxId, 96 | }); 97 | } catch (error) { 98 | console.log(error); 99 | return res.status(400).send({ error }); 100 | } 101 | }; 102 | 103 | export const FileInscribeController = async ( 104 | inscriptionData: IFileInscription, 105 | res: Response 106 | ) => { 107 | try { 108 | const tapScript = await fileTapScript(inscriptionData); 109 | 110 | const sentUtxo = { 111 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 112 | vout: 0, 113 | value: 1000000, 114 | }; 115 | const contentType = FILE_CONTENT; 116 | 117 | const inscriptionTxData: Transaction = await inscriptionPsbt( 118 | contentType, 119 | inscriptionData, 120 | tapScript, 121 | sentUtxo 122 | ); 123 | 124 | const sendUTXOSize = 125 | inscriptionTxData.virtualSize() * inscriptionData.feeRate + 126 | inscriptionData.files.length * inscriptionData.padding; 127 | 128 | const userUtxo: IUtxo = { 129 | txid: inscriptionData.sendBtcTxId, 130 | vout: inscriptionData.txIndex, 131 | value: +inscriptionData.btcAmount as number, 132 | }; 133 | 134 | console.log("Starting Waiting 5s!"); 135 | 136 | await delay(5000); 137 | 138 | const tapleafTxData: Transaction = await tapleafPsbt( 139 | contentType, 140 | inscriptionData, 141 | tapScript, 142 | userUtxo, 143 | sendUTXOSize 144 | ); 145 | 146 | const txid = await pushBTCpmt( 147 | tapleafTxData.toHex(), 148 | networkConfig.networkType 149 | ); 150 | 151 | const sendingUtxo = { 152 | txid: txid, 153 | vout: 0, 154 | value: sendUTXOSize, 155 | }; 156 | 157 | console.log("here5"); 158 | console.log("Sent Utxo for inscribing => ", sendingUtxo); 159 | 160 | const realInscriptionTxData: Transaction = await inscriptionPsbt( 161 | contentType, 162 | inscriptionData, 163 | tapScript, 164 | sendingUtxo 165 | ); 166 | 167 | const realInscriptiontxId = await pushBTCpmt( 168 | realInscriptionTxData.toHex(), 169 | networkConfig.networkType 170 | ); 171 | 172 | console.log("Successfully Inscribed Tx Id => ", realInscriptiontxId); 173 | 174 | return res.status(200).send({ 175 | tx: realInscriptiontxId, 176 | }); 177 | } catch (error) { 178 | console.log(error); 179 | return res.status(400).send({ error }); 180 | } 181 | }; 182 | 183 | export const DelegateInscribeController = async ( 184 | inscriptionData: IDelegateInscription, 185 | res: Response 186 | ) => { 187 | try { 188 | const tapScript = await delegateTapScript(inscriptionData); 189 | 190 | const sentUtxo = { 191 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 192 | vout: 0, 193 | value: 1000000, 194 | }; 195 | const contentType = DELEGATE_CONTENT; 196 | 197 | const inscriptionTxData: Transaction = await inscriptionPsbt( 198 | contentType, 199 | inscriptionData, 200 | tapScript, 201 | sentUtxo 202 | ); 203 | 204 | const sendUTXOSize = 205 | inscriptionTxData.virtualSize() * inscriptionData.feeRate + 206 | inscriptionData.delegateIds.length * inscriptionData.padding; 207 | 208 | const userUtxo: IUtxo = { 209 | txid: inscriptionData.sendBtcTxId, 210 | vout: inscriptionData.txIndex, 211 | value: inscriptionData.btcAmount, 212 | }; 213 | 214 | console.log("Starting Waiting 5s!"); 215 | 216 | await delay(5000); 217 | 218 | const tapleafTxData: Transaction = await tapleafPsbt( 219 | contentType, 220 | inscriptionData, 221 | tapScript, 222 | userUtxo, 223 | sendUTXOSize 224 | ); 225 | 226 | const txid = await pushBTCpmt( 227 | tapleafTxData.toHex(), 228 | networkConfig.networkType 229 | ); 230 | 231 | const sendingUtxo = { 232 | txid: txid, 233 | vout: 0, 234 | value: sendUTXOSize, 235 | }; 236 | 237 | console.log("Sent Utxo for inscribing => ", sendingUtxo); 238 | 239 | const realInscriptionTxData: Transaction = await inscriptionPsbt( 240 | contentType, 241 | inscriptionData, 242 | tapScript, 243 | sendingUtxo 244 | ); 245 | 246 | const realInscriptiontxId = await pushBTCpmt( 247 | realInscriptionTxData.toHex(), 248 | networkConfig.networkType 249 | ); 250 | 251 | console.log("Successfully Inscribed Tx Id => ", realInscriptiontxId); 252 | 253 | return res.status(200).send({ 254 | tx: realInscriptiontxId, 255 | }); 256 | } catch (error) { 257 | console.log(error); 258 | return res.status(400).send({ error }); 259 | } 260 | }; 261 | 262 | export const SendingOrdinalController = async ( 263 | sendingOrdinalData: ISendingOrdinalData, 264 | res: Response 265 | ) => { 266 | try { 267 | const response = await sendOrdinalBTCPsbt(sendingOrdinalData); 268 | if (!response.isSuccess) { 269 | return res.status(400).send({ data: response.data }); 270 | } else { 271 | console.log(response.data.toHex()); 272 | 273 | return res.status(200).send({ data: response.data.toHex() }); 274 | } 275 | } catch (error) { 276 | return res.status(400).send({ error }); 277 | } 278 | }; 279 | -------------------------------------------------------------------------------- /dist/src/routes/inscription.route.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | exports.InscriptionRouter = void 0; 13 | const express_1 = require("express"); 14 | const inscribe_controller_1 = require("../controller/inscribe.controller"); 15 | const validationAddress_1 = require("../utils/validationAddress"); 16 | // Create a new instance of the Inscription Router 17 | exports.InscriptionRouter = (0, express_1.Router)(); 18 | // @route POST api/inscribe/text 19 | // @desc Inscribe Text Inscription 20 | // @access Private 21 | exports.InscriptionRouter.post("/text", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 22 | var _a; 23 | try { 24 | if (!(req.body.receiveAddress && 25 | req.body.textContent && 26 | req.body.networkFee && 27 | req.body.padding)) { 28 | let error = []; 29 | if (!req.body.receiveAddress) { 30 | error.push({ receiveAddress: "ReceiveAddress is required" }); 31 | } 32 | if (!req.body.textContent) { 33 | error.push({ contents: "Content is required" }); 34 | } 35 | if (!req.body.networkFee) { 36 | error.push({ feeRate: "FeeRate is required" }); 37 | } 38 | if (!req.body.padding) { 39 | error.push({ padding: "Padding is required" }); 40 | } 41 | res.status(400).send({ error: { type: 0, data: error } }); 42 | } 43 | else { 44 | if (!(0, validationAddress_1.isValidBitcoinAddress)(req.body.receiveAddress)) { 45 | res 46 | .status(400) 47 | .send({ type: 2, data: "This address is not valid address." }); 48 | } 49 | else { 50 | const feeRate = +req.body.networkFee; 51 | const padding = +req.body.padding; 52 | const metadata = req.body.metadata; 53 | const contents = req.body.textContent.split("\n"); 54 | let txIndex = 0; 55 | if (req.body.parentId) { 56 | txIndex++; 57 | } 58 | if (req.body.reinscriptionId) { 59 | txIndex++; 60 | } 61 | const holderStatus = (_a = req.body.holderStatus) !== null && _a !== void 0 ? _a : false; 62 | const textInscriptionData = Object.assign(Object.assign({}, req.body), { feeRate: feeRate, padding: padding, metadata: metadata, contents: contents, txIndex: txIndex, holderStatus: holderStatus }); 63 | yield (0, inscribe_controller_1.TextInscribeController)(textInscriptionData, res); 64 | } 65 | } 66 | } 67 | catch (error) { 68 | console.error(error); 69 | return res.status(400).send({ error }); 70 | } 71 | })); 72 | // @route POST api/inscribe/file 73 | // @desc Inscribe File Inscription 74 | // @access Private 75 | exports.InscriptionRouter.post("/file", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 76 | var _b; 77 | try { 78 | if (!(req.body.receiveAddress && req.body.networkFee && req.body.padding)) { 79 | let error = []; 80 | if (!req.body.receiveAddress) { 81 | error.push({ receiveAddress: "ReceiveAddress is required" }); 82 | } 83 | if (!req.body.networkFee) { 84 | error.push({ feeRate: "FeeRate is required" }); 85 | } 86 | if (!req.body.padding) { 87 | error.push({ padding: "Padding is required" }); 88 | } 89 | res.status(400).send({ error: { type: 0, data: error } }); 90 | } 91 | else { 92 | if (!(0, validationAddress_1.isValidBitcoinAddress)(req.body.receiveAddress)) { 93 | res 94 | .status(400) 95 | .send({ type: 2, data: "This address is not valid address." }); 96 | } 97 | else { 98 | let files = req.files; 99 | let fileData = files["files[]"]; 100 | if (!Array.isArray(fileData)) { 101 | fileData = [fileData]; 102 | } 103 | const fileArray = fileData.map((item) => { 104 | return { 105 | mimetype: item.mimetype, 106 | data: item.data, 107 | }; 108 | }); 109 | const feeRate = +req.body.networkFee; 110 | const padding = +req.body.padding; 111 | const metadata = req.body.metadata; 112 | let txIndex = 0; 113 | if (req.body.parentId) { 114 | txIndex++; 115 | } 116 | if (req.body.reinscriptionId) { 117 | txIndex++; 118 | } 119 | const holderStatus = (_b = req.body.holderStatus) !== null && _b !== void 0 ? _b : false; 120 | const fileInscriptionData = Object.assign(Object.assign({}, req.body), { feeRate: feeRate, padding: padding, files: fileArray, metadata: metadata, txIndex: txIndex, holderStatus: holderStatus }); 121 | yield (0, inscribe_controller_1.FileInscribeController)(fileInscriptionData, res); 122 | } 123 | } 124 | } 125 | catch (error) { 126 | console.error(error); 127 | return res.status(400).send({ error }); 128 | } 129 | })); 130 | // @route POST api/inscribe/delegate 131 | // @desc Inscribe Delegate Inscription Fee 132 | // @access Private 133 | exports.InscriptionRouter.post("/delegate", (req, res) => __awaiter(void 0, void 0, void 0, function* () { 134 | var _c; 135 | try { 136 | if (!(req.body.receiveAddress && 137 | req.body.delegateId && 138 | req.body.networkFee && 139 | req.body.padding)) { 140 | let error = []; 141 | if (!req.body.receiveAddress) { 142 | error.push({ receiveAddress: "ReceiveAddress is required" }); 143 | } 144 | if (!req.body.delegateId) { 145 | error.push({ delegateId: "DelegateId is required" }); 146 | } 147 | if (!req.body.networkFee) { 148 | error.push({ feeRate: "FeeRate is required" }); 149 | } 150 | if (!req.body.padding) { 151 | error.push({ padding: "Padding is required" }); 152 | } 153 | res.status(400).send({ error: { type: 0, data: error } }); 154 | } 155 | else { 156 | if (!(0, validationAddress_1.isValidBitcoinAddress)(req.body.receiveAddress)) { 157 | res 158 | .status(400) 159 | .send({ type: 2, data: "This address is not valid address." }); 160 | } 161 | else { 162 | const feeRate = +req.body.networkFee; 163 | const padding = +req.body.padding; 164 | const metadata = req.body.metadata; 165 | const delegateIds = req.body.delegateId.split(","); 166 | let txIndex = 0; 167 | if (req.body.parentId) { 168 | txIndex++; 169 | } 170 | if (req.body.reinscriptionId) { 171 | txIndex++; 172 | } 173 | const holderStatus = (_c = req.body.holderStatus) !== null && _c !== void 0 ? _c : false; 174 | const delegateInscriptionData = Object.assign(Object.assign({}, req.body), { feeRate: feeRate, padding: padding, metadata: metadata, delegateIds: delegateIds, txIndex: txIndex, holderStatus: holderStatus }); 175 | yield (0, inscribe_controller_1.DelegateInscribeController)(delegateInscriptionData, res); 176 | } 177 | } 178 | } 179 | catch (error) { 180 | console.error(error); 181 | return res.status(400).send({ error: error }); 182 | } 183 | })); 184 | -------------------------------------------------------------------------------- /dist/src/controller/estimate.controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | var __importDefault = (this && this.__importDefault) || function (mod) { 12 | return (mod && mod.__esModule) ? mod : { "default": mod }; 13 | }; 14 | Object.defineProperty(exports, "__esModule", { value: true }); 15 | exports.DelegateEstimateFeeController = exports.FileEstimateFeeController = exports.TextEstimateFeeController = void 0; 16 | const fileTapScript_1 = require("../services/tapscript/fileTapScript"); 17 | const textTapScript_1 = require("../services/tapscript/textTapScript"); 18 | const inscriptionPsbt_1 = require("../services/psbt/inscriptionPsbt"); 19 | const network_config_1 = require("../config/network.config"); 20 | const TapLeafPsbtCreate_1 = __importDefault(require("../services/psbt/TapLeafPsbtCreate")); 21 | const math_1 = require("../utils/math"); 22 | const delegateTapScript_1 = require("../services/tapscript/delegateTapScript"); 23 | const TextEstimateFeeController = (inscriptionData, res) => __awaiter(void 0, void 0, void 0, function* () { 24 | try { 25 | const tapScript = yield (0, textTapScript_1.textTapScript)(inscriptionData); 26 | const sentUtxo = { 27 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 28 | vout: 0, 29 | value: 1000000, 30 | }; 31 | const contentType = network_config_1.TEXT_CONTENT; 32 | const redeemInscriptionData = Object.assign(Object.assign({}, inscriptionData), { ordinalsAddress: "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", ordinalsPublicKey: "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", paymentAddress: "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", paymentPublicKey: "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d" }); 33 | const inscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, redeemInscriptionData, tapScript, sentUtxo); 34 | const sendUTXOSize = inscriptionTxData.virtualSize() * inscriptionData.feeRate + 35 | inscriptionData.contents.length * inscriptionData.padding; 36 | const userUtxo = { 37 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 38 | vout: 0, 39 | value: 10 * Math.pow(10, 8), 40 | }; 41 | const tapleafTxData = yield (0, TapLeafPsbtCreate_1.default)(contentType, inscriptionData, tapScript, userUtxo, sendUTXOSize); 42 | const totalFee = tapleafTxData.virtualSize() * inscriptionData.feeRate + sendUTXOSize; 43 | const total = inscriptionData.padding * inscriptionData.contents.length + 44 | totalFee + 45 | (0, math_1.toInteger)(totalFee / 50) + 46 | (0, math_1.toInteger)(totalFee / 20); 47 | return res.status(200).send({ 48 | satsInItem: inscriptionData.padding * inscriptionData.contents.length, 49 | fee: totalFee, 50 | serviceFee: (0, math_1.toInteger)(totalFee / 50), 51 | feeBySize: (0, math_1.toInteger)(totalFee / 20), 52 | total: total, 53 | }); 54 | } 55 | catch (error) { 56 | console.log(error); 57 | return res.status(400).send({ error }); 58 | } 59 | }); 60 | exports.TextEstimateFeeController = TextEstimateFeeController; 61 | const FileEstimateFeeController = (inscriptionData, res) => __awaiter(void 0, void 0, void 0, function* () { 62 | try { 63 | const tapScript = yield (0, fileTapScript_1.fileTapScript)(inscriptionData); 64 | const sentUtxo = { 65 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 66 | vout: 0, 67 | value: 1000000, 68 | }; 69 | const contentType = network_config_1.FILE_CONTENT; 70 | const redeemInscriptionData = Object.assign(Object.assign({}, inscriptionData), { ordinalsAddress: "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", ordinalsPublicKey: "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", paymentAddress: "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", paymentPublicKey: "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d" }); 71 | const inscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, redeemInscriptionData, tapScript, sentUtxo); 72 | const sendUTXOSize = inscriptionTxData.virtualSize() * inscriptionData.feeRate + 73 | inscriptionData.files.length * inscriptionData.padding; 74 | const userUtxo = { 75 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 76 | vout: 0, 77 | value: 10 * Math.pow(10, 8), 78 | }; 79 | const tapleafTxData = yield (0, TapLeafPsbtCreate_1.default)(contentType, inscriptionData, tapScript, userUtxo, sendUTXOSize); 80 | const totalFee = tapleafTxData.virtualSize() * inscriptionData.feeRate + sendUTXOSize; 81 | const total = inscriptionData.padding * inscriptionData.files.length + 82 | totalFee + 83 | (0, math_1.toInteger)(totalFee / 50) + 84 | (0, math_1.toInteger)(totalFee / 20); 85 | return res.status(200).send({ 86 | satsInItem: inscriptionData.padding * inscriptionData.files.length, 87 | fee: totalFee, 88 | serviceFee: (0, math_1.toInteger)(totalFee / 50), 89 | feeBySize: (0, math_1.toInteger)(totalFee / 20), 90 | total: total, 91 | }); 92 | } 93 | catch (error) { 94 | console.log(error); 95 | return res.status(400).send({ error }); 96 | } 97 | }); 98 | exports.FileEstimateFeeController = FileEstimateFeeController; 99 | const DelegateEstimateFeeController = (inscriptionData, res) => __awaiter(void 0, void 0, void 0, function* () { 100 | try { 101 | const tapScript = yield (0, delegateTapScript_1.delegateTapScript)(inscriptionData); 102 | const sentUtxo = { 103 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 104 | vout: 0, 105 | value: 1000000, 106 | }; 107 | const contentType = network_config_1.DELEGATE_CONTENT; 108 | const redeemInscriptionData = Object.assign(Object.assign({}, inscriptionData), { ordinalsAddress: "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", ordinalsPublicKey: "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d", paymentAddress: "tb1p0sd5xq6sz0eg3r9j5df0qk38pgnuqreav2qqtq5jfvwpk3yhzuxqjyttjy", paymentPublicKey: "cde4d7fa3f66b13c61279a3a78fd3623428bc69d7e65c770a0fdfd6ea3b0758d" }); 109 | const inscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, redeemInscriptionData, tapScript, sentUtxo); 110 | const sendUTXOSize = inscriptionTxData.virtualSize() * inscriptionData.feeRate + 111 | inscriptionData.delegateIds.length * inscriptionData.padding; 112 | const userUtxo = { 113 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 114 | vout: 0, 115 | value: 10 * Math.pow(10, 8), 116 | }; 117 | const tapleafTxData = yield (0, TapLeafPsbtCreate_1.default)(contentType, inscriptionData, tapScript, userUtxo, sendUTXOSize); 118 | const totalFee = tapleafTxData.virtualSize() * inscriptionData.feeRate + sendUTXOSize; 119 | const total = inscriptionData.padding * inscriptionData.delegateIds.length + 120 | totalFee + 121 | (0, math_1.toInteger)(totalFee / 50) + 122 | (0, math_1.toInteger)(totalFee / 20); 123 | return res.status(200).send({ 124 | satsInItem: inscriptionData.padding * inscriptionData.delegateIds.length, 125 | fee: totalFee, 126 | serviceFee: (0, math_1.toInteger)(totalFee / 50), 127 | feeBySize: (0, math_1.toInteger)(totalFee / 20), 128 | total: total, 129 | }); 130 | } 131 | catch (error) { 132 | console.log(error); 133 | return res.status(400).send({ error }); 134 | } 135 | }); 136 | exports.DelegateEstimateFeeController = DelegateEstimateFeeController; 137 | -------------------------------------------------------------------------------- /dist/src/controller/inscribe.controller.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 26 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 27 | return new (P || (P = Promise))(function (resolve, reject) { 28 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 29 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 30 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 31 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 32 | }); 33 | }; 34 | var __importDefault = (this && this.__importDefault) || function (mod) { 35 | return (mod && mod.__esModule) ? mod : { "default": mod }; 36 | }; 37 | Object.defineProperty(exports, "__esModule", { value: true }); 38 | exports.SendingOrdinalController = exports.DelegateInscribeController = exports.FileInscribeController = exports.TextInscribeController = void 0; 39 | const fileTapScript_1 = require("../services/tapscript/fileTapScript"); 40 | const textTapScript_1 = require("../services/tapscript/textTapScript"); 41 | const inscriptionPsbt_1 = require("../services/psbt/inscriptionPsbt"); 42 | const network_config_1 = __importStar(require("../config/network.config")); 43 | const delegateTapScript_1 = require("../services/tapscript/delegateTapScript"); 44 | const TapLeafPsbtCreate_1 = __importDefault(require("../services/psbt/TapLeafPsbtCreate")); 45 | const sendOrdinalPsbt_1 = require("../services/psbt/sendOrdinalPsbt"); 46 | const mempool_1 = require("../utils/mempool"); 47 | const unisat_api_1 = require("../utils/unisat.api"); 48 | const TextInscribeController = (inscriptionData, res) => __awaiter(void 0, void 0, void 0, function* () { 49 | try { 50 | const tapScript = yield (0, textTapScript_1.textTapScript)(inscriptionData); 51 | const sentUtxo = { 52 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 53 | vout: 0, 54 | value: 1000000, 55 | }; 56 | const contentType = network_config_1.TEXT_CONTENT; 57 | const inscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, inscriptionData, tapScript, sentUtxo); 58 | const sendUTXOSize = inscriptionTxData.virtualSize() * inscriptionData.feeRate + 59 | inscriptionData.contents.length * inscriptionData.padding; 60 | const userUtxo = { 61 | txid: inscriptionData.sendBtcTxId, 62 | vout: inscriptionData.txIndex, 63 | value: inscriptionData.btcAmount, 64 | }; 65 | console.log("Starting Waiting 5s!"); 66 | yield (0, unisat_api_1.delay)(5000); 67 | const tapleafTxData = yield (0, TapLeafPsbtCreate_1.default)(contentType, inscriptionData, tapScript, userUtxo, sendUTXOSize); 68 | const txid = yield (0, mempool_1.pushBTCpmt)(tapleafTxData.toHex(), network_config_1.default.networkType); 69 | const sendingUtxo = { 70 | txid: txid, 71 | vout: 0, 72 | value: sendUTXOSize, 73 | }; 74 | console.log("Sent Utxo for inscribing => ", sendingUtxo); 75 | const realInscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, inscriptionData, tapScript, sendingUtxo); 76 | const realInscriptiontxId = yield (0, mempool_1.pushBTCpmt)(realInscriptionTxData.toHex(), network_config_1.default.networkType); 77 | console.log("Successfully Inscribed Tx Id => ", realInscriptiontxId); 78 | return res.status(200).send({ 79 | tx: realInscriptiontxId, 80 | }); 81 | } 82 | catch (error) { 83 | console.log(error); 84 | return res.status(400).send({ error }); 85 | } 86 | }); 87 | exports.TextInscribeController = TextInscribeController; 88 | const FileInscribeController = (inscriptionData, res) => __awaiter(void 0, void 0, void 0, function* () { 89 | try { 90 | const tapScript = yield (0, fileTapScript_1.fileTapScript)(inscriptionData); 91 | const sentUtxo = { 92 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 93 | vout: 0, 94 | value: 1000000, 95 | }; 96 | const contentType = network_config_1.FILE_CONTENT; 97 | const inscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, inscriptionData, tapScript, sentUtxo); 98 | const sendUTXOSize = inscriptionTxData.virtualSize() * inscriptionData.feeRate + 99 | inscriptionData.files.length * inscriptionData.padding; 100 | const userUtxo = { 101 | txid: inscriptionData.sendBtcTxId, 102 | vout: inscriptionData.txIndex, 103 | value: +inscriptionData.btcAmount, 104 | }; 105 | console.log("Starting Waiting 5s!"); 106 | yield (0, unisat_api_1.delay)(5000); 107 | const tapleafTxData = yield (0, TapLeafPsbtCreate_1.default)(contentType, inscriptionData, tapScript, userUtxo, sendUTXOSize); 108 | const txid = yield (0, mempool_1.pushBTCpmt)(tapleafTxData.toHex(), network_config_1.default.networkType); 109 | const sendingUtxo = { 110 | txid: txid, 111 | vout: 0, 112 | value: sendUTXOSize, 113 | }; 114 | console.log("here5"); 115 | console.log("Sent Utxo for inscribing => ", sendingUtxo); 116 | const realInscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, inscriptionData, tapScript, sendingUtxo); 117 | const realInscriptiontxId = yield (0, mempool_1.pushBTCpmt)(realInscriptionTxData.toHex(), network_config_1.default.networkType); 118 | console.log("Successfully Inscribed Tx Id => ", realInscriptiontxId); 119 | return res.status(200).send({ 120 | tx: realInscriptiontxId, 121 | }); 122 | } 123 | catch (error) { 124 | console.log(error); 125 | return res.status(400).send({ error }); 126 | } 127 | }); 128 | exports.FileInscribeController = FileInscribeController; 129 | const DelegateInscribeController = (inscriptionData, res) => __awaiter(void 0, void 0, void 0, function* () { 130 | try { 131 | const tapScript = yield (0, delegateTapScript_1.delegateTapScript)(inscriptionData); 132 | const sentUtxo = { 133 | txid: "7402984dae838f6700b561f425aacac82b91bc5924fb853631af65f0431cc76a", 134 | vout: 0, 135 | value: 1000000, 136 | }; 137 | const contentType = network_config_1.DELEGATE_CONTENT; 138 | const inscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, inscriptionData, tapScript, sentUtxo); 139 | const sendUTXOSize = inscriptionTxData.virtualSize() * inscriptionData.feeRate + 140 | inscriptionData.delegateIds.length * inscriptionData.padding; 141 | const userUtxo = { 142 | txid: inscriptionData.sendBtcTxId, 143 | vout: inscriptionData.txIndex, 144 | value: inscriptionData.btcAmount, 145 | }; 146 | console.log("Starting Waiting 5s!"); 147 | yield (0, unisat_api_1.delay)(5000); 148 | const tapleafTxData = yield (0, TapLeafPsbtCreate_1.default)(contentType, inscriptionData, tapScript, userUtxo, sendUTXOSize); 149 | const txid = yield (0, mempool_1.pushBTCpmt)(tapleafTxData.toHex(), network_config_1.default.networkType); 150 | const sendingUtxo = { 151 | txid: txid, 152 | vout: 0, 153 | value: sendUTXOSize, 154 | }; 155 | console.log("Sent Utxo for inscribing => ", sendingUtxo); 156 | const realInscriptionTxData = yield (0, inscriptionPsbt_1.inscriptionPsbt)(contentType, inscriptionData, tapScript, sendingUtxo); 157 | const realInscriptiontxId = yield (0, mempool_1.pushBTCpmt)(realInscriptionTxData.toHex(), network_config_1.default.networkType); 158 | console.log("Successfully Inscribed Tx Id => ", realInscriptiontxId); 159 | return res.status(200).send({ 160 | tx: realInscriptiontxId, 161 | }); 162 | } 163 | catch (error) { 164 | console.log(error); 165 | return res.status(400).send({ error }); 166 | } 167 | }); 168 | exports.DelegateInscribeController = DelegateInscribeController; 169 | const SendingOrdinalController = (sendingOrdinalData, res) => __awaiter(void 0, void 0, void 0, function* () { 170 | try { 171 | const response = yield (0, sendOrdinalPsbt_1.sendOrdinalBTCPsbt)(sendingOrdinalData); 172 | if (!response.isSuccess) { 173 | return res.status(400).send({ data: response.data }); 174 | } 175 | else { 176 | console.log(response.data.toHex()); 177 | return res.status(200).send({ data: response.data.toHex() }); 178 | } 179 | } 180 | catch (error) { 181 | return res.status(400).send({ error }); 182 | } 183 | }); 184 | exports.SendingOrdinalController = SendingOrdinalController; 185 | --------------------------------------------------------------------------------