Skip to content

Commit

Permalink
feat: add withdrawal transaction functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Polybius93 committed Jun 13, 2024
1 parent b50bd19 commit cf9980a
Show file tree
Hide file tree
Showing 2 changed files with 129 additions and 0 deletions.
38 changes: 38 additions & 0 deletions src/dlc-handlers/software-wallet-dlc-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import {
createClosingTransaction,
createFundingTransaction,
createWithdrawalTransaction,
} from '../functions/bitcoin/psbt-functions.js';
import { PaymentInformation } from '../models/bitcoin-models.js';
import { RawVault } from '../models/ethereum-models.js';
Expand Down Expand Up @@ -226,4 +227,41 @@ export class SoftwareWalletDLCHandler {
throw new Error(`Error creating Closing PSBT: ${error}`);
}
}

async createWithdrawalPSBT(
vault: RawVault,
fundingTransactionID: string,
feeRateMultiplier?: number,
customFeeRate?: bigint
): Promise<Transaction> {
try {
const { nativeSegwitPayment, taprootMultisigPayment } = this.getPayment();

if (
taprootMultisigPayment.address === undefined ||
nativeSegwitPayment.address === undefined
) {
throw new Error('Payment Address is undefined');
}

const feeRate =
customFeeRate ??
BigInt(await getFeeRate(this.bitcoinBlockchainFeeRecommendationAPI, feeRateMultiplier));

const withdrawalTransaction = await createWithdrawalTransaction(
this.bitcoinBlockchainAPI,
vault.valueLocked.toBigInt(),
this.bitcoinNetwork,
fundingTransactionID,
taprootMultisigPayment,
nativeSegwitPayment.address!,
feeRate,
vault.btcFeeRecipient,
vault.btcRedeemFeeBasisPoints.toBigInt()
);
return Transaction.fromPSBT(withdrawalTransaction);
} catch (error: any) {
throw new Error(`Error creating Withdrawal PSBT: ${error}`);
}
}
}
91 changes: 91 additions & 0 deletions src/functions/bitcoin/psbt-functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getFeeRecipientAddressFromPublicKey,
getUTXOs,
} from '../bitcoin/bitcoin-functions.js';
import { fetchBitcoinTransaction } from './bitcoin-request-functions.js';

/**
* Creates a Funding Transaction to fund the Multisig Transaction.
Expand Down Expand Up @@ -137,6 +138,96 @@ export function createClosingTransaction(
return closingPSBT;
}

/**
* Creates the Closing Transaction.
* Uses the Funding Transaction's ID to create the Closing Transaction.
* The Closing Transaction is sent to the User's Native Segwit Address.
*
* @param bitcoinAmount - The Amount of Bitcoin to fund the Transaction with.
* @param bitcoinNetwork - The Bitcoin Network to use.
* @param fundingTransactionID - The ID of the Funding Transaction.
* @param multisigTransaction - The Multisig Transaction.
* @param userNativeSegwitAddress - The User's Native Segwit Address.
* @param feeRate - The Fee Rate to use for the Transaction.
* @param feePublicKey - The Fee Recipient's Public Key.
* @param feeBasisPoints - The Fee Basis Points.
* @returns The Closing Transaction.
*/
export async function createWithdrawalTransaction(
bitcoinBlockchainURL: string,
bitcoinAmount: bigint,
bitcoinNetwork: Network,
fundingTransactionID: string,
multisigTransaction: P2TROut,
userNativeSegwitAddress: string,
feeRate: bigint,
feePublicKey: string,
feeBasisPoints: bigint
): Promise<Uint8Array> {
const multisigTransactionAddress = multisigTransaction.address;

if (!multisigTransactionAddress) {
throw new Error('Multisig Transaction is missing Address');
}
const fundingTransaction = await fetchBitcoinTransaction(
fundingTransactionID,
bitcoinBlockchainURL
);
const fundingTransactionOutputIndex = fundingTransaction.vout.findIndex(
output => output.scriptpubkey_address === multisigTransactionAddress
);

if (fundingTransactionOutputIndex === -1) {
throw new Error('Could not find Funding Transaction Output Index');
}

const feeAddress = getFeeRecipientAddressFromPublicKey(feePublicKey, bitcoinNetwork);
const feeAmount = getFeeAmount(Number(bitcoinAmount), Number(feeBasisPoints));

const inputs = [
{
txid: hexToBytes(fundingTransactionID),
index: fundingTransactionOutputIndex,
witnessUtxo: {
amount: bitcoinAmount,
script: multisigTransaction.script,
},
...multisigTransaction,
},
];

const outputs = [
{
address: feeAddress,
amount: BigInt(feeAmount),
},
{
address: userNativeSegwitAddress,
amount: BigInt(bitcoinAmount),
},
];

const selected = selectUTXO(inputs, outputs, 'default', {
changeAddress: multisigTransactionAddress,
feePerByte: feeRate,
bip69: false,
createTx: true,
network: bitcoinNetwork,
});

const closingTX = selected?.tx;

if (!closingTX) throw new Error('Could not create Closing Transaction');

closingTX.updateInput(0, {
sequence: 0xfffffff0,
});

const closingPSBT = closingTX.toPSBT();

return closingPSBT;
}

/**
* This function updates the PSBT with the necessary information to sign the inputs
* that correspond to the given input signing configuration.
Expand Down

0 comments on commit cf9980a

Please sign in to comment.