From c5b54f8cdbd03d95df05dbbfc2a87723df8309c8 Mon Sep 17 00:00:00 2001 From: KevinK <31565174+kevzzsk@users.noreply.github.com> Date: Mon, 10 Jun 2024 14:50:26 +0800 Subject: [PATCH] feat(sdk): add taproot tree version for backward compatibility (#119) * feat(sdk): add taproot tree version for backward compatibility * add strict typing for taptree version * lint * add comments --- packages/sdk/src/inscription/collection.ts | 3 +- packages/sdk/src/transactions/Inscriber.ts | 54 +++++++++++++++++++--- packages/sdk/src/transactions/types.ts | 4 +- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/packages/sdk/src/inscription/collection.ts b/packages/sdk/src/inscription/collection.ts index 06b1d475..5d350c2a 100644 --- a/packages/sdk/src/inscription/collection.ts +++ b/packages/sdk/src/inscription/collection.ts @@ -1,4 +1,4 @@ -import { BaseDatasource, GetWalletOptions, Inscriber, JsonRpcDatasource, verifyMessage } from ".." +import { BaseDatasource, GetWalletOptions, Inscriber, JsonRpcDatasource, TaptreeVersion, verifyMessage } from ".." import { Network } from "../config/types" import { MAXIMUM_ROYALTY_PERCENTAGE } from "../constants" import { OrditSDKError } from "../utils/errors" @@ -171,6 +171,7 @@ export type MintFromCollectionOptions = Pick & { datasource?: BaseDatasource // temporary flag for backward compatibility includeMintAddress?: boolean + taptreeVersion?: TaptreeVersion } type Outputs = Array<{ address: string; value: number }> diff --git a/packages/sdk/src/transactions/Inscriber.ts b/packages/sdk/src/transactions/Inscriber.ts index b94c8fa7..07e27b0c 100644 --- a/packages/sdk/src/transactions/Inscriber.ts +++ b/packages/sdk/src/transactions/Inscriber.ts @@ -1,6 +1,6 @@ import * as ecc from "@bitcoinerlab/secp256k1" import * as bitcoin from "bitcoinjs-lib" -import { Tapleaf } from "bitcoinjs-lib/src/types" +import { Taptree } from "bitcoinjs-lib/src/types" import { BaseDatasource, @@ -10,7 +10,8 @@ import { getDummyP2TRInput, getNetwork, GetWalletOptions, - OnOffUnion + OnOffUnion, + TaptreeVersion } from ".." import { Network } from "../config/types" import { MINIMUM_AMOUNT_IN_SATS } from "../constants" @@ -25,6 +26,7 @@ export class Inscriber extends PSBTBuilder { protected mediaType: string protected mediaContent: string protected meta?: NestedObject + protected taptreeVersion?: TaptreeVersion = "1" protected postage: number private ready = false @@ -38,11 +40,12 @@ export class Inscriber extends PSBTBuilder { private encodeMetadata: boolean private previewMode = false - private witnessScripts: Record<"inscription" | "recovery", Buffer | null> = { + private witnessScripts: Record<"inscription" | "recovery" | "inscriptionOnly", Buffer | null> = { inscription: null, + inscriptionOnly: null, recovery: null } - private taprootTree!: [Tapleaf, Tapleaf] + private taprootTree!: Taptree constructor({ network, @@ -58,6 +61,7 @@ export class Inscriber extends PSBTBuilder { encodeMetadata = false, safeMode, meta, + taptreeVersion, datasource }: InscriberArgOptions) { super({ @@ -78,6 +82,7 @@ export class Inscriber extends PSBTBuilder { this.mediaType = mediaType this.mediaContent = mediaContent this.meta = meta + this.taptreeVersion = taptreeVersion this.postage = postage this.safeMode = !safeMode ? "on" : safeMode this.encodeMetadata = encodeMetadata @@ -161,6 +166,12 @@ export class Inscriber extends PSBTBuilder { meta: this.getMetadata(), xkey: this.xKey }), + inscriptionOnly: buildWitnessScript({ + mediaContent: this.mediaContent, + mediaType: this.mediaType, + meta: false, // do not pass in metadata for v2 + xkey: this.xKey + }), recovery: buildWitnessScript({ mediaContent: this.mediaContent, mediaType: this.mediaType, @@ -173,7 +184,30 @@ export class Inscriber extends PSBTBuilder { buildTaprootTree() { this.buildWitness() - this.taprootTree = [{ output: this.witnessScripts.inscription! }, { output: this.witnessScripts.recovery! }] + switch (this.taptreeVersion) { + case "2": + // v2 allows for inscription only minting (without meta) and remains unique based on the meta (OIP-2 specs) + this.taprootTree = [ + [{ output: this.witnessScripts.recovery! }, { output: this.witnessScripts.inscription! }], + { output: this.witnessScripts.inscriptionOnly! } + ] + break + case "1": + default: + // v1 allows for inscription (with meta) and recovery minting (OIP-2 specs) + this.taprootTree = [{ output: this.witnessScripts.inscription! }, { output: this.witnessScripts.recovery! }] + break + } + } + + getReedemScript(): bitcoin.payments.Payment["redeem"] { + switch (this.taptreeVersion) { + case "2": + return this.getInscriptionOnlyRedeemScript() + case "1": + default: + return this.getInscriptionRedeemScript() + } } getInscriptionRedeemScript(): bitcoin.payments.Payment["redeem"] { @@ -183,6 +217,13 @@ export class Inscriber extends PSBTBuilder { } } + getInscriptionOnlyRedeemScript(): bitcoin.payments.Payment["redeem"] { + return { + output: this.witnessScripts.inscriptionOnly!, + redeemVersion: 192 + } + } + getRecoveryRedeemScript(): bitcoin.payments.Payment["redeem"] { return { output: this.witnessScripts.recovery!, @@ -230,7 +271,7 @@ export class Inscriber extends PSBTBuilder { internalPubkey: Buffer.from(this.xKey, "hex"), network: getNetwork(this.network), scriptTree: this.taprootTree, - redeem: this.getInscriptionRedeemScript() + redeem: this.getReedemScript() }) this.witness = this.payment.witness @@ -315,6 +356,7 @@ export type InscriberArgOptions = Pick & { mediaContent: string changeAddress: string meta?: NestedObject + taptreeVersion?: TaptreeVersion outputs?: Outputs encodeMetadata?: boolean datasource?: BaseDatasource diff --git a/packages/sdk/src/transactions/types.ts b/packages/sdk/src/transactions/types.ts index cc973818..b10bf889 100644 --- a/packages/sdk/src/transactions/types.ts +++ b/packages/sdk/src/transactions/types.ts @@ -51,7 +51,7 @@ export type Transaction = { } // used in Address.GetTransactions RPC, needed due to response not matching Transaction type (ex. blockhash vs blockHash) -export type TransactionV2 = Omit & { +export type TransactionV2 = Omit & { blockHash: string blockHeight: number blockTime: number @@ -95,3 +95,5 @@ export interface SkipStrictSatsCheckOptions { skipStrictSatsCheck?: boolean customAmount?: number } + +export type TaptreeVersion = "1" | "2"