From 8e84fc2678f65833d96f0678b9498176b7865222 Mon Sep 17 00:00:00 2001 From: Nishant Ghodke <64554492+iamcrazycoder@users.noreply.github.com> Date: Tue, 3 Oct 2023 08:53:21 +0530 Subject: [PATCH] feat(sdk)!: upgrade JSON RPC source to Trinity (#68) * feat: add abstract class to define datasource base * feat: add JSON RPC datasource class * refactor: replace OrditApi w/ JsonRpc datasource * refactor: move datasource to very top in inheritance chain * feat: decouple utils to separate class * refactor!: rename class to BaseDatasource and convert to abtract class * refactor: integrate trinity * refactor!: remove inscription & ordinals from UTXO * refactor: update UTXO props * feat: add getInscriptionUTXO RPC call * refactor(sdk): improvements / clean up / remove deprecated fns (#69) * feat: add abstract class to define datasource base * refactor: replace OrditApi w/ JsonRpc datasource * refasctor: move datasource to very top in inheritance chain * feat: decouple utils to separate class * refactor!: rename class to BaseDatasource and convert to abtract class * chore: remove OrditApi * chore: remove instant-buy & tx fns * refactor: rename types * chore!: remove deprecated fns * feat: spread args for clarity of options on usage --- packages/sdk/src/api/index.ts | 224 ----------- packages/sdk/src/api/types.ts | 26 +- packages/sdk/src/config/index.ts | 6 +- packages/sdk/src/index.ts | 3 - packages/sdk/src/inscription/collection.ts | 2 +- packages/sdk/src/inscription/commit.ts | 49 --- packages/sdk/src/inscription/index.ts | 5 +- packages/sdk/src/inscription/instant-buy.ts | 369 ------------------ packages/sdk/src/inscription/psbt.ts | 113 ------ .../src/instant-trade/InstantTradeBuilder.ts | 2 +- .../InstantTradeBuyerTxBuilder.ts | 2 +- .../InstantTradeSellerTxBuilder.ts | 2 +- packages/sdk/src/modules/BaseDatasource.ts | 35 +- packages/sdk/src/modules/DatasourceUtility.ts | 13 +- packages/sdk/src/modules/JsonRpcDatasource.ts | 97 +++-- packages/sdk/src/modules/types.ts | 5 + packages/sdk/src/transactions/Inscriber.ts | 10 - packages/sdk/src/transactions/index.ts | 1 - packages/sdk/src/transactions/psbt.ts | 2 +- packages/sdk/src/transactions/relay.ts | 6 - packages/sdk/src/transactions/types.ts | 10 +- packages/sdk/src/utils/index.ts | 4 - packages/sdk/src/wallet/Ordit.ts | 56 --- packages/sdk/src/wallet/index.ts | 149 ------- 24 files changed, 105 insertions(+), 1086 deletions(-) delete mode 100644 packages/sdk/src/api/index.ts delete mode 100644 packages/sdk/src/inscription/commit.ts delete mode 100644 packages/sdk/src/inscription/instant-buy.ts delete mode 100644 packages/sdk/src/inscription/psbt.ts create mode 100644 packages/sdk/src/modules/types.ts delete mode 100644 packages/sdk/src/transactions/relay.ts diff --git a/packages/sdk/src/api/index.ts b/packages/sdk/src/api/index.ts deleted file mode 100644 index 202bc5c2..00000000 --- a/packages/sdk/src/api/index.ts +++ /dev/null @@ -1,224 +0,0 @@ -import * as bitcoin from "bitcoinjs-lib" - -import { Inscription } from "../inscription/types" -import { Transaction, UTXO, UTXOLimited } from "../transactions/types" -import { decodeObject } from "../utils" -import { rpc } from "./jsonrpc" -import { - FetchInscriptionOptions, - FetchSpendablesOptions, - FetchTxOptions, - FetchTxResponse, - FetchUnspentUTXOsOptions, - FetchUnspentUTXOsResponse, - GetBalanceOptions, - GetInscriptionsOptions, - RelayTxOptions -} from "./types" - -/** - * @deprecated `OrditApi` has been deprecated and will be removed in future release. Use `JsonRpcDatasource` instead - */ -export class OrditApi { - static transformInscriptions(inscriptions: Inscription[] | undefined) { - if (!inscriptions) return [] - - return inscriptions.map((inscription) => { - inscription.meta = inscription.meta ? decodeObject(inscription.meta) : inscription.meta - return inscription - }) - } - - static async fetchUnspentUTXOs({ - address, - network = "testnet", - type = "spendable", - rarity = ["common"], - decodeMetadata = false, - sort = "desc" - }: FetchUnspentUTXOsOptions): Promise { - if (!address) { - throw new Error("Invalid address") - } - - const utxos = await rpc[network].call( - "GetUnspents", - { - address, - options: { - allowedrarity: rarity, - safetospend: type === "spendable" - }, - pagination: { - limit: 50 - }, - sort: { value: sort } - }, - rpc.id - ) - - const { spendableUTXOs, unspendableUTXOs } = utxos.reduce( - (acc, utxo) => { - if (utxo.inscriptions?.length && !utxo.safeToSpend) { - utxo.inscriptions = decodeMetadata ? this.transformInscriptions(utxo.inscriptions) : utxo.inscriptions - - acc.unspendableUTXOs.push(utxo) - } else { - acc.spendableUTXOs.push(utxo) - } - - return acc - }, - { - spendableUTXOs: [], - unspendableUTXOs: [] - } as Record - ) - - return { - totalUTXOs: utxos.length, - spendableUTXOs, - unspendableUTXOs - } - } - - static async fetchTx({ - txId, - network = "testnet", - ordinals = true, - hex = false, - witness = true, - decodeMetadata = false - }: FetchTxOptions): Promise { - if (!txId) { - throw new Error("Invalid txId") - } - - const tx = await rpc[network].call( - "GetTransaction", - { - txid: txId, - options: { - ord: ordinals, - hex, - witness - } - }, - rpc.id - ) - - tx.vout = tx.vout.map((vout) => { - vout.inscriptions = decodeMetadata ? this.transformInscriptions(vout.inscriptions) : vout.inscriptions - return vout - }) - - return { - tx, - rawTx: hex && tx.hex ? bitcoin.Transaction.fromHex(tx.hex) : undefined - } - } - - static async fetchInscriptions({ outpoint, network = "testnet", decodeMetadata = false }: GetInscriptionsOptions) { - if (!outpoint) { - throw new Error("Invalid options provided.") - } - - let inscriptions = await rpc[network].call( - "GetInscriptions", - { - outpoint, - network - }, - rpc.id - ) - - if (decodeMetadata) { - inscriptions = this.transformInscriptions(inscriptions) - } - - return inscriptions - } - - static async fetchInscription({ id, network = "testnet", decodeMetadata = false }: FetchInscriptionOptions) { - if (!id) { - throw new Error("Invalid options provided.") - } - - let inscription = await rpc[network].call( - "GetInscription", - { - id, - network - }, - rpc.id - ) - - if (decodeMetadata) { - inscription = this.transformInscriptions([inscription])[0] - } - - return inscription - } - - static async fetchSpendables({ - address, - value, - rarity = ["common"], - filter = [], - limit = 200, - network = "testnet", - type = "spendable" - }: FetchSpendablesOptions) { - if (!address || !value) { - throw new Error("Invalid options provided") - } - - return rpc[network].call( - "GetSpendables", - { - address, - value, - safetospend: type === "spendable", - allowedrarity: rarity, - filter, - limit - }, - rpc.id - ) - } - - static async relayTx({ hex, network = "testnet", maxFeeRate }: RelayTxOptions): Promise { - if (!hex) { - throw new Error("Invalid tx hex") - } - - if (maxFeeRate && (maxFeeRate < 0 || isNaN(maxFeeRate))) { - throw new Error("Invalid max fee rate") - } - - return rpc[network].call( - "SendRawTransaction", - { - hex, - maxFeeRate - }, - rpc.id - ) - } - - static async getBalance({ address, network = "testnet" }: GetBalanceOptions) { - if (!address) { - throw new Error("Invalid request") - } - - const balance = await rpc[network].call( - "GetBalance", - { - address - }, - rpc.id - ) - - return balance - } -} diff --git a/packages/sdk/src/api/types.ts b/packages/sdk/src/api/types.ts index 8d305ee4..b0d15681 100644 --- a/packages/sdk/src/api/types.ts +++ b/packages/sdk/src/api/types.ts @@ -1,30 +1,26 @@ import { Transaction as BTCTransaction } from "bitcoinjs-lib" -import { Network } from "../config/types" import { Rarity } from "../inscription/types" import { Transaction, UTXO } from "../transactions/types" import { RequireAtLeastOne } from "../utils/types" -export interface FetchUnspentUTXOsOptions { +export interface GetUnspentsOptions { address: string - network?: Network type?: "all" | "spendable" rarity?: Rarity[] - decodeMetadata?: boolean sort?: "asc" | "desc" limit?: number next?: string | null } -export interface FetchUnspentUTXOsResponse { +export interface GetUnspentsResponse { totalUTXOs: number spendableUTXOs: UTXO[] unspendableUTXOs: UTXO[] } -export interface FetchTxOptions { +export interface GetTxOptions { txId: string - network?: Network ordinals?: boolean hex?: boolean witness?: boolean @@ -36,6 +32,10 @@ export interface FetchTxResponse { rawTx?: BTCTransaction } +export interface GetInscriptionUTXOOptions { + id: string +} + export type GetInscriptionsOptions = RequireAtLeastOne<{ creator?: string owner?: string @@ -47,32 +47,28 @@ export type GetInscriptionsOptions = RequireAtLeastOne<{ limit?: number next?: string | null decodeMetadata?: boolean - network?: Network } -export interface FetchInscriptionOptions { +export interface GetInscriptionOptions { id: string - network?: Network decodeMetadata?: boolean } -export interface RelayTxOptions { +export interface RelayOptions { hex: string maxFeeRate?: number - network?: Network + validate?: boolean } -export interface FetchSpendablesOptions { +export interface GetSpendablesOptions { address: string value: number type?: "all" | "spendable" rarity?: Rarity[] filter?: string[] limit?: number - network?: Network } export interface GetBalanceOptions { address: string - network?: Network } diff --git a/packages/sdk/src/config/index.ts b/packages/sdk/src/config/index.ts index 6ebe5dc2..b4c2de40 100644 --- a/packages/sdk/src/config/index.ts +++ b/packages/sdk/src/config/index.ts @@ -2,17 +2,17 @@ export const apiConfig = { version: "0.0.0.10", apis: { mainnet: { - batter: "https://proto.ordit.io/", + batter: "https://mainnet.ordit.io/", orderbook: "1H4vvBnr62YWQmvNSt8Z4pDw3Vcv1n5xz7", ipfs: "http://ipfs-gateway.ordit.io/" }, regtest: { - batter: "https://regtest-v2.ordit.io/", + batter: "https://regtest.ordit.io/", orderbook: "bcrt1q2ys7qws8g072dqe3psp92pqz93ac6wmztexkh5", ipfs: "http://ipfs-gateway.ordit.io/" }, testnet: { - batter: "https://testnet-v2.ordit.io/", + batter: "https://testnet.ordit.io/", orderbook: "tb1qfnw26753j7kqu3q099sd48htvtk5wm4e0enmru", ipfs: "http://ipfs-gateway.ordit.io/" } diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 5890d09e..6f59e1f1 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -1,5 +1,4 @@ import * as addresses from "./addresses" -import * as api from "./api" import * as metamask from "./browser-wallets/metamask" import * as unisat from "./browser-wallets/unisat" import * as xverse from "./browser-wallets/xverse" @@ -13,7 +12,6 @@ import * as wallet from "./wallet" export const ordit = { config, - api, addresses, wallet, keys, @@ -27,7 +25,6 @@ export const ordit = { } export * from "./addresses" -export * from "./api" export * as metamask from "./browser-wallets/metamask" export * as unisat from "./browser-wallets/unisat" export * as xverse from "./browser-wallets/xverse" diff --git a/packages/sdk/src/inscription/collection.ts b/packages/sdk/src/inscription/collection.ts index 3399a9ac..ddf44d44 100644 --- a/packages/sdk/src/inscription/collection.ts +++ b/packages/sdk/src/inscription/collection.ts @@ -63,7 +63,7 @@ export async function mintFromCollection(options: MintFromCollectionOptions) { throw new Error("Invalid collection outpoint supplied.") } const datasource = options.datasource || new JsonRpcDatasource({ network: options.network }) - const collection = await datasource.getInscription(options.collectionOutpoint) + const collection = await datasource.getInscription({ id: options.collectionOutpoint }) if (!collection) { throw new Error("Invalid collection") } diff --git a/packages/sdk/src/inscription/commit.ts b/packages/sdk/src/inscription/commit.ts deleted file mode 100644 index cd22e3b0..00000000 --- a/packages/sdk/src/inscription/commit.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { getAddresses } from "../addresses" -import { createTransaction, encodeObject } from "../utils" -import { GetWalletOptions } from "../wallet" -import { buildWitnessScript } from "./witness" - -export async function generateCommitAddress(options: GenerateCommitAddressOptions) { - const { satsPerByte = 10, network, pubKey, encodeMetadata = false } = options - const key = (await getAddresses({ pubKey, network, format: "p2tr" }))[0] - const xkey = key.xkey - - if (xkey) { - const witnessScript = buildWitnessScript({ - ...options, - xkey, - meta: options.meta && encodeMetadata ? encodeObject(options.meta) : options.meta - }) - - if (!witnessScript) { - throw new Error("Failed to build witness script.") - } - - const scriptTree = { - output: witnessScript - } - - const p2tr = createTransaction(Buffer.from(xkey, "hex"), "p2tr", options.network, { - scriptTree - }) - - const fees = (80 + 1 * 180) * satsPerByte - const scriptLength = witnessScript.toString("hex").length - const scriptFees = (scriptLength / 10) * satsPerByte + fees - - return { - address: p2tr.address, - xkey, - format: "inscribe", - fees: scriptFees - } - } -} - -export type GenerateCommitAddressOptions = Omit & { - satsPerByte: number - mediaType: string - mediaContent: string - meta: any - encodeMetadata?: boolean -} diff --git a/packages/sdk/src/inscription/index.ts b/packages/sdk/src/inscription/index.ts index 7005502a..fa6cd9dc 100644 --- a/packages/sdk/src/inscription/index.ts +++ b/packages/sdk/src/inscription/index.ts @@ -1,6 +1,3 @@ export * from "./collection" -export * from "./commit" -export * from "./instant-buy" -export * from "./psbt" export * from "./types" -export * from "./witness" \ No newline at end of file +export * from "./witness" diff --git a/packages/sdk/src/inscription/instant-buy.ts b/packages/sdk/src/inscription/instant-buy.ts deleted file mode 100644 index eef80060..00000000 --- a/packages/sdk/src/inscription/instant-buy.ts +++ /dev/null @@ -1,369 +0,0 @@ -import * as bitcoin from "bitcoinjs-lib" - -import { - AddressFormats, - addressNameToType, - AddressTypes, - convertBTCToSatoshis, - generateTxUniqueIdentifier, - getAddressesFromPublicKey, - getNetwork, - InputType, - INSTANT_BUY_SELLER_INPUT_INDEX, - OrditApi, - processInput -} from ".." -import { Network } from "../config/types" -import { MINIMUM_AMOUNT_IN_SATS } from "../constants" -import FeeEstimator from "../fee/FeeEstimator" -import { InputsToSign } from "./types" - -/** - * @deprecated `generateSellerPsbt` has been deprecated and will be removed in future release. Use `InstantTradeSellerTxBuilder` class - */ -export async function generateSellerPsbt({ - inscriptionOutPoint, - price, - receiveAddress, - publicKey, - pubKeyType, - network = "testnet" -}: GenerateSellerInstantBuyPsbtOptions) { - const { - inputs: [input], - outputs: [output] - } = await getSellerInputsOutputs({ - inscriptionOutPoint, - price, - receiveAddress, - publicKey, - pubKeyType, - network - }) - - const format = addressNameToType[pubKeyType] - const { address } = getAddressesFromPublicKey(publicKey, network, format)[0] - const networkObj = getNetwork(network) - const psbt = new bitcoin.Psbt({ network: networkObj }) - - psbt.addInput(input) - psbt.addOutput(output) - - const inputsToSign: InputsToSign = { - address: address!, - signingIndexes: [0], // hardcoding because there will always be one input - sigHash: bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY - } - - return { - hex: psbt.toHex(), - base64: psbt.toBase64(), - inputsToSign - } -} - -/** - * @deprecated `generateBuyerPsbt` has been deprecated and will be removed in future release. Use `InstantTradeBuyerTxBuilder` class - */ -export async function generateBuyerPsbt({ - publicKey, - pubKeyType, - feeRate = 10, - network = "testnet", - sellerPsbt, - inscriptionOutPoint, - inscriptionDestinationAddress -}: GenerateBuyerInstantBuyPsbtOptions) { - const networkObj = getNetwork(network) - const format = addressNameToType[pubKeyType] - const address = getAddressesFromPublicKey(publicKey, network, format)[0] - let ordOutNumber = 0 - // get postage from outpoint - - const [ordTxId, ordOut] = inscriptionOutPoint.split(":") - if (!ordTxId || !ordOut) { - throw new Error("Invalid outpoint.") - } - - ordOutNumber = parseInt(ordOut) - const { tx } = await OrditApi.fetchTx({ txId: ordTxId, network }) - if (!tx) { - throw new Error("Failed to get raw transaction for id: " + ordTxId) - } - - const output = tx && tx.vout[ordOutNumber] - - if (!output) { - throw new Error("Outpoint not found.") - } - - const postage = convertBTCToSatoshis(output.value) - const utxos = ( - await OrditApi.fetchUnspentUTXOs({ - address: address.address!, - network, - sort: "asc" // sort by ascending order to use low amount utxos as refundable utxos - }) - ).spendableUTXOs.filter((utxo) => utxo.sats >= MINIMUM_AMOUNT_IN_SATS) - - // 3 = 2 refundables + 1 to cover for purchase - if (utxos.length < 3) { - throw new Error("No suitable UTXOs found") - } - - const psbt = new bitcoin.Psbt({ network: networkObj }) - let totalInput = postage - const usedUTXOTxIds: string[] = [] - const refundableUTXOs = [utxos[0]].concat(utxos[1]) - for (let i = 0; i < refundableUTXOs.length; i++) { - const refundableUTXO = refundableUTXOs[i] - if (usedUTXOTxIds.includes(generateTxUniqueIdentifier(refundableUTXO.txid, refundableUTXO.n))) continue - - const input = await processInput({ utxo: refundableUTXO, pubKey: publicKey, network }) - - usedUTXOTxIds.push(generateTxUniqueIdentifier(input.hash, input.index)) - psbt.addInput(input) - totalInput += refundableUTXO.sats - } - - // Add refundable output - psbt.addOutput({ - address: address.address!, - value: refundableUTXOs[0].sats + refundableUTXOs[1].sats - }) - - // Add ordinal output - psbt.addOutput({ - address: inscriptionDestinationAddress || address.address!, - value: postage - }) - - // seller psbt merge - - const decodedSellerPsbt = bitcoin.Psbt.fromHex(sellerPsbt, { network: networkObj }) - const sellPrice = (decodedSellerPsbt.data.globalMap.unsignedTx as any).tx.outs[0].value - postage - - // inputs - ;(psbt.data.globalMap.unsignedTx as any).tx.ins[INSTANT_BUY_SELLER_INPUT_INDEX] = ( - decodedSellerPsbt.data.globalMap.unsignedTx as any - ).tx.ins[0] - psbt.data.inputs[INSTANT_BUY_SELLER_INPUT_INDEX] = decodedSellerPsbt.data.inputs[0] - // outputs - ;(psbt.data.globalMap.unsignedTx as any).tx.outs[INSTANT_BUY_SELLER_INPUT_INDEX] = ( - decodedSellerPsbt.data.globalMap.unsignedTx as any - ).tx.outs[0] - psbt.data.outputs[INSTANT_BUY_SELLER_INPUT_INDEX] = decodedSellerPsbt.data.outputs[0] - - for (let i = 0; i < utxos.length; i++) { - const utxo = utxos[i] - if (usedUTXOTxIds.includes(generateTxUniqueIdentifier(utxo.txid, utxo.n))) continue - - const input = await processInput({ utxo, pubKey: publicKey, network }) - usedUTXOTxIds.push(generateTxUniqueIdentifier(input.hash, input.index)) - - psbt.addInput(input) - totalInput += utxo.sats - } - - const feeEstimator = new FeeEstimator({ - psbt, - feeRate, - network - }) - const fee = feeEstimator.calculateNetworkFee() - - const totalOutput = psbt.txOutputs.reduce((partialSum, a) => partialSum + a.value, 0) - - const changeValue = totalInput - totalOutput - fee - if (changeValue < 0) { - throw new Error("Insufficient funds to buy this inscription") - } - - if (changeValue > MINIMUM_AMOUNT_IN_SATS) { - psbt.addOutput({ - address: address.address!, - value: changeValue - }) - } - - const inputsToSign = psbt.txInputs.reduce( - (acc, _, index) => { - if (index !== INSTANT_BUY_SELLER_INPUT_INDEX) { - acc.signingIndexes = acc.signingIndexes.concat(index) - } - - return acc - }, - { - address: address.address!, - signingIndexes: [] - } as InputsToSign - ) - - return { - hex: psbt.toHex(), - base64: psbt.toBase64(), - fee, - postage, - sellPrice, - inputsToSign - } -} - -/** - * @deprecated `generateRefundableUTXOs` has been deprecated and will be removed in future release. Use `InstantTradeBuyerTxBuilder.splitUTXOsForTrade` - */ -export async function generateRefundableUTXOs({ - publicKey, - pubKeyType, - feeRate, - destinationAddress, - network = "testnet", - enableRBF = true -}: GenerateRefundableUTXOsOptions) { - const networkObj = getNetwork(network) - const format = addressNameToType[pubKeyType] - const address = getAddressesFromPublicKey(publicKey, network, format)[0] - - const { totalUTXOs, spendableUTXOs } = await OrditApi.fetchUnspentUTXOs({ address: address.address!, network }) - if (!totalUTXOs) { - throw new Error("No UTXOs found.") - } - - const utxo = spendableUTXOs.sort((a, b) => b.sats - a.sats)[0] // Largest UTXO - const psbt = new bitcoin.Psbt({ network: networkObj }) - const input = await processInput({ utxo, pubKey: publicKey, network }) - const totalOutputs = 3 - const outputs: { address: string; cardinals: number }[] = [] - - psbt.addInput(input) - - if (enableRBF) { - psbt.setInputSequence(0, 0xfffffffd) // hardcoded index because input is just one - } - - const feeEstimator = new FeeEstimator({ - psbt, - feeRate, - network - }) - const fee = feeEstimator.calculateNetworkFee() - - const remainingSats = utxo.sats - fee - for (let i = 0; i < totalOutputs; i++) { - const usedAmount = outputs.reduce((acc, curr) => (acc += curr.cardinals), 0) - const remainingAmount = remainingSats - usedAmount - const amount = [0, 1].includes(i) ? MINIMUM_AMOUNT_IN_SATS : remainingAmount - - if (amount < MINIMUM_AMOUNT_IN_SATS) { - throw new Error( - `Not enough sats to generate ${totalOutputs} UTXOs with at least ${MINIMUM_AMOUNT_IN_SATS} sats per UTXO. Try decreasing the count or deposit more BTC` - ) - } - - outputs.push({ - address: destinationAddress || address.address!, - cardinals: amount - }) - } - - outputs.forEach((output) => { - psbt.addOutput({ - address: output.address, - value: output.cardinals - }) - }) - - const inputsToSign: InputsToSign = { - address: address.address!, - signingIndexes: [0] // hardcoding because there will always be one input - } - - return { - hex: psbt.toHex(), - base64: psbt.toBase64(), - inputsToSign - } -} - -export async function getSellerInputsOutputs({ - inscriptionOutPoint, - price, - receiveAddress, - publicKey, - pubKeyType = "taproot", - network = "testnet" -}: GenerateSellerInstantBuyPsbtOptions) { - const format = addressNameToType[pubKeyType] - const address = getAddressesFromPublicKey(publicKey, network, format)[0] - const inputs: InputType[] = [] - const outputs: { address: string; value: number }[] = [] - - const { totalUTXOs, unspendableUTXOs } = await OrditApi.fetchUnspentUTXOs({ - address: address.address!, - network, - type: "all" - }) - if (!totalUTXOs) { - throw new Error("No UTXOs found") - } - - const utxo = unspendableUTXOs.find((utxo) => utxo.inscriptions?.find((i) => i.outpoint === inscriptionOutPoint)) - if (!utxo) { - throw new Error("Inscription not found") - } - - const input = await processInput({ - utxo, - pubKey: publicKey, - network, - sighashType: bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY - }) - - inputs.push(input) - outputs.push({ address: receiveAddress, value: price + utxo.sats }) - - return { inputs, outputs } -} - -export interface UnspentOutput { - txId: string - outputIndex: number - satoshis: number - scriptPk: string - addressType: AddressTypes - address: string - ords: { - id: string - offset: number - }[] -} - -export interface GenerateSellerInstantBuyPsbtOptions { - inscriptionOutPoint: string - price: number - receiveAddress: string - publicKey: string - pubKeyType: AddressFormats - network?: Network -} - -export interface GenerateBuyerInstantBuyPsbtOptions { - publicKey: string - pubKeyType: AddressFormats - network?: Network - feeRate?: number - inscriptionOutPoint: string - sellerPsbt: string - inscriptionDestinationAddress?: string -} - -export interface GenerateRefundableUTXOsOptions { - count?: number - publicKey: string - pubKeyType: AddressFormats - destinationAddress?: string - network?: Network - feeRate: number - enableRBF?: boolean -} diff --git a/packages/sdk/src/inscription/psbt.ts b/packages/sdk/src/inscription/psbt.ts deleted file mode 100644 index dd6f3cca..00000000 --- a/packages/sdk/src/inscription/psbt.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Psbt } from "bitcoinjs-lib" - -import { getAddresses } from "../addresses" -import { OrditApi } from "../api" -import { MINIMUM_AMOUNT_IN_SATS } from "../constants" -import { convertSatoshisToBTC, createTransaction, encodeObject, getNetwork } from "../utils" -import { GetWalletOptions } from "../wallet" -import { buildWitnessScript } from "./witness" - -export async function createRevealPsbt(options: CreateRevealPsbtOptions) { - const networkObj = getNetwork(options.network) - const key = (await getAddresses({ ...options, format: "p2tr" }))[0] - const xkey = key.xkey - const { encodeMetadata = false } = options - - options.safeMode = !options.safeMode ? "on" : options.safeMode - - if (!xkey) { - throw new Error("Failed to build createRevealPsbt") - } - - const witnessScript = buildWitnessScript({ - ...options, - xkey, - meta: options.meta && encodeMetadata ? encodeObject(options.meta) : options.meta - }) - - if (!witnessScript) { - throw new Error("Failed to build createRevealPsbt") - } - - const scriptTree = { - output: witnessScript - } - - const redeemScript = { - output: witnessScript, - redeemVersion: 192 - } - - const inscribePayTx = createTransaction(Buffer.from(xkey, "hex"), "p2tr", options.network, { - scriptTree: scriptTree, - redeem: redeemScript - }) - - const feesForWitnessData = options.fees - const totalAmount = options.postage + feesForWitnessData - const utxos = await OrditApi.fetchSpendables({ - address: inscribePayTx.address!, - network: options.network, - value: convertSatoshisToBTC(totalAmount), - type: options.safeMode === "on" ? "spendable" : "all" - }) - - const suitableUTXO = utxos.find((utxo) => utxo.sats === totalAmount) - if (!suitableUTXO) { - throw new Error("No suitable unspent found for reveal") - } - - const fees = options.postage + feesForWitnessData - const change = suitableUTXO.sats - fees - - const psbt = new Psbt({ network: networkObj }) - psbt.addInput({ - hash: suitableUTXO.txid, - index: suitableUTXO.n, - tapInternalKey: Buffer.from(xkey, "hex"), - witnessUtxo: { - script: inscribePayTx.output!, - value: suitableUTXO.sats - }, - tapLeafScript: [ - { - leafVersion: redeemScript.redeemVersion, - script: redeemScript.output, - controlBlock: inscribePayTx.witness![inscribePayTx.witness!.length - 1] - } - ] - }) - - psbt.addOutput({ - address: options.destination, - value: options.postage - }) - - if (change > MINIMUM_AMOUNT_IN_SATS) { - let changeAddress = inscribePayTx.address - if (options.changeAddress) { - changeAddress = options.changeAddress - } - - psbt.addOutput({ - address: changeAddress!, - value: change - }) - } - - return { - hex: psbt.toHex(), - base64: psbt.toBase64() - } -} - -export type CreateRevealPsbtOptions = Omit & { - fees: number - postage: number - mediaType: string - mediaContent: string - destination: string - changeAddress: string - meta: any - encodeMetadata?: boolean -} diff --git a/packages/sdk/src/instant-trade/InstantTradeBuilder.ts b/packages/sdk/src/instant-trade/InstantTradeBuilder.ts index 8ed49dbe..0b8d5e2c 100644 --- a/packages/sdk/src/instant-trade/InstantTradeBuilder.ts +++ b/packages/sdk/src/instant-trade/InstantTradeBuilder.ts @@ -72,7 +72,7 @@ export default class InstantTradeBuilder extends PSBTBuilder { throw new Error("Inscription not found") } - const utxo = await this.datasource.getInscriptionUTXO(this.inscription.genesis) + const utxo = await this.datasource.getInscriptionUTXO({ id: this.inscription.genesis }) if (!utxo) { throw new Error(`Unable to find UTXO: ${this.inscription.outpoint}`) } diff --git a/packages/sdk/src/instant-trade/InstantTradeBuyerTxBuilder.ts b/packages/sdk/src/instant-trade/InstantTradeBuyerTxBuilder.ts index d15fe95d..5942d96e 100644 --- a/packages/sdk/src/instant-trade/InstantTradeBuyerTxBuilder.ts +++ b/packages/sdk/src/instant-trade/InstantTradeBuyerTxBuilder.ts @@ -138,7 +138,7 @@ export default class InstantTradeBuyerTxBuilder extends InstantTradeBuilder { if (!inscription) { throw new Error("Inscription no longer available for trade") } - const inscriptionUTXO = await this.datasource.getInscriptionUTXO(inscription.id) + const inscriptionUTXO = await this.datasource.getInscriptionUTXO({ id: inscription.id }) this.postage = inscriptionUTXO.sats const sortedUTXOs = utxos.sort((a, b) => a.sats - b.sats) diff --git a/packages/sdk/src/instant-trade/InstantTradeSellerTxBuilder.ts b/packages/sdk/src/instant-trade/InstantTradeSellerTxBuilder.ts index 658f8d2d..bf32536b 100644 --- a/packages/sdk/src/instant-trade/InstantTradeSellerTxBuilder.ts +++ b/packages/sdk/src/instant-trade/InstantTradeSellerTxBuilder.ts @@ -67,7 +67,7 @@ export default class InstantTradeSellerTxBuilder extends InstantTradeBuilder { return } - const collection = await this.datasource.getInscription(this.inscription.meta.col as string) + const collection = await this.datasource.getInscription({ id: this.inscription.meta.col as string }) const royalty = collection.meta?.royalty if (!royalty || !royalty.address || !royalty.pct) { return diff --git a/packages/sdk/src/modules/BaseDatasource.ts b/packages/sdk/src/modules/BaseDatasource.ts index bf177194..64fac76b 100644 --- a/packages/sdk/src/modules/BaseDatasource.ts +++ b/packages/sdk/src/modules/BaseDatasource.ts @@ -2,15 +2,18 @@ import { Transaction as BTCTransaction } from "bitcoinjs-lib" import { Inscription } from ".." import { - FetchSpendablesOptions, - FetchTxOptions, - FetchUnspentUTXOsOptions, + GetBalanceOptions, + GetInscriptionOptions, GetInscriptionsOptions, - RelayTxOptions + GetInscriptionUTXOOptions, + GetSpendablesOptions, + GetTxOptions, + GetUnspentsOptions, + GetUnspentsResponse, + RelayOptions } from "../api/types" import { Network } from "../config/types" import { Transaction, UTXO, UTXOLimited } from "../transactions/types" -import { DatasourceUtility } from "." interface BaseDatasourceOptions { network: Network @@ -23,11 +26,11 @@ export default abstract class BaseDatasource { this.network = network } - abstract getBalance(address: string): Promise + abstract getBalance({ address }: GetBalanceOptions): Promise - abstract getInscription(id: string, decodeMetadata?: boolean): Promise + abstract getInscription({ id, decodeMetadata }: GetInscriptionOptions): Promise - abstract getInscriptionUTXO(id: string): Promise + abstract getInscriptionUTXO({ id }: GetInscriptionUTXOOptions): Promise abstract getInscriptions({ creator, @@ -41,13 +44,17 @@ export default abstract class BaseDatasource { decodeMetadata }: GetInscriptionsOptions): Promise - abstract getSpendables(args: FetchSpendablesOptions): Promise + abstract getSpendables({ address, value, type, rarity, filter, limit }: GetSpendablesOptions): Promise - abstract getTransaction(args: FetchTxOptions): Promise<{ tx: Transaction; rawTx?: BTCTransaction }> + abstract getTransaction({ + txId, + ordinals, + hex, + witness, + decodeMetadata + }: GetTxOptions): Promise<{ tx: Transaction; rawTx?: BTCTransaction }> - abstract getUnspents( - args: FetchUnspentUTXOsOptions - ): Promise> + abstract getUnspents({ address, type, rarity, sort, limit, next }: GetUnspentsOptions): Promise - abstract relay(args: RelayTxOptions): Promise + abstract relay({ hex, maxFeeRate, validate }: RelayOptions): Promise } diff --git a/packages/sdk/src/modules/DatasourceUtility.ts b/packages/sdk/src/modules/DatasourceUtility.ts index d4067193..0172e352 100644 --- a/packages/sdk/src/modules/DatasourceUtility.ts +++ b/packages/sdk/src/modules/DatasourceUtility.ts @@ -1,9 +1,9 @@ import { decodeObject, Inscription } from ".." +import { GetUnspentsResponse } from "../api/types" import { UTXO } from "../transactions/types" interface SegregateUTXOsBySpendStatusArgOptions { utxos: UTXO[] - decodeMetadata?: boolean } export default class DatasourceUtility { @@ -16,17 +16,10 @@ export default class DatasourceUtility { }) } - static segregateUTXOsBySpendStatus({ utxos, decodeMetadata }: SegregateUTXOsBySpendStatusArgOptions) { + static segregateUTXOsBySpendStatus({ utxos }: SegregateUTXOsBySpendStatusArgOptions): GetUnspentsResponse { const { spendableUTXOs, unspendableUTXOs } = utxos.reduce( (acc, utxo) => { - if (utxo.inscriptions?.length && !utxo.safeToSpend) { - utxo.inscriptions = decodeMetadata ? this.transformInscriptions(utxo.inscriptions) : utxo.inscriptions - - acc.unspendableUTXOs.push(utxo) - } else { - acc.spendableUTXOs.push(utxo) - } - + !utxo.safeToSpend ? acc.unspendableUTXOs.push(utxo) : acc.spendableUTXOs.push(utxo) return acc }, { diff --git a/packages/sdk/src/modules/JsonRpcDatasource.ts b/packages/sdk/src/modules/JsonRpcDatasource.ts index 0ff96888..01f4cd35 100644 --- a/packages/sdk/src/modules/JsonRpcDatasource.ts +++ b/packages/sdk/src/modules/JsonRpcDatasource.ts @@ -3,15 +3,20 @@ import { Transaction as BTCTransaction } from "bitcoinjs-lib" import { Inscription } from ".." import { rpc } from "../api/jsonrpc" import { - FetchSpendablesOptions, - FetchTxOptions, - FetchUnspentUTXOsOptions, + GetBalanceOptions, + GetInscriptionOptions, GetInscriptionsOptions, - RelayTxOptions + GetInscriptionUTXOOptions, + GetSpendablesOptions, + GetTxOptions, + GetUnspentsOptions, + GetUnspentsResponse, + RelayOptions } from "../api/types" import { Network } from "../config/types" import { Transaction, UTXO, UTXOLimited } from "../transactions/types" import { BaseDatasource, DatasourceUtility } from "." +import { JsonRpcPagination } from "./types" interface JsonRpcDatasourceOptions { network: Network @@ -22,22 +27,22 @@ export default class JsonRpcDatasource extends BaseDatasource { super({ network }) } - async getBalance(address: string) { + async getBalance({ address }: GetBalanceOptions) { if (!address) { throw new Error("Invalid request") } - return rpc[this.network].call("GetBalance", { address }, rpc.id) + return rpc[this.network].call("Address.GetBalance", { address }, rpc.id) } - async getInscription(id: string, decodeMetadata = false) { + async getInscription({ id, decodeMetadata }: GetInscriptionOptions) { if (!id) { throw new Error("Invalid request") } id = id.includes(":") ? id.replace(":", "i") : !id.includes("i") ? `${id}i0` : id - let inscription = await rpc[this.network].call("GetInscription", { id }, rpc.id) + let inscription = await rpc[this.network].call("Ordinals.GetInscription", { id }, rpc.id) if (decodeMetadata) { inscription = DatasourceUtility.transformInscriptions([inscription])[0] } @@ -45,7 +50,7 @@ export default class JsonRpcDatasource extends BaseDatasource { return inscription } - async getInscriptionUTXO(id: string) { + async getInscriptionUTXO({ id }: GetInscriptionUTXOOptions) { if (!id) { throw new Error("Invalid request") } @@ -55,33 +60,51 @@ export default class JsonRpcDatasource extends BaseDatasource { return rpc[this.network].call("Ordinals.GetInscriptionUtxo", { id }, rpc.id) } - async getInscriptions({ outpoint, decodeMetadata }: GetInscriptionsOptions) { - const { inscriptions } = await rpc[this.network].call<{ - inscriptions: Inscription[] - pagination: { - limit: number - prev: string | null - next: string | null - } - }>("GetInscriptions", { outpoint }, rpc.id) - + async getInscriptions({ + creator, + owner, + mimeType, + mimeSubType, + outpoint, + decodeMetadata, + sort = "asc", + limit = 25, + next = null + }: GetInscriptionsOptions) { + let inscriptions: Inscription[] = [] + do { + const { inscriptions: _inscriptions, pagination } = await rpc[this.network].call<{ + inscriptions: Inscription[] + pagination: JsonRpcPagination + }>( + "Ordinals.GetInscriptions", + { + filter: { creator, owner, mimeType, mimeSubType, outpoint }, + sort: { number: sort }, + pagination: { limit, next } + }, + rpc.id + ) + inscriptions = inscriptions.concat(_inscriptions) + next = pagination.next + } while (next !== null) return decodeMetadata ? DatasourceUtility.transformInscriptions(inscriptions) : inscriptions } async getSpendables({ - address, // TODO rename interface + address, value, rarity = ["common"], filter = [], limit = 200, type = "spendable" - }: FetchSpendablesOptions) { + }: GetSpendablesOptions) { if (!address || isNaN(value) || !value) { throw new Error("Invalid request") } return rpc[this.network].call( - "GetSpendables", + "Address.GetSpendables", { address, value, @@ -94,19 +117,13 @@ export default class JsonRpcDatasource extends BaseDatasource { ) } - async getTransaction({ - txId, // TODO rename interface - ordinals = true, - hex = false, - witness = true, - decodeMetadata = false - }: FetchTxOptions) { + async getTransaction({ txId, ordinals = true, hex = false, witness = true, decodeMetadata = true }: GetTxOptions) { if (!txId) { throw new Error("Invalid request") } const tx = await rpc[this.network].call( - "GetTransaction", + "Transactions.GetTransaction", { txid: txId, options: { @@ -132,14 +149,13 @@ export default class JsonRpcDatasource extends BaseDatasource { } async getUnspents({ - address, // TODO rename interface + address, type = "spendable", rarity = ["common"], - decodeMetadata = false, sort = "desc", limit = 50, next = null - }: FetchUnspentUTXOsOptions) { + }: GetUnspentsOptions): Promise { if (!address) { throw new Error("Invalid request") } @@ -148,15 +164,10 @@ export default class JsonRpcDatasource extends BaseDatasource { do { const { unspents, pagination } = await rpc[this.network].call<{ unspents: UTXO[] - pagination: { - limit: number - prev: string | null - next: string | null - } + pagination: JsonRpcPagination }>( - "GetUnspents", + "Address.GetUnspents", { - format: "next", address, options: { allowedrarity: rarity, @@ -175,10 +186,10 @@ export default class JsonRpcDatasource extends BaseDatasource { next = pagination.next } while (next !== null) - return DatasourceUtility.segregateUTXOsBySpendStatus({ utxos, decodeMetadata }) + return DatasourceUtility.segregateUTXOsBySpendStatus({ utxos }) } - async relay({ hex, maxFeeRate }: RelayTxOptions) { + async relay({ hex, maxFeeRate, validate = true }: RelayOptions) { if (!hex) { throw new Error("Invalid request") } @@ -187,6 +198,6 @@ export default class JsonRpcDatasource extends BaseDatasource { throw new Error("Invalid max fee rate") } - return rpc[this.network].call("SendRawTransaction", { hex, maxFeeRate }, rpc.id) + return rpc[this.network].call("Transactions.Relay", { hex, maxFeeRate, validate }, rpc.id) } } diff --git a/packages/sdk/src/modules/types.ts b/packages/sdk/src/modules/types.ts new file mode 100644 index 00000000..b4f5b4ef --- /dev/null +++ b/packages/sdk/src/modules/types.ts @@ -0,0 +1,5 @@ +export interface JsonRpcPagination { + limit: number + prev: string | null + next: string | null +} diff --git a/packages/sdk/src/transactions/Inscriber.ts b/packages/sdk/src/transactions/Inscriber.ts index 71d021b2..961f09ae 100644 --- a/packages/sdk/src/transactions/Inscriber.ts +++ b/packages/sdk/src/transactions/Inscriber.ts @@ -277,16 +277,6 @@ export class Inscriber extends PSBTBuilder { } } -/** - * @deprecated `OrdTransaction` class has been renamed to `Inscriber` - */ -export class OrdTransaction extends Inscriber { - constructor(args: InscriberArgOptions) { - super(args) - console.error("DEPRECATION WARNING: 'OrdTransaction' class has been renamed to 'Inscriber'") - } -} - export type InscriberArgOptions = Pick & { network: Network address: string diff --git a/packages/sdk/src/transactions/index.ts b/packages/sdk/src/transactions/index.ts index abf1eadd..fcf8fff2 100644 --- a/packages/sdk/src/transactions/index.ts +++ b/packages/sdk/src/transactions/index.ts @@ -1,3 +1,2 @@ export * from "./Inscriber" export * from "./psbt" -export * from "./relay" diff --git a/packages/sdk/src/transactions/psbt.ts b/packages/sdk/src/transactions/psbt.ts index 8206d84d..9788d14e 100644 --- a/packages/sdk/src/transactions/psbt.ts +++ b/packages/sdk/src/transactions/psbt.ts @@ -135,7 +135,7 @@ async function generateLegacyInput({ pubKey, datasource }: ProcessInputOptions & Required>): Promise { - const { rawTx } = await datasource.getTransaction({ txId: utxo.txid, network, hex: true }) + const { rawTx } = await datasource.getTransaction({ txId: utxo.txid, hex: true }) if (!rawTx) { throw new Error("Unable to process legacy input") } diff --git a/packages/sdk/src/transactions/relay.ts b/packages/sdk/src/transactions/relay.ts deleted file mode 100644 index 957e2d0a..00000000 --- a/packages/sdk/src/transactions/relay.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { OrditApi } from "../api" -import { Network } from "../config/types" - -export async function relayTransaction(hex: string, network: Network, maxFeeRate?: number) { - return OrditApi.relayTx({ hex, network, maxFeeRate }) -} diff --git a/packages/sdk/src/transactions/types.ts b/packages/sdk/src/transactions/types.ts index e022aa83..ca18b07e 100644 --- a/packages/sdk/src/transactions/types.ts +++ b/packages/sdk/src/transactions/types.ts @@ -57,20 +57,14 @@ export interface ScriptPubKey { export interface UTXO { n: number - txHash: string - blockHash: string - blockN: number + txid: string sats: number scriptPubKey: ScriptPubKey - txid: string - value: number - ordinals?: Ordinal[] | null - inscriptions?: Inscription[] | null safeToSpend: boolean confirmation: number } -export type UTXOLimited = Pick +export type UTXOLimited = Pick export interface Output { address: string diff --git a/packages/sdk/src/utils/index.ts b/packages/sdk/src/utils/index.ts index 313c61ab..154d0c59 100644 --- a/packages/sdk/src/utils/index.ts +++ b/packages/sdk/src/utils/index.ts @@ -242,9 +242,6 @@ export function getScriptType(script: Buffer, network: Network): GetScriptTypeRe export function getDummyP2TRInput(): UTXO { return { n: 1, - txHash: "3045867081e53f33a4dbd930bf0c121fe30155c767e98895470a572eefc4b7dd", - blockHash: "0000000000002764723466e6584169a56703591befb5b435ed0bc1197b57a982", - blockN: 2476710, sats: 2885, scriptPubKey: { asm: "1 29dacd26920d003a894d5f7f263877046a618ce2e7408657b24c74c42b7b80f8", @@ -254,7 +251,6 @@ export function getDummyP2TRInput(): UTXO { type: "witness_v1_taproot" }, txid: "3045867081e53f33a4dbd930bf0c121fe30155c767e98895470a572eefc4b7dd", - value: 0.00002885, safeToSpend: true, confirmation: 10 } diff --git a/packages/sdk/src/wallet/Ordit.ts b/packages/sdk/src/wallet/Ordit.ts index 700b3610..91066ad8 100644 --- a/packages/sdk/src/wallet/Ordit.ts +++ b/packages/sdk/src/wallet/Ordit.ts @@ -10,9 +10,6 @@ import { Account, AddressFormats, addressNameToType, - generateBuyerPsbt, - generateRefundableUTXOs, - generateSellerPsbt, getAccountDataFromHdNode, getAddressesFromPublicKey, getAllAccountsFromHdNode, @@ -21,10 +18,7 @@ import { publishCollection, tweakSigner } from ".." -import { OrditApi } from "../api" import { Network } from "../config/types" -import { Inscription } from "../inscription/types" -import { Inscriber, InscriberArgOptions } from "../transactions" bitcoin.initEccLib(ecc) const ECPair = ECPairFactory(ecc) @@ -243,56 +237,6 @@ export class Ordit { return signature.toString("base64") } - async relayTx(hex: string, network?: Network, maxFeeRate?: number) { - return OrditApi.relayTx({ hex, network, maxFeeRate }) - } - - async getInscriptions() { - if (!this.selectedAddress) { - throw new Error("Wallet not fully initialized.") - } - - const { unspendableUTXOs } = await OrditApi.fetchUnspentUTXOs({ - address: this.selectedAddress, - network: this.#network - }) - - return unspendableUTXOs.reduce((acc, curr) => { - if (curr.inscriptions) { - acc.push(...curr.inscriptions) - } - - return acc - }, [] as Inscription[]) - } - - /** - * @deprecated `Ordit.inscription.new` has been deprecated and will be removed in future release. Use `Inscriber` class. - * @deprecated `Ordit.inscription.fetchInscriptions` has been deprecated and will be removed in future release. Use `OrditApi.fetchInscriptions` - */ - static inscription = { - new: (options: InscriberArgOptions) => new Inscriber(options), - fetchInscriptions: (outpoint: string, network: Network = "testnet") => { - if (!outpoint) { - throw new Error("Outpoint is required.") - } - - return OrditApi.fetchInscriptions({ - outpoint, - network - }) - } - } - - /** - * @deprecated `Ordit.instantBuy.*` has been deprecated and will be removed in future release. Use relevant `InstantTrader` sub-class - */ - static instantBuy = { - generateBuyerPsbt, - generateSellerPsbt, - generateRefundableUTXOs - } - static collection = { publish: publishCollection, mint: mintFromCollection diff --git a/packages/sdk/src/wallet/index.ts b/packages/sdk/src/wallet/index.ts index 152d7594..8006dcf6 100644 --- a/packages/sdk/src/wallet/index.ts +++ b/packages/sdk/src/wallet/index.ts @@ -1,119 +1,5 @@ -import { getAddressesFromPublicKey } from "../addresses" import { AddressTypes } from "../addresses/formats" -import { OrditApi } from "../api" import { Network } from "../config/types" -import { Inscription, Ordinal } from "../inscription/types" -import { getWalletKeys } from "../keys" -import { UTXO } from "../transactions/types" - -export async function getWallet({ - pubKey, - network = "testnet", - format = "all" -}: GetWalletOptions): Promise { - const addresses = getAddressesFromPublicKey(pubKey, network, format) - - return { - counts: { - addresses: addresses.length - }, - keys: [{ pub: pubKey }], - addresses - } -} - -export async function getWalletWithBalances({ pubKey, format, network, safeMode = "on" }: GetWalletOptions) { - const wallet = (await getWallet({ pubKey, format, network })) as GetWalletWithBalances - - const ordinals: Ordinal[] = [] - const inscriptions: Inscription[] = [] - const spendables: UTXO[] = [] - const unspendables: UTXO[] = [] - - wallet.counts.unspents = 0 - wallet.counts.satoshis = 0 - wallet.counts.cardinals = 0 - wallet.counts.spendables = 0 - wallet.counts.unspendables = 0 - wallet.counts.ordinals = 0 - wallet.counts.inscriptions = 0 - - const { addresses } = wallet - - for (let i = 0; i < addresses.length; i++) { - const address = addresses[i] - - let wallet_unspents = 0 - let wallet_satoshis = 0 - let wallet_cardinals = 0 - let wallet_spendables = 0 - let wallet_unspendables = 0 - - const { totalUTXOs, spendableUTXOs, unspendableUTXOs } = await OrditApi.fetchUnspentUTXOs({ - address: address.address!, - network, - type: "all" - }) - - address.unspents = spendableUTXOs.concat(unspendableUTXOs) - wallet_unspents += totalUTXOs - - for (let j = 0; j < address.unspents!.length; j++) { - const unspentObj = address.unspents![j] - unspentObj.pub = address.pub - wallet.counts.satoshis += unspentObj.sats - wallet_satoshis += unspentObj.sats - - if (safeMode === "off" || (safeMode === "on" && unspentObj.safeToSpend)) { - wallet.counts.cardinals += unspentObj.sats - wallet_cardinals += unspentObj.sats - - wallet.counts.spendables++ - wallet_spendables++ - spendables.push(unspentObj) - } else { - wallet.counts.unspendables++ - wallet_unspendables++ - - unspendables.push(unspentObj) - } - - const _ordinals = unspentObj.ordinals - const _inscriptions = unspentObj.inscriptions - - _ordinals.forEach((_ord: any, index: number) => { - _ordinals[index].address = address - _ordinals[index].unspent = unspentObj.txid - - ordinals.push(_ord) - }) - - _inscriptions.forEach((_inscription: any, index: number) => { - _inscriptions[index].address = address - _inscriptions[index].unspent = unspentObj.txid - - inscriptions.push(_inscription) - }) - - wallet.spendables = spendables - wallet.unspendables = unspendables - wallet.ordinals = ordinals - wallet.inscriptions = inscriptions - wallet.counts.ordinals = ordinals.length - wallet.counts.inscriptions = inscriptions.length - - address.counts = { - unspents: wallet_unspents, - satoshis: wallet_satoshis, - cardinals: wallet_cardinals, - spendables: wallet_spendables, - unspendables: wallet_unspendables - } - } - } - - return wallet -} export type OnOffUnion = "on" | "off" @@ -123,38 +9,3 @@ export type GetWalletOptions = { format: AddressTypes | "all" safeMode?: OnOffUnion } - -export type GetWalletReturnType = { - counts: { - addresses: number - } - keys: [Partial>>] - addresses: ReturnType -} - -export type GetWalletWithBalances = GetWalletReturnType & { - spendables: UTXO[] - unspendables: UTXO[] - ordinals: Ordinal[] - inscriptions: Inscription[] - - counts: { - unspents: number - satoshis: number - cardinals: number - spendables: number - unspendables: number - ordinals: number - inscriptions: number - } - addresses: Array<{ - unspents: any[] - counts: { - unspents: number - satoshis: number - cardinals: number - spendables: number - unspendables: number - } - }> -}