Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
},
"devDependencies": {
"@expo/ngrok": "^4.1.3",
"@types/node": "^24.5.2",
"@types/node": "^24.10.7",
"@types/react": "~19.1.0",
"eas-cli-local-build-plugin": "^1.0.240",
"eslint": "^9.35.0",
Expand Down
63 changes: 56 additions & 7 deletions services/rbf-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@ const MIN_RBF_FEE_INCREASE_RATE = 0.1;
* Validates the given ECC library by checking basic functionality.
* Throws an error if validation fails.
*/
function validateECCLibrary(ecc: any): void {
interface ECCLibrary {
isPrivate(privKey: Uint8Array): boolean;
pointFromScalar(scalar: Uint8Array, compressed: boolean): Uint8Array | null;
}

function validateECCLibrary(ecc: ECCLibrary): void {
const testPrivateKey = new Uint8Array(32);
testPrivateKey[31] = 1; // Set to 1 to ensure it's a valid private key

Expand All @@ -45,9 +50,47 @@ function validateECCLibrary(ecc: any): void {
}
}

export interface EsploraTransactionVin {
txid?: string;
vout?: number;
sequence?: number;
prevout?: {
value?: number;
scriptpubkey?: string;
scriptpubkey_address?: string;
} | null;
[key: string]: unknown;
}

export interface EsploraTransactionVout {
value?: number;
scriptpubkey?: string;
scriptpubkey_address?: string;
[key: string]: unknown;
}

/**
* Minimal representation of an Esplora transaction used by the RBF service.
* Additional fields returned by Esplora are allowed via the index signature.
*/
export interface EsploraTransaction {
txid: string;
version?: number;
locktime?: number;
vin: EsploraTransactionVin[];
vout: EsploraTransactionVout[];
status?: {
confirmed?: boolean;
block_height?: number;
block_hash?: string;
block_time?: number;
};
[key: string]: unknown;
}

export interface RBFTransaction {
txid: string;
originalTx: any; // Raw Esplora transaction with vin/vout properties
originalTx: EsploraTransaction; // Raw Esplora transaction with vin/vout properties
newFeeRate: number;
newFee: number;
replacementTx?: string; // hex transaction
Expand All @@ -59,7 +102,7 @@ export interface RBFValidationResult {
isValid: boolean;
canReplace: boolean;
reason?: string;
originalTx?: any; // Raw Esplora transaction with vin/vout properties
originalTx?: EsploraTransaction; // Raw Esplora transaction with vin/vout properties
utxos?: UTXO[];
}

Expand All @@ -72,7 +115,7 @@ export interface RBFValidationResult {
* causing incorrect RBF validation. This function now detects the format
* and parses accordingly.
*/
function parseSequenceNumber(sequence: any): number {
function parseSequenceNumber(sequence: unknown): number {
if (typeof sequence === 'number') {
return sequence;
}
Expand Down Expand Up @@ -415,6 +458,7 @@ export async function createReplacementTransaction(
const bip32Instance = bip32.BIP32Factory(ecc);

// Create transaction builder (replace TransactionBuilder with PSBT for modern bitcoinjs-lib)
// TransactionBuilder is deprecated in newer versions of bitcoinjs-lib. Consider migrating to PSBT (Partially Signed Bitcoin Transaction) for better compatibility and future-proofing.
let txb = new bitcoin.TransactionBuilder(bitcoin.networks.bitcoin);

// Get our inputs from the original transaction
Expand All @@ -433,7 +477,11 @@ export async function createReplacementTransaction(
const inputMap = new Map<string, { input: any; utxo: UTXO; addressIndex: number; chain: number }>();

for (const input of ourInputs) {
const address = input.prevout.scriptpubkey_address;
const address = input.prevout?.scriptpubkey_address;

if (!address) {
throw new Error(`Input missing address information: ${JSON.stringify(input)}`);
}

// Derive the actual BIP32 address index and chain instead of using array index
const { index: addressIndex, chain } = await deriveAddressIndexAndChainFromAddress(mnemonic, address);
Expand Down Expand Up @@ -490,7 +538,7 @@ export async function createReplacementTransaction(
} else {
throw new Error(`Unsupported output type: ${JSON.stringify(output)}`);
}
totalOutputValue += output.value;
totalOutputValue += output.value ?? 0;
}

// Find the change output in the original transaction (if any)
Expand All @@ -502,7 +550,7 @@ export async function createReplacementTransaction(
const output = originalTx.vout[i];
if (output.scriptpubkey_address && walletAddressesSet.has(output.scriptpubkey_address)) {
changeOutputIndex = i;
changeOutputValue = output.value;
changeOutputValue = output.value ?? 0;
break;
}
}
Expand Down Expand Up @@ -947,6 +995,7 @@ async function createCancellationTransaction(
const bip32Instance = bip32.BIP32Factory(ecc);

// Create transaction builder (replace TransactionBuilder with PSBT for modern bitcoinjs-lib)
// TransactionBuilder is deprecated in newer versions of bitcoinjs-lib. Consider migrating to PSBT (Partially Signed Bitcoin Transaction) for better compatibility and future-proofing.
let txb = new bitcoin.TransactionBuilder(bitcoin.networks.bitcoin);

// Get our inputs from the original transaction
Expand Down
2 changes: 2 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"lib": ["ES2015", "ES2016", "ES2017", "ES2018", "ES2019", "ES2020", "DOM"],
"target": "ES2015",
"paths": {
"@/*": [
"./*"
Expand Down
Loading