diff --git a/src/constants/dlc-handler.constants.ts b/src/constants/dlc-handler.constants.ts new file mode 100644 index 0000000..7502a28 --- /dev/null +++ b/src/constants/dlc-handler.constants.ts @@ -0,0 +1,7 @@ +export const DLCHandlers = { + KEYPAIR: 'keypair', + LEATHER: 'leather', + UNISAT_FORDEFI: 'unisat/fordefi', + LEDGER: 'ledger', + DFNS: 'dfns', +} as const; diff --git a/src/dlc-handlers/abstract-dlc-handler.ts b/src/dlc-handlers/abstract-dlc-handler.ts index fde7b02..51ed80c 100644 --- a/src/dlc-handlers/abstract-dlc-handler.ts +++ b/src/dlc-handlers/abstract-dlc-handler.ts @@ -19,68 +19,24 @@ import { createWithdrawTransaction, } from '../functions/bitcoin/psbt-functions.js'; import { PaymentInformation } from '../models/bitcoin-models.js'; +import { + DLCHandlerType, + FundingPaymentType, + PaymentType, + TransactionType, +} from '../models/dlc-handler.models.js'; +import { + AddressNotFoundError, + InsufficientFundsError, + InvalidPaymentTypeError, + PaymentNotSetError, +} from '../models/errors/dlc-handler.errors.models.js'; import { RawVault } from '../models/ethereum-models.js'; -export type DLCHandlerType = 'browser' | 'ledger' | 'dfns'; -export type FundingPaymentType = 'wpkh' | 'tr'; -export type PaymentType = 'funding' | 'multisig'; -export type TransactionType = 'funding' | 'deposit' | 'withdraw'; - -export class DLCHandlerError extends Error { - constructor(message: string) { - super(message); - this.name = 'DLCHandlerError'; - } -} - -export class PaymentNotSetError extends DLCHandlerError { - constructor( - message: string = 'Payment information not initialized. Make sure to create payments before attempting to access them.' - ) { - super(message); - this.name = 'PaymentNotSetError'; - } -} - -export class AddressNotFoundError extends DLCHandlerError { - constructor(paymentType: PaymentType) { - super(`Address not found for ${paymentType} payment`); - this.name = 'AddressNotFoundError'; - } -} - -export class InvalidPaymentTypeError extends DLCHandlerError { - constructor(paymentType: PaymentType) { - super(`Invalid payment type: ${paymentType}`); - this.name = 'InvalidPaymentTypeError'; - } -} - -export class InvalidTransactionTypeError extends DLCHandlerError { - constructor(transactionType: TransactionType) { - super(`Invalid transaction type: ${transactionType}`); - this.name = 'InvalidTransactionTypeError'; - } -} - -export class InsufficientFundsError extends DLCHandlerError { - constructor(available: bigint, required: bigint) { - super(`Insufficient funds: have ${available}, need ${required}`); - this.name = 'InsufficientFundsError'; - } -} - -export class IncompatibleTransactionArgument extends DLCHandlerError { - constructor() { - super('Incompatible transaction argument'); - this.name = 'IncompatibleTransactionArgument'; - } -} - export abstract class AbstractDLCHandler { abstract readonly _dlcHandlerType: DLCHandlerType; protected fundingPaymentType: FundingPaymentType; - protected abstract _payment?: PaymentInformation; + protected _payment?: PaymentInformation; protected readonly bitcoinNetwork: Network; protected readonly bitcoinBlockchainAPI: string; protected readonly bitcoinBlockchainFeeRecommendationAPI: string; diff --git a/src/dlc-handlers/dfns-dlc-handler.ts b/src/dlc-handlers/dfns-dlc-handler.ts index b63572e..5538b1f 100644 --- a/src/dlc-handlers/dfns-dlc-handler.ts +++ b/src/dlc-handlers/dfns-dlc-handler.ts @@ -5,45 +5,17 @@ import { bytesToHex, hexToBytes } from '@noble/hashes/utils'; import { Transaction } from '@scure/btc-signer'; import { Network } from 'bitcoinjs-lib'; -import { PaymentInformation } from '../models/bitcoin-models.js'; +import { FundingPaymentType, TransactionType } from '../models/dlc-handler.models.js'; import { - AbstractDLCHandler, - DLCHandlerError, - FundingPaymentType, + DFNSWalletIDNotSetError, PaymentNotSetError, - TransactionType, -} from './abstract-dlc-handler.js'; - -export class DFNSWalletIDNotSetError extends DLCHandlerError { - constructor( - message: string = 'DFNS Wallet ID not set. Make sure to initialize the wallet before attempting to access it.' - ) { - super(message); - this.name = 'DFNSWalletIDNotSetError'; - } -} - -export class TaprootDerivedPublicKeyNotSet extends DLCHandlerError { - constructor( - message: string = 'Taproot Derived Public Key not set. Make sure to initialize the wallet before attempting to access it.' - ) { - super(message); - this.name = 'TaprootDerivedPublicKeyNotSet'; - } -} - -export class SignatureGenerationFailed extends DLCHandlerError { - constructor( - message: string = 'Signature generation failed. Make sure to initialize the wallet before attempting to access it.' - ) { - super(message); - this.name = 'SignatureGenerationFailed'; - } -} + SignatureGenerationFailed, + TaprootDerivedPublicKeyNotSet, +} from '../models/errors/dlc-handler.errors.models.js'; +import { AbstractDLCHandler } from './abstract-dlc-handler.js'; export class DFNSDLCHandler extends AbstractDLCHandler { readonly _dlcHandlerType = 'dfns' as const; - protected _payment?: PaymentInformation; private readonly dfnsDelegatedAPIClient: DfnsDelegatedApiClient; private _taprootDerivedPublicKey?: string; private _dfnsWalletID?: string; @@ -70,6 +42,21 @@ export class DFNSDLCHandler extends AbstractDLCHandler { }); } + set dfnsWalletID(dfnsWalletID: string) { + this._dfnsWalletID = dfnsWalletID; + } + + get dfnsWalletID(): string { + if (!this._dfnsWalletID) { + throw new DFNSWalletIDNotSetError(); + } + return this._dfnsWalletID; + } + + set taprootDerivedPublicKey(taprootDerivedPublicKey: string) { + this._taprootDerivedPublicKey = taprootDerivedPublicKey; + } + async getWallets(): Promise { try { return await this.dfnsDelegatedAPIClient.wallets.listWallets(); @@ -90,27 +77,12 @@ export class DFNSDLCHandler extends AbstractDLCHandler { } } - set dfnsWalletID(dfnsWalletID: string) { - this._dfnsWalletID = dfnsWalletID; - } - - get dfnsWalletID(): string { - if (!this._dfnsWalletID) { - throw new DFNSWalletIDNotSetError(); - } - return this._dfnsWalletID; - } - - set taprootDerivedPublicKey(taprootDerivedPublicKey: string) { - this._taprootDerivedPublicKey = taprootDerivedPublicKey; - } - getUserTaprootPublicKey(tweaked: boolean = false): string { if (!tweaked) { if (!this._taprootDerivedPublicKey) { throw new TaprootDerivedPublicKeyNotSet(); } - return this.taprootDerivedPublicKey; + return this._taprootDerivedPublicKey; } if (!this.payment) { @@ -124,7 +96,7 @@ export class DFNSDLCHandler extends AbstractDLCHandler { if (!this._taprootDerivedPublicKey) { throw new TaprootDerivedPublicKeyNotSet(); } - return this.taprootDerivedPublicKey; + return this._taprootDerivedPublicKey; } async signPSBT(transaction: Transaction, transactionType: TransactionType): Promise { diff --git a/src/dlc-handlers/private-key-dlc-handler.ts b/src/dlc-handlers/keypair-dlc-handler.ts similarity index 90% rename from src/dlc-handlers/private-key-dlc-handler.ts rename to src/dlc-handlers/keypair-dlc-handler.ts index 19c85a8..df71d3f 100644 --- a/src/dlc-handlers/private-key-dlc-handler.ts +++ b/src/dlc-handlers/keypair-dlc-handler.ts @@ -8,19 +8,15 @@ import { deriveUnhardenedKeyPairFromRootPrivateKey, getInputIndicesByScript, } from '../functions/bitcoin/bitcoin-functions.js'; -import { PaymentInformation } from '../models/bitcoin-models.js'; +import { FundingPaymentType, PaymentType, TransactionType } from '../models/dlc-handler.models.js'; import { - AbstractDLCHandler, - FundingPaymentType, InvalidTransactionTypeError, PaymentNotSetError, - PaymentType, - TransactionType, -} from './abstract-dlc-handler.js'; +} from '../models/errors/dlc-handler.errors.models.js'; +import { AbstractDLCHandler } from './abstract-dlc-handler.js'; -export class PrivateKeyDLCHandler extends AbstractDLCHandler { - readonly _dlcHandlerType = 'browser' as const; - protected _payment?: PaymentInformation; +export class KeyPairDLCHandler extends AbstractDLCHandler { + readonly _dlcHandlerType = 'keypair' as const; private fundingDerivedKeyPair: BIP32Interface; private taprootDerivedKeyPair: BIP32Interface; diff --git a/src/dlc-handlers/leather-dlc-handler.ts b/src/dlc-handlers/leather-dlc-handler.ts index b1436fa..0206e48 100644 --- a/src/dlc-handlers/leather-dlc-handler.ts +++ b/src/dlc-handlers/leather-dlc-handler.ts @@ -2,13 +2,9 @@ import { bytesToHex, hexToBytes } from '@noble/hashes/utils'; import { Transaction } from '@scure/btc-signer'; import { Network } from 'bitcoinjs-lib'; -import { PaymentInformation } from '../models/bitcoin-models.js'; -import { - AbstractDLCHandler, - FundingPaymentType, - PaymentNotSetError, - TransactionType, -} from './abstract-dlc-handler.js'; +import { FundingPaymentType, TransactionType } from '../models/dlc-handler.models.js'; +import { PaymentNotSetError } from '../models/errors/dlc-handler.errors.models.js'; +import { AbstractDLCHandler } from './abstract-dlc-handler.js'; const networkModes = ['mainnet', 'testnet', 'regtest'] as const; @@ -33,8 +29,7 @@ interface SignPsbtRequestParams { } export class LeatherDLCHandler extends AbstractDLCHandler { - readonly _dlcHandlerType = 'browser' as const; - protected _payment?: PaymentInformation; + readonly _dlcHandlerType = 'leather' as const; private taprootDerivedPublicKey: string; private fundingDerivedPublicKey: string; diff --git a/src/dlc-handlers/ledger-dlc-handler.ts b/src/dlc-handlers/ledger-dlc-handler.ts index 8e5054c..daec7d3 100644 --- a/src/dlc-handlers/ledger-dlc-handler.ts +++ b/src/dlc-handlers/ledger-dlc-handler.ts @@ -25,16 +25,17 @@ import { updateNativeSegwitInputs, updateTaprootInputs, } from '../functions/bitcoin/psbt-functions.js'; -import { ExtendedPaymentInformation, PaymentInformation } from '../models/bitcoin-models.js'; -import { RawVault } from '../models/ethereum-models.js'; -import { truncateAddress } from '../utilities/index.js'; +import { ExtendedPaymentInformation } from '../models/bitcoin-models.js'; +import { FundingPaymentType, TransactionType } from '../models/dlc-handler.models.js'; import { - AbstractDLCHandler, - DLCHandlerError, - FundingPaymentType, + FundingDerivedPublicKeyNotSet, IncompatibleTransactionArgument, - TransactionType, -} from './abstract-dlc-handler.js'; + PolicyInformationNotSet, + TaprootDerivedPublicKeyNotSet, +} from '../models/errors/dlc-handler.errors.models.js'; +import { RawVault } from '../models/ethereum-models.js'; +import { truncateAddress } from '../utilities/index.js'; +import { AbstractDLCHandler } from './abstract-dlc-handler.js'; interface LedgerPolicyInformation { fundingWalletPolicy: DefaultWalletPolicy; @@ -42,36 +43,8 @@ interface LedgerPolicyInformation { multisigWalletPolicyHMac: Buffer; } -export class PolicyInformationNotSet extends DLCHandlerError { - constructor( - message: string = 'Policy Information not initialized. Make sure to create payments before attempting to access them.' - ) { - super(message); - this.name = 'PolicyInformationNotSet'; - } -} - -export class TaprootDerivedPublicKeyNotSet extends DLCHandlerError { - constructor( - message: string = 'Taproot Derived Public Key not set. Make sure to initialize the wallet before attempting to access it.' - ) { - super(message); - this.name = 'TaprootDerivedPublicKeyNotSet'; - } -} - -export class FundingDerivedPublicKeyNotSet extends DLCHandlerError { - constructor( - message: string = 'Funding Derived Public Key not set. Make sure to initialize the wallet before attempting to access it.' - ) { - super(message); - this.name = 'FundingDerivedPublicKeyNotSet'; - } -} - export class LedgerDLCHandler extends AbstractDLCHandler { readonly _dlcHandlerType = 'ledger' as const; - protected _payment?: PaymentInformation; private ledgerApp: AppClient; private masterFingerprint: string; private walletAccountIndex: number; diff --git a/src/dlc-handlers/unisat-fordefi-dlc-handler.ts b/src/dlc-handlers/unisat-fordefi-dlc-handler.ts index e178db3..e57a2d7 100644 --- a/src/dlc-handlers/unisat-fordefi-dlc-handler.ts +++ b/src/dlc-handlers/unisat-fordefi-dlc-handler.ts @@ -1,15 +1,11 @@ import { bytesToHex, hexToBytes } from '@noble/hashes/utils'; import { Transaction } from '@scure/btc-signer'; import { Network } from 'bitcoinjs-lib'; -import { getInputIndicesByScript } from 'src/functions/bitcoin/bitcoin-functions.js'; -import { PaymentInformation } from '../models/bitcoin-models.js'; -import { - AbstractDLCHandler, - FundingPaymentType, - PaymentNotSetError, - TransactionType, -} from './abstract-dlc-handler.js'; +import { getInputIndicesByScript } from '../functions/bitcoin/bitcoin-functions.js'; +import { FundingPaymentType, TransactionType } from '../models/dlc-handler.models.js'; +import { PaymentNotSetError } from '../models/errors/dlc-handler.errors.models.js'; +import { AbstractDLCHandler } from './abstract-dlc-handler.js'; export interface UnisatToSignInput { index: number; @@ -25,8 +21,7 @@ export interface UnisatSignPsbtRequestOptions { } export class UnisatFordefiDLCHandler extends AbstractDLCHandler { - readonly _dlcHandlerType = 'browser' as const; - protected _payment?: PaymentInformation; + readonly _dlcHandlerType = 'unisat/fordefi' as const; private taprootDerivedPublicKey: string; private fundingDerivedPublicKey: string; diff --git a/src/index.ts b/src/index.ts index 0ab0b95..e01d81f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import { DFNSDLCHandler } from './dlc-handlers/dfns-dlc-handler.js'; +import { KeyPairDLCHandler } from './dlc-handlers/keypair-dlc-handler.js'; import { LeatherDLCHandler } from './dlc-handlers/leather-dlc-handler.js'; import { LedgerDLCHandler } from './dlc-handlers/ledger-dlc-handler.js'; -import { PrivateKeyDLCHandler } from './dlc-handlers/private-key-dlc-handler.js'; import { UnisatFordefiDLCHandler } from './dlc-handlers/unisat-fordefi-dlc-handler.js'; import { EthereumHandler } from './network-handlers/ethereum-handler.js'; import { RippleHandler } from './network-handlers/ripple-handler.js'; @@ -10,7 +10,7 @@ import { LedgerXRPHandler } from './network-handlers/xrp-ledger-handler.js'; import { ProofOfReserveHandler } from './proof-of-reserve-handlers/proof-of-reserve-handler.js'; export { - PrivateKeyDLCHandler, + KeyPairDLCHandler, LedgerDLCHandler, LeatherDLCHandler, UnisatFordefiDLCHandler, diff --git a/src/models/dlc-handler.models.ts b/src/models/dlc-handler.models.ts new file mode 100644 index 0000000..ccc14f8 --- /dev/null +++ b/src/models/dlc-handler.models.ts @@ -0,0 +1,6 @@ +import { DLCHandlers } from '../constants/dlc-handler.constants.js'; + +export type DLCHandlerType = (typeof DLCHandlers)[keyof typeof DLCHandlers]; +export type FundingPaymentType = 'wpkh' | 'tr'; +export type PaymentType = 'funding' | 'multisig'; +export type TransactionType = 'funding' | 'deposit' | 'withdraw'; diff --git a/src/models/errors/dlc-handler.errors.models.ts b/src/models/errors/dlc-handler.errors.models.ts new file mode 100644 index 0000000..94e2f07 --- /dev/null +++ b/src/models/errors/dlc-handler.errors.models.ts @@ -0,0 +1,97 @@ +import { PaymentType, TransactionType } from '../dlc-handler.models.js'; + +export class DLCHandlerError extends Error { + constructor(message: string) { + super(message); + this.name = 'DLCHandlerError'; + } +} + +export class PaymentNotSetError extends DLCHandlerError { + constructor( + message: string = 'Payment information not initialized. Make sure to create payments before attempting to access them.' + ) { + super(message); + this.name = 'PaymentNotSetError'; + } +} + +export class AddressNotFoundError extends DLCHandlerError { + constructor(paymentType: PaymentType) { + super(`Address not found for ${paymentType} payment`); + this.name = 'AddressNotFoundError'; + } +} + +export class InvalidPaymentTypeError extends DLCHandlerError { + constructor(paymentType: PaymentType) { + super(`Invalid payment type: ${paymentType}`); + this.name = 'InvalidPaymentTypeError'; + } +} + +export class InvalidTransactionTypeError extends DLCHandlerError { + constructor(transactionType: TransactionType) { + super(`Invalid transaction type: ${transactionType}`); + this.name = 'InvalidTransactionTypeError'; + } +} + +export class InsufficientFundsError extends DLCHandlerError { + constructor(available: bigint, required: bigint) { + super(`Insufficient funds: have ${available}, need ${required}`); + this.name = 'InsufficientFundsError'; + } +} + +export class IncompatibleTransactionArgument extends DLCHandlerError { + constructor() { + super('Incompatible transaction argument'); + this.name = 'IncompatibleTransactionArgument'; + } +} + +export class PolicyInformationNotSet extends DLCHandlerError { + constructor( + message: string = 'Policy Information not initialized. Make sure to create payments before attempting to access them.' + ) { + super(message); + this.name = 'PolicyInformationNotSet'; + } +} + +export class TaprootDerivedPublicKeyNotSet extends DLCHandlerError { + constructor( + message: string = 'Taproot Derived Public Key not set. Make sure to initialize the wallet before attempting to access it.' + ) { + super(message); + this.name = 'TaprootDerivedPublicKeyNotSet'; + } +} + +export class FundingDerivedPublicKeyNotSet extends DLCHandlerError { + constructor( + message: string = 'Funding Derived Public Key not set. Make sure to initialize the wallet before attempting to access it.' + ) { + super(message); + this.name = 'FundingDerivedPublicKeyNotSet'; + } +} + +export class DFNSWalletIDNotSetError extends DLCHandlerError { + constructor( + message: string = 'DFNS Wallet ID not set. Make sure to initialize the wallet before attempting to access it.' + ) { + super(message); + this.name = 'DFNSWalletIDNotSetError'; + } +} + +export class SignatureGenerationFailed extends DLCHandlerError { + constructor( + message: string = 'Signature generation failed. Make sure to initialize the wallet before attempting to access it.' + ) { + super(message); + this.name = 'SignatureGenerationFailed'; + } +} diff --git a/tests/unit/sign-transactions.test.ts b/tests/unit/sign-transactions.test.ts deleted file mode 100644 index 893ac27..0000000 --- a/tests/unit/sign-transactions.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { hexToBytes } from '@noble/hashes/utils'; -import { Transaction } from '@scure/btc-signer'; -import { regtest } from 'bitcoinjs-lib/src/networks.js'; - -import { PrivateKeyDLCHandler } from '../../src/index.js'; -import { - TEST_BITCOIN_BLOCKCHAIN_FEE_RECOMMENDATION_API, - TEST_REGTEST_BITCOIN_BLOCKCHAIN_API, -} from '../mocks/api.test.constants.js'; -import { TEST_FUNDING_PSBT_PARTIALLY_SIGNED_WITHDRAW_PSBT_1 } from '../mocks/bitcoin-transaction.test.constants.js'; -import { - TEST_BITCOIN_EXTENDED_PRIVATE_KEY, - TEST_BITCOIN_WALLET_ACCOUNT_INDEX, - TEST_FUNDING_PAYMENT_TYPE, -} from '../mocks/bitcoin.test.constants.js'; - -describe('Create and Sign Vault related Transactions', () => { - let dlcHandler: PrivateKeyDLCHandler; - - it('should initialize a Private Key DLC Handler', async () => { - dlcHandler = new PrivateKeyDLCHandler( - TEST_BITCOIN_EXTENDED_PRIVATE_KEY, - TEST_BITCOIN_WALLET_ACCOUNT_INDEX, - TEST_FUNDING_PAYMENT_TYPE, - regtest, - TEST_REGTEST_BITCOIN_BLOCKCHAIN_API, - TEST_BITCOIN_BLOCKCHAIN_FEE_RECOMMENDATION_API - ); - }); - - it('should sign a funding transaction', async () => { - const signedFundingTransaction = dlcHandler.signPSBT( - Transaction.fromPSBT(hexToBytes(TEST_FUNDING_PSBT_PARTIALLY_SIGNED_WITHDRAW_PSBT_1)), - 'funding' - ); - - expect(signedFundingTransaction.isFinal).toBeTruthy(); - }); -});