Skip to content
This repository was archived by the owner on Feb 9, 2026. It is now read-only.

Commit 4c1119e

Browse files
authored
Merge pull request #240 from synonymdev/verify-output-index
Verify Watch Output Index
2 parents 1458b92 + b313813 commit 4c1119e

6 files changed

Lines changed: 225 additions & 17 deletions

File tree

example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ PODS:
316316
- React-jsinspector (0.72.4)
317317
- React-logger (0.72.4):
318318
- glog
319-
- react-native-ldk (0.0.139):
319+
- react-native-ldk (0.0.140):
320320
- React
321321
- react-native-randombytes (3.6.1):
322322
- React-Core
@@ -621,7 +621,7 @@ SPEC CHECKSUMS:
621621
React-jsiexecutor: c7f826e40fa9cab5d37cab6130b1af237332b594
622622
React-jsinspector: aaed4cf551c4a1c98092436518c2d267b13a673f
623623
React-logger: da1ebe05ae06eb6db4b162202faeafac4b435e77
624-
react-native-ldk: c8a4bdf3fa122794a0f3c52f6926cf7ccba1e3ea
624+
react-native-ldk: c5037d5c182a25d23569ae79083bb18c8bc0a69a
625625
react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846
626626
react-native-tcp-socket: c1b7297619616b4c9caae6889bcb0aba78086989
627627
React-NativeModulesApple: edb5ace14f73f4969df6e7b1f3e41bef0012740f

example/yarn.lock

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2093,6 +2093,11 @@
20932093
dependencies:
20942094
eslint-scope "5.1.1"
20952095

2096+
"@noble/hashes@^1.2.0":
2097+
version "1.4.0"
2098+
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426"
2099+
integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==
2100+
20962101
"@nodelib/fs.scandir@2.1.5":
20972102
version "2.1.5"
20982103
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
@@ -2408,9 +2413,17 @@
24082413
dependencies:
24092414
"@sinonjs/commons" "^3.0.0"
24102415

2416+
"@synonymdev/raw-transaction-decoder@1.0.0":
2417+
version "1.0.0"
2418+
resolved "https://registry.yarnpkg.com/@synonymdev/raw-transaction-decoder/-/raw-transaction-decoder-1.0.0.tgz#2090dd0979c0f35f126f9a2c7557a4bc8cda44eb"
2419+
integrity sha512-YCv96YA+OTFbb/3pUFEXavaivP1Ofwq7TJInsHlP7QrJlBBSqnT3aZXsRGC2dLm8PA8HR4Fnu5Kq0tCnJ7tEqA==
2420+
dependencies:
2421+
bitcoinjs-lib "6.1.4"
2422+
24112423
"@synonymdev/react-native-ldk@../lib":
2412-
version "0.0.127"
2424+
version "0.0.140"
24132425
dependencies:
2426+
"@synonymdev/raw-transaction-decoder" "1.0.0"
24142427
bech32 "^2.0.0"
24152428
bitcoinjs-lib "^6.0.2"
24162429

@@ -3117,6 +3130,11 @@ base-x@^3.0.2:
31173130
dependencies:
31183131
safe-buffer "^5.0.1"
31193132

3133+
base-x@^4.0.0:
3134+
version "4.0.0"
3135+
resolved "https://registry.yarnpkg.com/base-x/-/base-x-4.0.0.tgz#d0e3b7753450c73f8ad2389b5c018a4af7b2224a"
3136+
integrity sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==
3137+
31203138
base64-js@^1.0.2, base64-js@^1.1.2, base64-js@^1.3.1:
31213139
version "1.5.1"
31223140
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
@@ -3144,6 +3162,11 @@ bip174@^2.0.1:
31443162
resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.1.0.tgz#cd3402581feaa5116f0f00a0eaee87a5843a2d30"
31453163
integrity sha512-lkc0XyiX9E9KiVAS1ZiOqK1xfiwvf4FXDDdkDq5crcDzOq+xGytY+14qCsqz7kCiy8rpN1CRNfacRhf9G3JNSA==
31463164

3165+
bip174@^2.1.0:
3166+
version "2.1.1"
3167+
resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.1.1.tgz#ef3e968cf76de234a546962bcf572cc150982f9f"
3168+
integrity sha512-mdFV5+/v0XyNYXjBS6CQPLo9ekCx4gtKZFnJm5PMto7Fs9hTTDpkkzOB7/FtluRI6JbUUAu+snTYfJRgHLZbZQ==
3169+
31473170
bip32@2.0.6:
31483171
version "2.0.6"
31493172
resolved "https://registry.yarnpkg.com/bip32/-/bip32-2.0.6.tgz#6a81d9f98c4cd57d05150c60d8f9e75121635134"
@@ -3193,6 +3216,18 @@ bitcoinjs-lib@6.0.2, bitcoinjs-lib@^6.0.2:
31933216
varuint-bitcoin "^1.1.2"
31943217
wif "^2.0.1"
31953218

3219+
bitcoinjs-lib@6.1.4:
3220+
version "6.1.4"
3221+
resolved "https://registry.yarnpkg.com/bitcoinjs-lib/-/bitcoinjs-lib-6.1.4.tgz#c985c90ae4d0385f951436c36dc3d2555961a63f"
3222+
integrity sha512-MRbVBPJ0DI7nhnisoFYVbUGMQwZDrkfkTuZghtsblokq3OYRMuek2We1jhpAqDVwtM3CrO7AbwANdBZ/KgzQyg==
3223+
dependencies:
3224+
"@noble/hashes" "^1.2.0"
3225+
bech32 "^2.0.0"
3226+
bip174 "^2.1.0"
3227+
bs58check "^3.0.1"
3228+
typeforce "^1.11.3"
3229+
varuint-bitcoin "^1.1.2"
3230+
31963231
bl@^4.1.0:
31973232
version "4.1.0"
31983233
resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz"
@@ -3335,6 +3370,13 @@ bs58@^4.0.0:
33353370
dependencies:
33363371
base-x "^3.0.2"
33373372

3373+
bs58@^5.0.0:
3374+
version "5.0.0"
3375+
resolved "https://registry.yarnpkg.com/bs58/-/bs58-5.0.0.tgz#865575b4d13c09ea2a84622df6c8cbeb54ffc279"
3376+
integrity sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==
3377+
dependencies:
3378+
base-x "^4.0.0"
3379+
33383380
bs58check@<3.0.0, bs58check@^2.1.1, bs58check@^2.1.2:
33393381
version "2.1.2"
33403382
resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
@@ -3344,6 +3386,14 @@ bs58check@<3.0.0, bs58check@^2.1.1, bs58check@^2.1.2:
33443386
create-hash "^1.1.0"
33453387
safe-buffer "^5.1.2"
33463388

3389+
bs58check@^3.0.1:
3390+
version "3.0.1"
3391+
resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-3.0.1.tgz#2094d13720a28593de1cba1d8c4e48602fdd841c"
3392+
integrity sha512-hjuuJvoWEybo7Hn/0xOrczQKKEKD63WguEjlhLExYs2wUBcebDC1jDNK17eEAD2lYfw82d5ASC1d7K3SWszjaQ==
3393+
dependencies:
3394+
"@noble/hashes" "^1.2.0"
3395+
bs58 "^5.0.0"
3396+
33473397
bser@2.1.1:
33483398
version "2.1.1"
33493399
resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz"

lib/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@synonymdev/react-native-ldk",
33
"title": "React Native LDK",
4-
"version": "0.0.139",
4+
"version": "0.0.140",
55
"description": "React Native wrapper for LDK",
66
"main": "./dist/index.js",
77
"types": "./dist/index.d.ts",
@@ -65,6 +65,7 @@
6565
"typescript": "^4.2.4"
6666
},
6767
"dependencies": {
68+
"@synonymdev/raw-transaction-decoder": "1.0.0",
6869
"bech32": "^2.0.0",
6970
"bitcoinjs-lib": "^6.0.2"
7071
},

lib/src/lightning-manager.ts

Lines changed: 120 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import {
7070
import * as bitcoin from 'bitcoinjs-lib';
7171
import networks from './utils/networks';
7272
import { EmitterSubscription } from 'react-native';
73+
import decodeRawTx from '@synonymdev/raw-transaction-decoder';
7374

7475
const MAX_PENDING_PAY_AGE = 1000 * 60 * 60; // 1 hour
7576

@@ -125,6 +126,7 @@ class LightningManager {
125126
address: '',
126127
publicKey: '',
127128
});
129+
addresses: string[] = [];
128130
getScriptPubKeyHistory: TGetScriptPubKeyHistory = async (): Promise<
129131
TGetScriptPubKeyHistoryResponse[]
130132
> => [];
@@ -360,7 +362,15 @@ class LightningManager {
360362
this.getBestBlock = getBestBlock;
361363
this.account = account;
362364
this.network = network;
363-
this.getAddress = getAddress;
365+
this.addresses = await this.readAddressesFromFile();
366+
this.getAddress = async () => {
367+
const addressObj = await getAddress();
368+
const address = addressObj?.address;
369+
if (address) {
370+
this.saveAddressToFile(address).then();
371+
}
372+
return addressObj;
373+
};
364374
this.getScriptPubKeyHistory = getScriptPubKeyHistory;
365375
this.getFees = getFees;
366376
this.broadcastTransaction = broadcastTransaction;
@@ -797,6 +807,39 @@ class LightningManager {
797807
return ok('Watch transactions checked');
798808
};
799809

810+
saveAddressToFile = async (address: string): Promise<Result<boolean>> => {
811+
if (!address) {
812+
return err('No address provided');
813+
}
814+
if (this.addresses.includes(address)) {
815+
return ok(true);
816+
}
817+
this.addresses.push(address);
818+
const accountPath = appendPath(this.baseStoragePath, this.account.name);
819+
const writeRes = await ldk.writeToFile({
820+
fileName: ELdkFiles.addresses,
821+
path: accountPath,
822+
content: JSON.stringify(this.addresses),
823+
remotePersist: true,
824+
});
825+
if (writeRes.isErr()) {
826+
return err(writeRes.error.message);
827+
}
828+
return ok(true);
829+
};
830+
831+
readAddressesFromFile = async (): Promise<string[]> => {
832+
const accountPath = appendPath(this.baseStoragePath, this.account.name);
833+
const writeRes = await ldk.readFromFile({
834+
fileName: ELdkFiles.addresses,
835+
path: accountPath,
836+
});
837+
if (writeRes.isOk()) {
838+
return parseData(writeRes.value.content, []);
839+
}
840+
return [];
841+
};
842+
800843
/**
801844
* Saves confirmed watch outputs to storage.
802845
* @param {string} confirmedWatchOutput
@@ -871,25 +914,89 @@ class LightningManager {
871914
transactionData?.vout[index].hex,
872915
);
873916

874-
if (txs.length < 1) {
917+
if (txs.length <= 1) {
875918
continue;
876919
}
877920

878-
// TODO: Implement index fix
879-
const tx = txs[1];
880-
if (!tx?.txid) {
881-
continue;
921+
// Used for older versions that have not previously tracked addresses.
922+
const fallbackIndex = 1;
923+
const fallbackTx = txs[fallbackIndex];
924+
let useFallback = true;
925+
926+
const txsLength = txs.length - 1;
927+
let transactionId: string | undefined;
928+
let txData: TTransactionData | undefined;
929+
let fallbackTxData: TTransactionData | undefined;
930+
for (let i = txsLength; i >= 0; i--) {
931+
await sleep(100);
932+
933+
const tx = txs[i];
934+
if (!tx?.txid) {
935+
continue;
936+
}
937+
938+
const txDataRes = await this.getTransactionData(tx.txid);
939+
if (!txDataRes || !txDataRes?.height || !txDataRes?.transaction) {
940+
if (!txDataRes) {
941+
console.error('No txDataRes');
942+
}
943+
continue;
944+
}
945+
946+
if (i === fallbackIndex) {
947+
// Set fallbackTxData data to prevent calling getTransactionData again.
948+
fallbackTxData = txDataRes;
949+
}
950+
951+
const decodedTxRes = decodeRawTx(txDataRes?.transaction);
952+
if (decodedTxRes.isErr()) {
953+
console.error(
954+
'Error decoding transaction',
955+
decodedTxRes.error.message,
956+
);
957+
continue;
958+
}
959+
960+
const decodedTx = decodedTxRes.value;
961+
const decodedOutputs = decodedTx?.outputs ?? [];
962+
let decodedAddresses: string[] = [];
963+
decodedOutputs.map((o) => {
964+
if (o?.scriptPubKey?.addresses) {
965+
decodedAddresses = decodedAddresses.concat(
966+
o.scriptPubKey.addresses,
967+
);
968+
}
969+
});
970+
971+
for (const address of decodedAddresses) {
972+
if (this.addresses.includes(address)) {
973+
transactionId = tx.txid;
974+
txData = txDataRes;
975+
useFallback = false;
976+
break;
977+
}
978+
}
979+
if (transactionId && txData) {
980+
break;
981+
}
882982
}
883-
const txData = await this.getTransactionData(tx.txid);
884-
if (!txData) {
885-
//Watch Output was never confirmed so there's no need to unconfirm it.
886-
continue;
983+
984+
if (useFallback) {
985+
transactionId = fallbackTx?.txid;
986+
if (!transactionId) {
987+
continue;
988+
}
989+
txData =
990+
fallbackTxData ?? (await this.getTransactionData(transactionId));
887991
}
888-
if (!txData?.height) {
992+
993+
if (!transactionId || !txData?.height) {
994+
// Transaction has either not entered the mempool yet or has zero confirmations. Try again later.
889995
continue;
890996
}
997+
891998
const pos = await this.getTransactionPosition({
892-
tx_hash: tx.txid,
999+
tx_hash: transactionId,
8931000
height: txData.height,
8941001
});
8951002
if (pos >= 0) {
@@ -901,7 +1008,7 @@ class LightningManager {
9011008
if (setTxConfirmedRes.isOk()) {
9021009
await this.saveUnconfirmedTx({
9031010
...txData,
904-
txid: tx.txid,
1011+
txid: transactionId,
9051012
script_pubkey,
9061013
});
9071014
await this.saveConfirmedWatchOutput(script_pubkey);

lib/src/utils/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@ export enum ELdkFiles {
486486
payments_claimed = 'payments_claimed.json', // Written in swift/kotlin and read from JS
487487
payments_sent = 'payments_sent.json', // Written in swift/kotlin and read from JS
488488
bolt11_invoices = 'bolt11_invoices.json', // Saved/read from JS
489+
addresses = 'addresses.json',
489490
confirmed_watch_outputs = 'confirmed_watch_outputs.json',
490491
}
491492

0 commit comments

Comments
 (0)