@@ -800,30 +800,45 @@ impl BoltzApiClientV2 {
800800 self . get_json ( & end_point) . await
801801 }
802802
803- /// Restore swaps from an xpub
803+ /// Restore swaps from an xpub.
804+ ///
805+ /// `derivation_path` is the path boltz appends `/{index}` to when deriving
806+ /// child keys from `xpub`. Pass `"m"` when `xpub` is already the
807+ /// swap-account key (`m/44/0/0/0`), so boltz derives `xpub/{index}` to match
808+ /// our per-swap keys. Omitting the path makes boltz apply its own default
809+ /// and find nothing.
804810 pub async fn post_swap_restore (
805811 & self ,
806812 xpub : & String ,
813+ derivation_path : Option < String > ,
814+ gap_limit : Option < u32 > ,
807815 ) -> Result < Vec < SwapRestoreResponse > , Error > {
808- let data = json ! (
809- {
810- "xpub" : xpub,
811- }
812- ) ;
816+ let mut data = json ! ( { "xpub" : xpub } ) ;
817+ if let Some ( path) = derivation_path {
818+ data[ "derivationPath" ] = json ! ( path) ;
819+ }
820+ if let Some ( gap) = gap_limit {
821+ data[ "gapLimit" ] = json ! ( gap) ;
822+ }
813823
814824 self . post_json ( "swap/restore" , data) . await
815825 }
816826
817- /// Restore swaps from an xpub
827+ /// Highest swap-key derivation index boltz has seen for `xpub` (-1 if none).
828+ /// See [`Self::post_swap_restore`] for the `derivation_path` semantics.
818829 pub async fn post_swap_restore_index (
819830 & self ,
820831 xpub : & String ,
832+ derivation_path : Option < String > ,
833+ gap_limit : Option < u32 > ,
821834 ) -> Result < SwapRestoreIndexResponse , Error > {
822- let data = json ! (
823- {
824- "xpub" : xpub,
825- }
826- ) ;
835+ let mut data = json ! ( { "xpub" : xpub } ) ;
836+ if let Some ( path) = derivation_path {
837+ data[ "derivationPath" ] = json ! ( path) ;
838+ }
839+ if let Some ( gap) = gap_limit {
840+ data[ "gapLimit" ] = json ! ( gap) ;
841+ }
827842
828843 self . post_json ( "swap/restore/index" , data) . await
829844 }
@@ -1871,6 +1886,115 @@ mod tests {
18711886 assert ! ( result. is_ok( ) , "Failed to get height" ) ;
18721887 }
18731888
1889+ // Hits the live mainnet swap/restore endpoint with the swap-master xpub
1890+ // derived from a known wallet mnemonic, and prints what boltz returns.
1891+ // Run: cargo test test_swap_restore_endpoint_print -- --nocapture
1892+ #[ macros:: async_test_all]
1893+ async fn test_swap_restore_endpoint_print ( ) {
1894+ let wallet_mnemonic =
1895+ "slogan prevent affair connect autumn crop together earn track ribbon horn copy" ;
1896+ let swap_master_key =
1897+ crate :: util:: secrets:: SwapMasterKey :: new ( wallet_mnemonic, None , Network :: Mainnet )
1898+ . unwrap ( ) ;
1899+ let xpub = swap_master_key. get_master_xpub ( ) . to_string ( ) ;
1900+ println ! ( "SWAP_RESTORE_TEST xpub: {xpub}" ) ;
1901+
1902+ let client = BoltzApiClientV2 :: new ( BOLTZ_MAINNET_URL_V2 . to_string ( ) , None ) ;
1903+ let responses = client
1904+ . post_swap_restore ( & xpub, Some ( "m" . to_string ( ) ) , Some ( 100 ) )
1905+ . await
1906+ . unwrap ( ) ;
1907+ println ! ( "SWAP_RESTORE_TEST returned {} swaps" , responses. len( ) ) ;
1908+ for r in & responses {
1909+ println ! (
1910+ "SWAP_RESTORE_TEST {} type={:?} status={} {}->{}" ,
1911+ r. id, r. swap_type, r. status, r. from, r. to
1912+ ) ;
1913+ }
1914+ }
1915+
1916+ // Creates a fresh BTC->L-BTC chain swap at swap-key indexes 0 (refund) and
1917+ // 1 (claim) using the seed's xpub-derived keys, then immediately calls
1918+ // swap/restore (and swap/restore/index) with the same xpub to see whether
1919+ // boltz matches the just-registered leaf pubkeys.
1920+ // Run: cargo test test_create_chain_then_restore -- --nocapture
1921+ #[ macros:: async_test_all]
1922+ async fn test_create_chain_then_restore ( ) {
1923+ use crate :: util:: secrets:: { Preimage , SwapMasterKey } ;
1924+ let wallet_mnemonic =
1925+ "slogan prevent affair connect autumn crop together earn track ribbon horn copy" ;
1926+ let smk = SwapMasterKey :: new ( wallet_mnemonic, None , Network :: Mainnet ) . unwrap ( ) ;
1927+ let xpub = smk. get_master_xpub ( ) . to_string ( ) ;
1928+ let refund_kps = smk. derive_swapkey ( 0 ) . unwrap ( ) ;
1929+ let claim_kps = smk. derive_swapkey ( 1 ) . unwrap ( ) ;
1930+ let refund_public_key = PublicKey {
1931+ inner : refund_kps. public_key ( ) ,
1932+ compressed : true ,
1933+ } ;
1934+ let claim_public_key = PublicKey {
1935+ inner : claim_kps. public_key ( ) ,
1936+ compressed : true ,
1937+ } ;
1938+ let preimage = Preimage :: from_swap_key ( & claim_kps) ;
1939+ println ! ( "CREATE_RESTORE xpub : {xpub}" ) ;
1940+ println ! ( "CREATE_RESTORE refund pubkey: {refund_public_key} (index 0)" ) ;
1941+ println ! ( "CREATE_RESTORE claim pubkey : {claim_public_key} (index 1)" ) ;
1942+
1943+ let client = BoltzApiClientV2 :: new ( BOLTZ_MAINNET_URL_V2 . to_string ( ) , None ) ;
1944+ let req = CreateChainRequest {
1945+ from : "BTC" . to_string ( ) ,
1946+ to : "L-BTC" . to_string ( ) ,
1947+ preimage_hash : preimage. sha256 ,
1948+ claim_public_key : Some ( claim_public_key) ,
1949+ refund_public_key : Some ( refund_public_key) ,
1950+ user_lock_amount : Some ( 100_000 ) ,
1951+ server_lock_amount : None ,
1952+ pair_hash : None ,
1953+ referral_id : None ,
1954+ webhook : None ,
1955+ } ;
1956+ let created = client. post_chain_req ( req) . await ;
1957+ match & created {
1958+ Ok ( resp) => println ! ( "CREATE_RESTORE created chain swap: {}" , resp. id) ,
1959+ Err ( e) => println ! ( "CREATE_RESTORE create FAILED: {e:?}" ) ,
1960+ }
1961+ let created_id = created. ok ( ) . map ( |r| r. id ) ;
1962+
1963+ match client
1964+ . post_swap_restore_index ( & xpub, Some ( "m" . to_string ( ) ) , Some ( 100 ) )
1965+ . await
1966+ {
1967+ Ok ( idx) => println ! ( "CREATE_RESTORE restore/index for xpub = {}" , idx. index) ,
1968+ Err ( e) => println ! ( "CREATE_RESTORE restore/index FAILED: {e:?}" ) ,
1969+ }
1970+
1971+ let responses = client
1972+ . post_swap_restore ( & xpub, Some ( "m" . to_string ( ) ) , Some ( 100 ) )
1973+ . await
1974+ . unwrap ( ) ;
1975+ println ! ( "CREATE_RESTORE restore returned {} swaps" , responses. len( ) ) ;
1976+ for r in & responses {
1977+ let ck = r
1978+ . claim_details
1979+ . as_ref ( )
1980+ . map ( |d| d. key_index as i64 )
1981+ . unwrap_or ( -1 ) ;
1982+ let rk = r
1983+ . refund_details
1984+ . as_ref ( )
1985+ . map ( |d| d. key_index as i64 )
1986+ . unwrap_or ( -1 ) ;
1987+ println ! (
1988+ "CREATE_RESTORE {} type={:?} status={} {}->{} claimIdx={} refundIdx={}" ,
1989+ r. id, r. swap_type, r. status, r. from, r. to, ck, rk
1990+ ) ;
1991+ }
1992+ if let Some ( id) = created_id {
1993+ let found = responses. iter ( ) . any ( |r| r. id == id) ;
1994+ println ! ( "CREATE_RESTORE just-created swap {id} found in restore: {found}" ) ;
1995+ }
1996+ }
1997+
18741998 #[ macros:: async_test_all]
18751999 async fn test_get_submarine_pairs ( ) {
18762000 let client = BoltzApiClientV2 :: new ( BOLTZ_MAINNET_URL_V2 . to_string ( ) , None ) ;
0 commit comments