@@ -336,7 +336,12 @@ func (a *covenantlessArkClient) listenForBoardingTxs(ctx context.Context) {
336
336
for {
337
337
select {
338
338
case <- ticker .C :
339
- txsToAdd , txsToConfirm , err := a .getBoardingPendingTransactions (ctx )
339
+ _ , boardingAddrs , _ , err := a .wallet .GetAddresses (ctx )
340
+ if err != nil {
341
+ log .WithError (err ).Error ("failed to get all boarding addresses" )
342
+ continue
343
+ }
344
+ txsToAdd , txsToConfirm , rbfTxs , err := a .getBoardingTransactions (ctx , boardingAddrs )
340
345
if err != nil {
341
346
log .WithError (err ).Error ("failed to get pending transactions" )
342
347
continue
@@ -361,36 +366,99 @@ func (a *covenantlessArkClient) listenForBoardingTxs(ctx context.Context) {
361
366
log .WithError (err ).Error ("failed to update boarding transactions" )
362
367
continue
363
368
}
364
- log .Debugf ("updated %d boarding transaction(s)" , count )
369
+ log .Debugf ("confirmed %d boarding transaction(s)" , count )
370
+ }
371
+
372
+ if len (rbfTxs ) > 0 {
373
+ count , err := a .store .TransactionStore ().RbfTransactions (ctx , rbfTxs )
374
+ if err != nil {
375
+ log .WithError (err ).Error ("failed to update rbf boarding transactions" )
376
+ continue
377
+ }
378
+ log .Debugf ("replaced %d transaction(s)" , count )
365
379
}
366
380
case <- ctx .Done ():
367
381
return
368
382
}
369
383
}
370
384
}
371
385
372
- func (a * covenantlessArkClient ) getBoardingPendingTransactions (
373
- ctx context.Context ,
374
- ) ([]types.Transaction , []string , error ) {
386
+ func (a * covenantlessArkClient ) getBoardingTransactions (
387
+ ctx context.Context , boardingAddrs []wallet. TapscriptsAddress ,
388
+ ) ([]types.Transaction , []string , map [ string ]types. Transaction , error ) {
375
389
oldTxs , err := a .store .TransactionStore ().GetAllTransactions (ctx )
376
390
if err != nil {
377
- return nil , nil , err
391
+ return nil , nil , nil , err
378
392
}
379
393
380
- boardingUtxos , err := a .getClaimableBoardingUtxos (ctx , nil )
394
+ rbfTxs := make (map [string ]types.Transaction , 0 )
395
+ replacements := make (map [string ]struct {}, 0 )
396
+ for _ , tx := range oldTxs {
397
+ if tx .IsBoarding () && tx .CreatedAt .IsZero () {
398
+ isRbf , replacedBy , timestamp , err := a .explorer .IsRBFTx (tx .BoardingTxid , tx .Hex )
399
+ if err != nil {
400
+ return nil , nil , nil , err
401
+ }
402
+ if isRbf {
403
+ txHex , err := a .explorer .GetTxHex (replacedBy )
404
+ if err != nil {
405
+ return nil , nil , nil , err
406
+ }
407
+ rawTx := & wire.MsgTx {}
408
+ if err := rawTx .Deserialize (strings .NewReader (txHex )); err != nil {
409
+ return nil , nil , nil , err
410
+ }
411
+ amount := uint64 (0 )
412
+ netParams := utils .ToBitcoinNetwork (a .Network )
413
+ for _ , addr := range boardingAddrs {
414
+ decoded , err := btcutil .DecodeAddress (addr .Address , & netParams )
415
+ if err != nil {
416
+ return nil , nil , nil , err
417
+ }
418
+ pkScript , err := txscript .PayToAddrScript (decoded )
419
+ if err != nil {
420
+ return nil , nil , nil , err
421
+ }
422
+ for _ , out := range rawTx .TxOut {
423
+ if bytes .Equal (out .PkScript , pkScript ) {
424
+ amount = uint64 (out .Value )
425
+ break
426
+ }
427
+ }
428
+ if amount > 0 {
429
+ break
430
+ }
431
+ }
432
+ rbfTxs [tx .BoardingTxid ] = types.Transaction {
433
+ TransactionKey : types.TransactionKey {
434
+ BoardingTxid : replacedBy ,
435
+ },
436
+ CreatedAt : time .Unix (timestamp , 0 ),
437
+ Hex : txHex ,
438
+ Amount : amount ,
439
+ }
440
+ replacements [replacedBy ] = struct {}{}
441
+ }
442
+ }
443
+ }
444
+
445
+ boardingUtxos , err := a .getClaimableBoardingUtxos (ctx , boardingAddrs , nil )
381
446
if err != nil {
382
- return nil , nil , err
447
+ return nil , nil , nil , err
383
448
}
384
449
385
450
txsToAdd := make ([]types.Transaction , 0 )
386
451
txsToConfirm := make ([]string , 0 )
387
452
for _ , u := range boardingUtxos {
453
+ if _ , ok := replacements [u .Txid ]; ok {
454
+ continue
455
+ }
456
+
388
457
found := false
389
458
for _ , tx := range oldTxs {
390
459
if tx .BoardingTxid == u .Txid {
391
460
found = true
392
- emptyTime := time.Time {}
393
- if tx .CreatedAt == emptyTime && tx .CreatedAt != u .CreatedAt {
461
+ if tx .CreatedAt .IsZero () && tx .CreatedAt != u .CreatedAt {
394
462
txsToConfirm = append (txsToConfirm , tx .TransactionKey .String ())
395
463
}
396
464
break
@@ -408,10 +476,11 @@ func (a *covenantlessArkClient) getBoardingPendingTransactions(
408
476
Amount : u .Amount ,
409
477
Type : types .TxReceived ,
410
478
CreatedAt : u .CreatedAt ,
479
+ Hex : u .Tx ,
411
480
})
412
481
}
413
482
414
- return txsToAdd , txsToConfirm , nil
483
+ return txsToAdd , txsToConfirm , rbfTxs , nil
415
484
}
416
485
417
486
func (a * covenantlessArkClient ) Balance (
@@ -877,7 +946,7 @@ func (a *covenantlessArkClient) CollaborativeExit(
877
946
return "" , fmt .Errorf ("invalid onchain address" )
878
947
}
879
948
880
- offchainAddrs , _ , _ , err := a .wallet .GetAddresses (ctx )
949
+ offchainAddrs , boardingAddrs , _ , err := a .wallet .GetAddresses (ctx )
881
950
if err != nil {
882
951
return "" , err
883
952
}
@@ -911,7 +980,7 @@ func (a *covenantlessArkClient) CollaborativeExit(
911
980
}
912
981
}
913
982
914
- boardingUtxos , err := a .getClaimableBoardingUtxos (ctx , nil )
983
+ boardingUtxos , err := a .getClaimableBoardingUtxos (ctx , boardingAddrs , nil )
915
984
if err != nil {
916
985
return "" , err
917
986
}
@@ -1001,7 +1070,14 @@ func (a *covenantlessArkClient) GetTransactionHistory(
1001
1070
}
1002
1071
1003
1072
if a .Config .WithTransactionFeed {
1004
- return a .store .TransactionStore ().GetAllTransactions (ctx )
1073
+ history , err := a .store .TransactionStore ().GetAllTransactions (ctx )
1074
+ if err != nil {
1075
+ return nil , err
1076
+ }
1077
+ sort .SliceStable (history , func (i , j int ) bool {
1078
+ return history [i ].CreatedAt .IsZero () || history [i ].CreatedAt .After (history [j ].CreatedAt )
1079
+ })
1080
+ return history , nil
1005
1081
}
1006
1082
1007
1083
spendableVtxos , spentVtxos , err := a .ListVtxos (ctx )
@@ -1019,13 +1095,13 @@ func (a *covenantlessArkClient) GetTransactionHistory(
1019
1095
return nil , err
1020
1096
}
1021
1097
1022
- txs := append (boardingTxs , offchainTxs ... )
1098
+ history := append (boardingTxs , offchainTxs ... )
1023
1099
// Sort the slice by age
1024
- sort .SliceStable (txs , func (i , j int ) bool {
1025
- return txs [i ].CreatedAt .After (txs [j ].CreatedAt )
1100
+ sort .SliceStable (history , func (i , j int ) bool {
1101
+ return history [i ].CreatedAt .IsZero () || history [ i ]. CreatedAt . After (history [j ].CreatedAt )
1026
1102
})
1027
1103
1028
- return txs , nil
1104
+ return history , nil
1029
1105
}
1030
1106
1031
1107
func (a * covenantlessArkClient ) SetNostrNotificationRecipient (ctx context.Context , nostrProfile string ) error {
@@ -1328,7 +1404,7 @@ func (a *covenantlessArkClient) sendOffchain(
1328
1404
sumOfReceivers += receiver .Amount ()
1329
1405
}
1330
1406
1331
- offchainAddrs , _ , _ , err := a .wallet .GetAddresses (ctx )
1407
+ offchainAddrs , boardingAddrs , _ , err := a .wallet .GetAddresses (ctx )
1332
1408
if err != nil {
1333
1409
return "" , err
1334
1410
}
@@ -1360,7 +1436,7 @@ func (a *covenantlessArkClient) sendOffchain(
1360
1436
}
1361
1437
}
1362
1438
1363
- boardingUtxos , err := a .getClaimableBoardingUtxos (ctx , nil )
1439
+ boardingUtxos , err := a .getClaimableBoardingUtxos (ctx , boardingAddrs , nil )
1364
1440
if err != nil {
1365
1441
return "" , err
1366
1442
}
@@ -2331,6 +2407,10 @@ func (a *covenantlessArkClient) getAllBoardingUtxos(ctx context.Context) ([]type
2331
2407
for i , vout := range tx .Vout {
2332
2408
var spent bool
2333
2409
if vout .Address == addr .Address {
2410
+ txHex , err := a .explorer .GetTxHex (tx .Txid )
2411
+ if err != nil {
2412
+ return nil , nil , err
2413
+ }
2334
2414
spentStatuses , err := a .explorer .GetTxOutspends (tx .Txid )
2335
2415
if err != nil {
2336
2416
return nil , nil , err
@@ -2350,6 +2430,7 @@ func (a *covenantlessArkClient) getAllBoardingUtxos(ctx context.Context) ([]type
2350
2430
CreatedAt : createdAt ,
2351
2431
Tapscripts : addr .Tapscripts ,
2352
2432
Spent : spent ,
2433
+ Tx : txHex ,
2353
2434
})
2354
2435
}
2355
2436
}
@@ -2359,12 +2440,9 @@ func (a *covenantlessArkClient) getAllBoardingUtxos(ctx context.Context) ([]type
2359
2440
return utxos , ignoreVtxos , nil
2360
2441
}
2361
2442
2362
- func (a * covenantlessArkClient ) getClaimableBoardingUtxos (ctx context.Context , opts * CoinSelectOptions ) ([]types.Utxo , error ) {
2363
- _ , boardingAddrs , _ , err := a .wallet .GetAddresses (ctx )
2364
- if err != nil {
2365
- return nil , err
2366
- }
2367
-
2443
+ func (a * covenantlessArkClient ) getClaimableBoardingUtxos (
2444
+ _ context.Context , boardingAddrs []wallet.TapscriptsAddress , opts * CoinSelectOptions ,
2445
+ ) ([]types.Utxo , error ) {
2368
2446
claimable := make ([]types.Utxo , 0 )
2369
2447
for _ , addr := range boardingAddrs {
2370
2448
boardingScript , err := bitcointree .ParseVtxoScript (addr .Tapscripts )
@@ -2525,10 +2603,10 @@ func (a *covenantlessArkClient) getBoardingTxs(
2525
2603
Type : types .TxReceived ,
2526
2604
CreatedAt : u .CreatedAt ,
2527
2605
Settled : u .Spent ,
2606
+ Hex : u .Tx ,
2528
2607
}
2529
2608
2530
- emptyTime := time.Time {}
2531
- if u .CreatedAt == emptyTime {
2609
+ if u .CreatedAt .IsZero () {
2532
2610
unconfirmedTxs = append (unconfirmedTxs , tx )
2533
2611
continue
2534
2612
}
@@ -2620,6 +2698,7 @@ func (a *covenantlessArkClient) handleRoundTx(
2620
2698
Type : types .TxReceived ,
2621
2699
Settled : true ,
2622
2700
CreatedAt : time .Now (),
2701
+ Hex : roundTx .Hex ,
2623
2702
})
2624
2703
} else {
2625
2704
vtxosToAddAmount := uint64 (0 )
@@ -2639,6 +2718,7 @@ func (a *covenantlessArkClient) handleRoundTx(
2639
2718
Type : types .TxSent ,
2640
2719
Settled : true ,
2641
2720
CreatedAt : time .Now (),
2721
+ Hex : roundTx .Hex ,
2642
2722
})
2643
2723
}
2644
2724
}
@@ -2661,6 +2741,7 @@ func (a *covenantlessArkClient) handleRoundTx(
2661
2741
Type : types .TxSent ,
2662
2742
Settled : true ,
2663
2743
CreatedAt : time .Now (),
2744
+ Hex : roundTx .Hex ,
2664
2745
})
2665
2746
}
2666
2747
@@ -2758,6 +2839,7 @@ func (a *covenantlessArkClient) handleRedeemTx(
2758
2839
Amount : amount ,
2759
2840
Type : types .TxReceived ,
2760
2841
CreatedAt : time .Now (),
2842
+ Hex : redeemTx .Hex ,
2761
2843
})
2762
2844
}
2763
2845
} else {
0 commit comments