@@ -20,9 +20,8 @@ public class HierarchicalDeterministicAddressProvider(
2020 ArkAddress ? sweepDestination )
2121 : IArkadeAddressProvider
2222{
23- private int _lastUsedIndex = wallet . LastUsedIndex ;
24-
25- public async Task < bool > IsOurs ( OutputDescriptor descriptor , CancellationToken cancellationToken = default )
23+ ///<inheritdoc/>
24+ public Task < bool > IsOurs ( OutputDescriptor descriptor , CancellationToken cancellationToken = default )
2625 {
2726 // Guard: the wallet must have an account descriptor. The pre-existing
2827 // `OutputDescriptor.Parse(wallet.AccountDescriptor, network)` line on
@@ -36,12 +35,13 @@ public async Task<bool> IsOurs(OutputDescriptor descriptor, CancellationToken ca
3635 var index = descriptor . Extract ( ) . DerivationPath ? . Indexes . Last ( ) . ToString ( ) ;
3736 if ( index is null )
3837 {
39- return false ;
38+ return Task . FromResult ( false ) ;
4039 }
4140 var expected = GetDescriptorFromIndex ( network , wallet . AccountDescriptor , Convert . ToInt32 ( index ) ) ;
42- return expected . Equals ( descriptor ) ;
41+ return Task . FromResult ( expected . Equals ( descriptor ) ) ;
4342 }
44-
43+
44+ ///<inheritdoc/>
4545 public async Task < OutputDescriptor > GetNextSigningDescriptor ( CancellationToken cancellationToken = default )
4646 {
4747 await using var @lock = await safetyService . LockKeyAsync ( $ "wallet::{ wallet . Id } ", cancellationToken ) ;
@@ -58,8 +58,6 @@ public async Task<OutputDescriptor> GetNextSigningDescriptor(CancellationToken c
5858
5959 await walletStorage . UpdateLastUsedIndex ( wallet . Id , nextIndex + 1 , cancellationToken ) ;
6060
61- _lastUsedIndex = nextIndex + 1 ;
62-
6361 return descriptor ;
6462 }
6563
@@ -74,58 +72,56 @@ internal static OutputDescriptor GetDescriptorFromIndex(Network network, string
7472 {
7573 // Route through the cached parser — `OutputDescriptor.Parse` is observed
7674 // at ~500-1000ms per call and this path runs on every IsOurs check.
77- return NArk . Abstractions . Extensions . KeyExtensions . ParseOutputDescriptor ( descriptor . Replace ( "/*" , $ "/{ index } ") , network ) ;
75+ return KeyExtensions . ParseOutputDescriptor ( descriptor . Replace ( "/*" , $ "/{ index } ") , network ) ;
7876 }
7977
78+ ///<inheritdoc/>
8079 public async Task < ( ArkContract contract , ArkContractEntity entity ) > GetNextContract (
8180 NextContractPurpose purpose ,
8281 ContractActivityState activityState ,
8382 ArkContract [ ] ? inputContracts = null ,
8483 CancellationToken cancellationToken = default )
8584 {
8685 var info = await transport . GetServerInfoAsync ( cancellationToken ) ;
87- ArkContract ? result = null ;
88-
89- if ( purpose == NextContractPurpose . Boarding )
90- {
91- result = new ArkBoardingContract (
92- info . SignerKey ,
93- info . BoardingExit ,
94- await GetNextSigningDescriptor ( cancellationToken )
95- ) ;
96- }
97- else if ( purpose == NextContractPurpose . SendToSelf && sweepDestination is not null )
98- {
99- result = new UnknownArkContract ( sweepDestination , info . SignerKey , info . Network . ChainName == ChainName . Mainnet ) ;
100- activityState = ContractActivityState . Inactive ;
101- }
102- else if ( purpose == NextContractPurpose . SendToSelf )
86+
87+ ( ArkContract contract , ContractActivityState state ) = purpose switch
10388 {
104- var recycledDescriptor = inputContracts is not null
105- ? await TryGetRecyclableDescriptor ( inputContracts , info . SignerKey , cancellationToken )
106- : null ;
107-
108- if ( recycledDescriptor is not null )
109- {
110- result = new ArkPaymentContract ( info . SignerKey , info . UnilateralExit , recycledDescriptor ) ;
111- activityState = ContractActivityState . Inactive ;
112- }
113- else
114- {
115- activityState = ContractActivityState . AwaitingFundsBeforeDeactivate ;
116- }
117- }
118-
119- result ??= new ArkPaymentContract (
120- info . SignerKey ,
121- info . UnilateralExit ,
122- await GetNextSigningDescriptor ( cancellationToken )
123- ) ;
124-
125- var entity = result . ToEntity ( wallet . Id , info . SignerKey , null , activityState ) ;
126- if ( result is UnknownArkContract )
89+ NextContractPurpose . Boarding => (
90+ new ArkBoardingContract (
91+ info . SignerKey , info . BoardingExit , await GetNextSigningDescriptor ( cancellationToken ) ) ,
92+ activityState ) ,
93+
94+ // Collaborative-exit sweep target: a fixed external address, never tracked.
95+ NextContractPurpose . SendToSelf when sweepDestination is not null => (
96+ new UnknownArkContract ( sweepDestination , info . SignerKey , info . Network . ChainName == ChainName . Mainnet ) ,
97+ ContractActivityState . Inactive ) ,
98+
99+ NextContractPurpose . SendToSelf =>
100+ await SendToSelfContractAsync ( info , inputContracts , cancellationToken ) ,
101+
102+ _ => (
103+ new ArkPaymentContract ( info . SignerKey , info . UnilateralExit , await GetNextSigningDescriptor ( cancellationToken ) ) ,
104+ activityState ) ,
105+ } ;
106+
107+ var entity = contract . ToEntity ( wallet . Id , info . SignerKey , null , state ) ;
108+ if ( contract is UnknownArkContract )
127109 entity = entity with { Metadata = new Dictionary < string , string > { [ "Source" ] = "sweep-destination" } } ;
128- return ( result , entity ) ;
110+ return ( contract , entity ) ;
111+ }
112+
113+ private async Task < ( ArkContract , ContractActivityState ) > SendToSelfContractAsync (
114+ ArkServerInfo info , ArkContract [ ] ? inputContracts , CancellationToken cancellationToken )
115+ {
116+ var recycledDescriptor = inputContracts is not null
117+ ? await TryGetRecyclableDescriptor ( inputContracts , info . SignerKey , cancellationToken )
118+ : null ;
119+
120+ return recycledDescriptor is not null
121+ ? ( new ArkPaymentContract ( info . SignerKey , info . UnilateralExit , recycledDescriptor ) ,
122+ ContractActivityState . Inactive )
123+ : ( new ArkPaymentContract ( info . SignerKey , info . UnilateralExit , await GetNextSigningDescriptor ( cancellationToken ) ) ,
124+ ContractActivityState . AwaitingFundsBeforeDeactivate ) ;
129125 }
130126
131127 private async Task < OutputDescriptor ? > TryGetRecyclableDescriptor (
0 commit comments