Skip to content

feat: lwk#504

Merged
jackstar12 merged 13 commits into
masterfrom
feat/lwk
Jul 16, 2025
Merged

feat: lwk#504
jackstar12 merged 13 commits into
masterfrom
feat/lwk

Conversation

@jackstar12

@jackstar12 jackstar12 commented Jul 11, 2025

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • New Features

    • Added support for Liquid wallets with full lifecycle: creation, management, synchronization, address generation, transaction creation, sending, and fee estimation.
    • Integrated a Rust-based library for Liquid wallet functionality with build and binding generation support.
    • Extended wallet interface with output retrieval and synchronization methods.
    • Implemented automatic UTXO consolidation for Liquid wallets.
    • Added refined handling of direct payments for reverse swaps with improved output selection.
  • Improvements

    • Updated database schema to track legacy wallets and address indices.
    • Enhanced wallet import, unlock, and synchronization flows including Liquid wallet backend initialization.
    • Improved fee estimation and transaction handling across wallet types.
    • Deprecated subaccount-related gRPC fields and RPC methods.
    • Refined wallet retrieval methods for clearer error handling and type safety.
  • Bug Fixes

    • Improved error handling and test coverage for wallet operations.
    • Fixed test race conditions and streamlined test wallet funding logic.
    • Adjusted reverse swap tests and removed obsolete subtests.
  • Documentation

    • Updated gRPC documentation to mark subaccount fields and messages as deprecated.
  • Chores

    • Added new Git submodule for Rust Liquid wallet library and updated build, test, and CI configurations accordingly.
    • Updated .gitignore and linter configuration to exclude new components.
    • Added static and dynamic linking support for Rust library in Liquid wallet integration.
    • Enhanced CI workflows with recursive submodule checkout and updated action versions.

* feat: lwk server integration

* feat: lwk `GetOutputs` implementation for MRHs

* refactor: improve wallet tests

* chore: .gitignore

* chore: deprecate subaccount apis

* feat: electrum backend

* refactor: `getOwnWallet` -> `getGdkWallet`

* fix: dont select subaccounts in cli wallet import

not needed anymore

* feat: `TxProvider` in wallet backend

important to not broadcast via boltz

* feat: lwk index persister

* refactor: move full scan of wallets into seperate `Sync` function

this way, we can create the wallet inside of a transaction with the
other db operations and have the heavier sync not block the db

* feat: transactions pagination

* docs: clarify persister comments

* fix: correct insufficient funds error

want to be consistent with gdk

* fix: encrypt wallets after logging in

* feat: derive default descriptor before creating wallet in db

users will see descriptor in credentials of hot wallets this way

* fix: use `GetSendFee` from wallet interface in `checkBalance`

* feat: implement `transaction.direct` status update (#497)

* feat: implement `transaction.direct` status update

* refactor: dont set mrh for external claim addresses

this simplifies the handling of direct payments and gets rid of the
problem where we cant verify the value of liquid transactions made to
these transactions

* fix: flaky gdk bump test

* fix: remove manual GC run

debugging leftover
* feat: lwk esplora concurrency

* refactor: remove manual destroy calls

leave that to go GC
* feat: spent txo map in lwk

important to not double spend between syncs

* refactor: lwk test package

move to `liquid_wallet` package so we can access the spenOutputs in
tests
* fix: direct tx tests for btc

* chore: skip flaky gdk bump test
@coderabbitai

coderabbitai Bot commented Jul 11, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This change introduces a new Liquid wallet implementation, integrating it into the system with support for wallet management, transaction creation, synchronization, and blockchain interactions. It adds backend configuration, database schema extensions, new Makefile targets, test coverage, and updates to RPC and gRPC interfaces to support Liquid wallets and deprecate subaccount features.

Changes

Files/Group Change Summary
.github/workflows/test.yml, .github/workflows/artifacts.yml, .github/workflows/release.yml, .gitmodules, .golangci.yml, rust-toolchain.toml, lwk Add Liquid wallet submodule, enable recursive submodule checkout in workflows, update linter config, comment Rust toolchain version, add lwk submodule reference.
.gitignore Ignore new Liquid wallet test data directory internal/rpcserver/test/liquid-wallet/.
Makefile Add build and binding targets for Rust-based Liquid wallet library (lwk), update regtest setup and test targets, reorganize dependencies.
cmd/boltzcli/commands.go Remove subaccount selection after wallet import; add //nolint:staticcheck directive for printSubaccount.
docs/grpc.md, pkg/boltzrpc/boltzrpc.proto Mark subaccount fields, messages, and RPC methods as deprecated in documentation and proto definitions.
internal/cln/cln.go, internal/lnd/lnd.go Add Sync() and GetOutputs(address string) methods to Lightning node structs returning unsupported errors or no-op.
internal/database/database.go, internal/database/migration.go Extend wallets table with legacy (bool) and lastIndex (int) columns; add migration logic for version 16.
internal/database/wallet.go Add WalletPersister implementing Persister interface; support LastIndex field in Wallet, update create and parse logic.
internal/mocks/lightning/LightningNode_mock.go, internal/mocks/onchain/Wallet_mock.go Add mocks for new Sync() and GetOutputs() methods with expectation helpers.
internal/nursery/nursery.go, internal/nursery/refund.go, internal/nursery/reverse.go, internal/nursery/nursery_test.go Refactor reverse swap direct payment handling to support transaction.direct status, add output selection logic, fee estimation method, remove unused parameters, and add related tests.
internal/onchain/liquid-wallet/lwk/dynamic.go, internal/onchain/liquid-wallet/lwk/static.go Add cgo build files for dynamic and static linking of the lwk Rust library.
internal/onchain/liquid-wallet/wallet.go, internal/onchain/liquid-wallet/wallet_test.go Implement new Liquid wallet with backend, synchronization, transaction creation, address management, and tests covering wallet functionality and consolidation.
internal/onchain/wallet.go, internal/onchain/wallet/wallet.go, internal/onchain/wallet/wallet_test.go Extend wallet interface with GetOutputs and Sync methods; update transaction limits and tests accordingly.
internal/rpcserver/router.go, internal/rpcserver/server.go, internal/rpcserver/serializer.go Integrate Liquid wallet backend; refactor wallet import and login to support Liquid wallets; update RPC methods to handle various wallet types; add //nolint:staticcheck annotations.
internal/rpcserver/rpcserver_test.go Update test helpers for wallet import and funding; add test for legacy wallet support; adjust reverse swap test logic.
internal/test/test.go Add FundWallet helper function; assign wallet ID constant; simplify wallet funding logic in tests.
pkg/boltzrpc/client/client.go Add //nolint:staticcheck directives to deprecated client methods SetSubaccount and GetSubaccounts.
internal/onchain/onchain.go Add FeeProvider interface; embed it in BlockProvider interface, removing direct fee estimation from BlockProvider.
internal/autoswap/chain.go Reorder import statements (no functional changes).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant RPCServer
    participant DB
    participant LiquidBackend
    participant LiquidWallet

    User->>RPCServer: ImportWallet(credentials)
    RPCServer->>DB: Begin transaction
    alt Liquid wallet and no descriptor
        RPCServer->>LiquidWallet: DeriveDefaultDescriptor
    end
    RPCServer->>DB: CreateWallet (with lastIndex)
    RPCServer->>LiquidBackend: NewWallet(credentials)
    LiquidBackend->>LiquidWallet: Initialize
    LiquidWallet-->>LiquidBackend: Wallet instance
    RPCServer->>LiquidWallet: Sync()
    RPCServer->>DB: Commit transaction
    RPCServer-->>User: Success
Loading
sequenceDiagram
    participant LiquidWallet
    participant BlockchainBackend
    participant Esplora/Electrum
    participant DB

    loop Sync Loop
        LiquidWallet->>BlockchainBackend: Query UTXOs, transactions
        BlockchainBackend->>Esplora/Electrum: Fetch data
        Esplora/Electrum-->>BlockchainBackend: Data
        BlockchainBackend-->>LiquidWallet: Data
        LiquidWallet->>DB: Persist lastIndex if needed
        alt UTXO count > threshold
            LiquidWallet->>BlockchainBackend: Consolidate UTXOs
        end
    end
Loading

Possibly related PRs

  • BoltzExchange/boltz-client#451: Adds and integrates the "lwk" Liquid wallet implementation, including workflow, Makefile, and internal package changes—overlaps directly with this PR.
  • BoltzExchange/boltz-client#497: Refactors reverse swap direct payment handling, including output selection and method changes, matching changes in this PR.
  • BoltzExchange/boltz-client#462: Extends Liquid wallet implementation with GetOutputs method and refactors backend client interface, closely related to this PR.

Suggested reviewers

  • michael1011
  • kilrau

Poem

A wallet for Liquid, so shiny and new,
With bindings and backends and tests to review.
Migrations and mocks, a Makefile delight,
Now coins can be managed both morning and night.
The subaccounts fade, but new features hop in—
🐰 Cheers for the Liquid leap, let the swaps begin!

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🔭 Outside diff range comments (1)
internal/rpcserver/router.go (1)

1394-1444: Consider handling sync errors more gracefully.

The wallet import succeeds even if the sync fails at line 1443. This could leave the wallet in an inconsistent state where it's imported but not properly synchronized.

Consider either:

  1. Making sync errors non-fatal with a warning
  2. Providing a way to retry sync later
  3. Adding a wallet state to indicate sync status
-	// TODO: maybe allow returning without sync here
-	return imported.Sync()
+	// TODO: maybe allow returning without sync here
+	if err := imported.Sync(); err != nil {
+		logger.Warnf("Initial wallet sync failed for %s: %v", imported.GetWalletInfo().Name, err)
+		// Wallet is still usable, sync will retry in background
+	}
+	return nil
🧹 Nitpick comments (13)
.gitignore (1)

30-31: Use a consistent directory-ignore pattern

Line 30 ignores internal/rpcserver/test/wallet without a trailing slash, while the new entry on line 31 adds the slash. Functionally both work, but mixing styles is noisy and may confuse future edits. Pick one convention (directory paths ending with / is common in this file) and apply it to both lines.

-internal/rpcserver/test/wallet
-internal/rpcserver/test/liquid-wallet/
+internal/rpcserver/test/wallet/
+internal/rpcserver/test/liquid-wallet/
lwk (1)

1-1: Verify lwk’s license compatibility

Pulling in a new submodule adds transitive license obligations.
Confirm that lwk’s license terms are compatible with the BoltzExchange/boltz-client license before merging.

internal/rpcserver/serializer.go (1)

231-239: Add explicit TODO for later removal of deprecated Subaccount code

//nolint:staticcheck silences the “deprecated” warning, but nothing in the code signals future removal.
Add a short TODO so the team tracks technical-debt clean-up once subaccounts are fully removed.

-//nolint:staticcheck
+//nolint:staticcheck // TODO(boltz#451): drop once subaccounts are removed from the API
.gitmodules (1)

11-14: Tracking main breaks reproducible builds

Submodules should normally be pinned to a commit hash.
Using branch = main means every git submodule update pulls the latest commit, making CI/debugging nondeterministic and potentially introducing breaking changes without a version bump.

Recommend:

-[submodule "lwk"]
-  path = lwk
-  url = https://github.com/Blockstream/lwk
-  branch = main
+[submodule "lwk"]
+  path = lwk
+  url = https://github.com/Blockstream/lwk
+  # Pinned to <commit-hash>; update manually when bumping lwk
+  # branch = main
internal/onchain/wallet/wallet_test.go (1)

31-33: Test stability improvement with race condition mitigation.

The addition of t.SkipNow() and the 10-second sleep addresses race conditions related to transaction confirmation timing. However, consider using require.Eventually instead of a fixed sleep for more robust and potentially faster test execution.

Consider replacing the fixed sleep with a more robust waiting mechanism:

-t.SkipNow()
-// we sleep here to avoid a race where tx doesn't get confirmed by a block we mined, causing the tx to be confirmed
-time.Sleep(10 * time.Second)
+t.SkipNow()
+// Wait for transaction to be in a suitable state for fee bumping
+require.Eventually(t, func() bool {
+    // Add appropriate condition check here
+    return true
+}, 15*time.Second, 250*time.Millisecond)
internal/rpcserver/rpcserver_test.go (1)

1896-1896: Consider replacing sleep with proper synchronization.

Using time.Sleep(1 * time.Second) can make tests flaky and slow. Consider using a more deterministic approach like waiting for a specific event or state change.

-			time.Sleep(1 * time.Second)
+			// Wait for any pending operations to complete
+			require.Eventually(t, func() bool {
+				// Check for expected state or use a synchronization primitive
+				return true // Replace with actual condition
+			}, 2*time.Second, 100*time.Millisecond)
internal/onchain/liquid-wallet/wallet_test.go (4)

69-73: Consider making timeouts configurable for CI environments.

The timeout of 10*syncInterval might be insufficient in slow CI environments. Consider making this configurable or more generous.

+	const testTimeout = 30 * time.Second
	require.Eventually(t, func() bool {
		balance, err = wallet.GetBalance()
		require.NoError(t, err)
		return balance.Total == amount && balance.Unconfirmed == amount
-	}, 10*syncInterval, syncInterval)
+	}, testTimeout, syncInterval)

126-129: Address the TODO comment.

There's a TODO indicating that output address verification is not working correctly. This should be tracked and fixed.

Would you like me to open an issue to track this TODO for fixing the transaction output verification?


168-169: Consider extracting test transaction data.

The someTx constant is a very long hex string. Consider moving it to a test fixture file or generating it programmatically for better maintainability.

Also applies to: 265-265


241-241: Add delay explanation.

Consider adding a comment explaining why the 100ms delay is needed between transactions.

+		// Small delay to ensure transactions are processed in order
		time.Sleep(100 * time.Millisecond)
internal/onchain/liquid-wallet/wallet.go (2)

296-299: Consider more accurate Ready() implementation.

The Ready() method always returns true, but the comment mentions waiting for sync during initialization. This could be misleading if:

  1. Initial sync fails
  2. Wallet loses connection to backend

Consider tracking sync status and returning false if the wallet is not properly synced or connected.

Also, BumpTransactionFee should document that it's not yet implemented for Liquid wallets.

Also applies to: 307-309


459-462: Verify typed error support for insufficient funds
Our search didn’t find any InsufficientFunds error types or constants in the lwk library, so the current string‐based check remains the only way to detect this condition and is fragile if the library’s message changes. Please manually confirm whether lwk exposes a dedicated error enum or code for insufficient funds. If it does, update the check to use that typed error; otherwise, consider requesting lwk to add one and document the existing string match with a clear comment.

• Location: internal/onchain/liquid-wallet/wallet.go Lines 459–462

internal/nursery/reverse.go (1)

300-313: Add debug logging for rejected outputs.

Consider adding debug logging to help troubleshoot why outputs are rejected, especially for amount validation failures.

 		if output.Value < swap.OnchainAmount {
 			if err := boltz.CheckAmounts(
 				boltz.ReverseSwap,
 				swap.Pair,
 				swap.InvoiceAmount,
 				output.Value,
 				swap.ServiceFeePercent,
 				feeEstimations,
 				true,
 			); err != nil {
 				logger.Infof("Output from %s does not pass amount checks: %s", output.TxId, err)
+				logger.Debugf("Output value: %d, expected: %d", output.Value, swap.OnchainAmount)
 				continue
 			}
 		}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c829fa8 and c141c8f.

⛔ Files ignored due to path filters (5)
  • pkg/boltzrpc/autoswaprpc/autoswaprpc.pb.go is excluded by !**/*.pb.go
  • pkg/boltzrpc/autoswaprpc/autoswaprpc_grpc.pb.go is excluded by !**/*.pb.go
  • pkg/boltzrpc/boltzrpc.pb.go is excluded by !**/*.pb.go
  • pkg/boltzrpc/boltzrpc.pb.gw.go is excluded by !**/*.pb.gw.go
  • pkg/boltzrpc/boltzrpc_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (34)
  • .github/workflows/test.yml (3 hunks)
  • .gitignore (1 hunks)
  • .gitmodules (1 hunks)
  • .golangci.yml (1 hunks)
  • Makefile (3 hunks)
  • cmd/boltzcli/commands.go (2 hunks)
  • docs/grpc.md (1 hunks)
  • internal/cln/cln.go (2 hunks)
  • internal/database/database.go (1 hunks)
  • internal/database/migration.go (2 hunks)
  • internal/database/wallet.go (4 hunks)
  • internal/lnd/lnd.go (2 hunks)
  • internal/mocks/lightning/LightningNode_mock.go (2 hunks)
  • internal/mocks/onchain/Wallet_mock.go (2 hunks)
  • internal/nursery/nursery.go (2 hunks)
  • internal/nursery/nursery_test.go (2 hunks)
  • internal/nursery/refund.go (2 hunks)
  • internal/nursery/reverse.go (5 hunks)
  • internal/onchain/liquid-wallet/lwk/dynamic.go (1 hunks)
  • internal/onchain/liquid-wallet/lwk/static.go (1 hunks)
  • internal/onchain/liquid-wallet/wallet.go (1 hunks)
  • internal/onchain/liquid-wallet/wallet_test.go (1 hunks)
  • internal/onchain/wallet.go (3 hunks)
  • internal/onchain/wallet/wallet.go (4 hunks)
  • internal/onchain/wallet/wallet_test.go (2 hunks)
  • internal/rpcserver/router.go (18 hunks)
  • internal/rpcserver/rpcserver_test.go (8 hunks)
  • internal/rpcserver/serializer.go (1 hunks)
  • internal/rpcserver/server.go (2 hunks)
  • internal/test/test.go (3 hunks)
  • lwk (1 hunks)
  • pkg/boltzrpc/boltzrpc.proto (4 hunks)
  • pkg/boltzrpc/client/client.go (1 hunks)
  • rust-toolchain.toml (1 hunks)
🧰 Additional context used
🧠 Learnings (14)
📓 Common learnings
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.421Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
pkg/boltzrpc/client/client.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
.gitignore (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
internal/nursery/nursery_test.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
internal/onchain/wallet.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.421Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
internal/nursery/refund.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
.github/workflows/test.yml (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#451
File: .github/workflows/test.yml:33-36
Timestamp: 2025-06-02T18:32:38.175Z
Learning: The actions-rust-lang/setup-rust-toolchain GitHub Action has caching enabled by default (cache: true), and the cache-workspaces parameter works independently to specify workspace paths for caching without requiring explicit cache: cargo configuration.
internal/rpcserver/server.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
internal/rpcserver/rpcserver_test.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
internal/onchain/liquid-wallet/wallet_test.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.421Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
internal/mocks/onchain/Wallet_mock.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.421Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
internal/nursery/reverse.go (2)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.421Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
internal/rpcserver/router.go (2)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.421Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
internal/onchain/liquid-wallet/wallet.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.421Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
🧬 Code Graph Analysis (10)
pkg/boltzrpc/client/client.go (2)
internal/onchain/wallet/wallet.go (1)
  • Subaccount (52-58)
pkg/boltzrpc/boltzrpc.pb.go (6)
  • Subaccount (5993-6002)
  • Subaccount (6017-6017)
  • Subaccount (6032-6034)
  • SetSubaccountRequest (4619-4627)
  • SetSubaccountRequest (4642-4642)
  • SetSubaccountRequest (4657-4659)
internal/nursery/nursery_test.go (4)
internal/test/test.go (1)
  • InitLogger (143-145)
internal/database/reverse.go (1)
  • ReverseSwap (17-49)
pkg/boltz/fees.go (1)
  • FeeEstimations (31-31)
pkg/boltz/currency.go (2)
  • CurrencyBtc (11-11)
  • CurrencyLiquid (12-12)
internal/cln/cln.go (2)
internal/onchain/onchain.go (1)
  • Output (33-36)
internal/lightning/lightning.go (1)
  • ErrUnsupported (28-28)
cmd/boltzcli/commands.go (3)
internal/utils/utils.go (1)
  • Satoshis (16-18)
internal/onchain/wallet.go (1)
  • Balance (13-17)
pkg/boltzrpc/boltzrpc.pb.go (3)
  • Balance (5929-5937)
  • Balance (5952-5952)
  • Balance (5967-5969)
internal/onchain/wallet/wallet_test.go (1)
internal/onchain/wallet/wallet.go (1)
  • MaxInputs (36-36)
internal/onchain/wallet.go (1)
internal/onchain/onchain.go (1)
  • Output (33-36)
internal/onchain/wallet/wallet.go (1)
internal/onchain/wallet.go (2)
  • Wallet (49-61)
  • DefaultTransactionsLimit (11-11)
internal/lnd/lnd.go (2)
internal/onchain/onchain.go (1)
  • Output (33-36)
internal/lightning/lightning.go (1)
  • ErrUnsupported (28-28)
internal/mocks/lightning/LightningNode_mock.go (2)
internal/onchain/onchain.go (1)
  • Output (33-36)
internal/nursery/swap.go (1)
  • Output (32-39)
internal/mocks/onchain/Wallet_mock.go (2)
internal/onchain/onchain.go (1)
  • Output (33-36)
internal/nursery/swap.go (1)
  • Output (32-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
🔇 Additional comments (61)
rust-toolchain.toml (1)

2-2: Comment LGTM, but keep version drift in mind

Locking to 1.81.0 is sensible for cache-hits; just remember to bump when lwk moves to a newer compiler.
No code changes needed.

internal/database/database.go (1)

221-222: It looks like the migration for legacy and lastIndex is already implemented in internal/database/migration.go:

ALTER TABLE wallets ADD COLUMN legacy BOOLEAN DEFAULT FALSE;
ALTER TABLE wallets ADD COLUMN lastIndex INT;
UPDATE wallets SET legacy = TRUE;

No “version 16” bump is required here—this file uses a self-contained migration sequence rather than discrete numbered versions. All existing wallets will be marked as legacy=true, preserving the intended backward-compatibility.

All set!

docs/grpc.md (1)

1514-1514: LGTM! Proper deprecation marking aligns with subaccount removal.

The documentation correctly marks the subaccount field as deprecated, which aligns with the coordinated deprecation of subaccount functionality across the codebase mentioned in the AI summary.

cmd/boltzcli/commands.go (2)

1832-1832: LGTM - Appropriate use of linter directive for deprecated function.

The staticcheck linter directive is correctly applied to suppress warnings for the printSubaccount function, which aligns with the deprecation of subaccount functionality mentioned in the PR objectives.


1912-1912: LGTM - Simplified wallet import flow.

The removal of selectSubaccount call and always printing the total balance aligns with the deprecation of subaccount functionality. The change provides consistent user feedback regardless of wallet type.

internal/onchain/liquid-wallet/lwk/static.go (1)

1-8: LGTM - Correct CGO static linking implementation.

The build constraints and linker flags are properly configured for static linking:

  • Build tag static && !dynamic correctly excludes dynamic builds
  • Static library path ${SRCDIR}/liblwk.a is appropriate for build-time linking
  • Linker flags -Wl,--no-as-needed -ldl ensure proper static linking with dynamic loading support
.github/workflows/test.yml (3)

23-24: LGTM - Required for lwk submodule integration.

The addition of submodules: recursive is necessary to properly checkout the new lwk Git submodule introduced in this PR.


34-36: LGTM - Appropriate cache expansion for new Rust workspace.

The addition of ./lwk to the cache-workspaces list is correct for the new lwk Rust library. This will improve CI performance by caching Rust dependencies for both the existing bolt12 and new lwk workspaces.


47-47: LGTM - Simplified regtest startup aligns with Makefile refactoring.

The removal of make download-regtest is consistent with the Makefile refactoring that replaced it with setup-regtest and integrated the functionality into the startup process.

internal/onchain/liquid-wallet/lwk/dynamic.go (1)

1-9: LGTM - Correct CGO dynamic linking implementation.

The build constraints and linker flags are properly configured for dynamic linking:

  • Build tag dynamic || !static correctly includes dynamic builds and default builds
  • Dynamic library linking -llwk with search path -L${SRCDIR} is appropriate
  • Runtime path -Wl,-rpath=${SRCDIR} ensures library can be found at runtime
internal/lnd/lnd.go (2)

77-79: LGTM - Appropriate no-op implementation for interface compliance.

The Sync() method returning nil is correct for LND since synchronization is handled internally by the LND node. This aligns with the wallet interface standardization across implementations.


404-406: LGTM - Consistent unsupported method implementation.

The GetOutputs() method correctly returns lightning.ErrUnsupported, which is consistent with other unsupported methods in the LND implementation like GetTransactions() and BumpTransactionFee().

internal/onchain/wallet.go (3)

11-11: LGTM: Well-defined constant for transaction limits.

The constant name and value are appropriate for pagination purposes.


59-61: LGTM: Interface extension follows consistent patterns.

The new methods GetOutputs and Sync are well-defined and follow the existing interface patterns. The GetOutputs method properly returns a slice of pointers to Output structs, and both methods have appropriate error handling.


78-78: LGTM: Simple and clear field addition.

The Legacy boolean field is a straightforward addition to the struct that aligns with the wallet credential requirements.

internal/cln/cln.go (2)

102-104: LGTM: Appropriate no-op implementation for Lightning.

The Sync() method correctly returns nil as a no-op, which is appropriate for CLN since Lightning nodes don't require explicit synchronization like onchain wallets.


358-362: LGTM: Correct unsupported operation handling.

The GetOutputs method appropriately returns lightning.ErrUnsupported since CLN is a Lightning implementation that doesn't manage individual UTXOs. The method signature correctly matches the interface and the fix for the missing closing brace resolves the syntax issue.

internal/nursery/refund.go (1)

35-35: LGTM: Method call simplified correctly.

The removal of the empty string parameter from checkExternalReverseSwaps aligns with the method signature change mentioned in the AI summary, making the call cleaner and more focused.

internal/nursery/nursery_test.go (1)

111-221: LGTM: Comprehensive test coverage for direct output selection.

The test function provides excellent coverage with well-structured table-driven tests covering three important scenarios:

  1. Match: Successfully finding a suitable output within the expected range
  2. NoMatch: Handling cases where no suitable output exists
  3. Overpaid: Handling overpaid scenarios gracefully

The test data is well-constructed with realistic ReverseSwap configurations, fee estimations, and output values. The use of require.Eventually pattern would be beneficial here if the tested method involves any asynchronous operations, but the current synchronous assertion approach is appropriate for this logic.

internal/onchain/wallet/wallet_test.go (1)

262-275: LGTM: Improved test reliability with require.Eventually.

The replacement of the manual timeout loop with require.Eventually is an excellent improvement. This approach:

  • Eliminates the need for explicit channel management
  • Provides cleaner error reporting
  • Uses consistent polling intervals
  • Reduces complexity in the test logic

The 15-second timeout with 250ms intervals is appropriate for blockchain-related operations.

internal/database/migration.go (3)

24-24: LGTM on schema version increment.

The increment from 15 to 16 is correct for the new migration.


591-592: Good addition of logging for consistency.

Adding the logMigration(oldVersion) call to case 14 maintains consistency with other migration cases.


598-608: Migration columns look safe—confirm legacy column is read by your queries

We’ve verified that:

  • lastIndex is represented as sql.NullInt64 in internal/database/wallet.go, and all codepaths check .Valid before using the value.
  • The DDL sets legacy BOOLEAN DEFAULT FALSE and immediately flips existing rows to TRUE, so old wallets are correctly marked while new ones default to FALSE.

Please double-check that your data-access layer actually selects and scans the new legacy column so it’s surfaced in your Wallet structs. In particular, review:

  • internal/database/wallet.go – ensure your SELECT clauses and Scan(...) calls include the legacy field.
  • internal/rpcserver (or wherever you map DB rows to RPC models) – confirm the Legacy flag is being loaded and returned.
internal/onchain/wallet/wallet.go (3)

770-773: Good refactoring to use shared constant.

Replacing the local GetTransactionsMaxLimit with onchain.DefaultTransactionsLimit improves consistency across the codebase.


858-858: Consistent usage of shared constant in BumpTransactionFee.

The replacement of hardcoded limits with onchain.DefaultTransactionsLimit is consistent with the interface changes.

Also applies to: 868-868, 871-871


509-511: Appropriate no-op implementation of Sync method.

The no-op implementation is appropriate for this wallet type and satisfies the interface requirement. The method can be enhanced in the future if synchronization logic is needed.

internal/test/test.go (3)

50-50: Good addition of WalletId constant.

Adding the WalletId constant and using it in the WalletCredentials function provides consistency across tests.

Also applies to: 59-59


66-105: Well-implemented FundWallet function.

The FundWallet function is well-structured with:

  • Early return if wallet already has funds
  • Proper error handling
  • Reasonable timeout (15 seconds)
  • Clean separation of concerns

The function improves code reusability and maintainability.


135-137: Good refactoring of InitTestWallet.

Replacing the inline funding logic with the FundWallet function call improves code organization and reusability.

internal/nursery/nursery.go (3)

252-252: Appropriate parameter removal.

Removing the TxId parameter from the checkExternalReverseSwaps call simplifies the interface and aligns with the refactoring mentioned in the AI summary.


368-380: Well-implemented fee estimation method.

The GetFeeEstimations method is well-structured with:

  • Clear separation of concerns
  • Proper error handling for both currencies
  • Clean return of fee estimations map
  • Appropriate error propagation

This consolidates fee estimation logic effectively.


382-388: Good refactoring of CheckAmounts method.

The refactoring to use the new GetFeeEstimations method improves code organization while maintaining the same functionality. Error handling is preserved correctly.

internal/rpcserver/server.go (2)

19-19: Appropriate imports for Liquid wallet backend.

The addition of database and liquid_wallet imports are necessary for the new backend initialization.

Also applies to: 23-23


210-223: Well-implemented Liquid wallet backend initialization.

The backend initialization is well-structured with:

  • Proper configuration setup using server's network and data directory
  • Appropriate use of existing onchain Liquid transaction provider
  • Database-backed wallet persister
  • Conditional Electrum configuration based on availability
  • Proper error handling and propagation

The initialization follows established patterns and integrates well with the existing server architecture.

pkg/boltzrpc/boltzrpc.proto (1)

108-108: Consistent deprecation of subaccount functionality.

The deprecation of subaccount-related RPCs, fields, and messages is properly implemented following protobuf conventions. This maintains backward compatibility while signaling to users that these features should no longer be used.

Also applies to: 113-113, 722-722, 747-747, 754-754, 759-759, 900-900

internal/rpcserver/rpcserver_test.go (4)

20-21: LGTM! Clean import additions.

The new imports for liquid wallet packages are properly organized and necessary for the new wallet implementation and serialization.

Also applies to: 34-34


1161-1186: Improved wallet retrieval and funding logic.

The refactored fundedWallet function now properly checks for existing wallets before importing, avoiding duplicate imports. The funding logic with require.Eventually is more robust than the previous implementation.


1740-1762: Test updates align with new wallet implementation.

The test correctly validates both mnemonic and the new CoreDescriptor field after wallet creation. The removal of the deprecated SetSubaccount call is appropriate.


3134-3171: Excellent backward compatibility test.

The test properly validates that wallets can switch between the new Liquid wallet implementation and the legacy implementation based on the database flag. This ensures smooth migration paths for existing users.

Makefile (3)

50-55: Proper Rust-Go binding generation setup.

The use of uniffi-bindgen-go with a specific version tag ensures reproducible builds. The conditional installation of the tool is a good practice.


96-101: Efficient conditional build for lwk library.

The build target properly checks for the existence of the library before building, preventing unnecessary rebuilds. Copying both .a and .so files ensures compatibility with different linking scenarios.


68-86: Improved test isolation with wallet data cleanup.

The addition of clear-wallet-data ensures clean test runs by removing any persisted wallet data. This prevents test pollution and makes tests more reliable.

internal/database/wallet.go (2)

10-41: Well-structured persistence layer for Liquid wallet.

The WalletPersister provides a clean implementation of the liquid_wallet.Persister interface with proper error handling and null value support for the lastIndex field.


84-106: Robust null handling for optional LastIndex field.

The use of sql.NullInt64 properly handles nullable database columns, and the error wrapping provides better context for debugging.

internal/mocks/onchain/Wallet_mock.go (1)

204-264: LGTM!

The auto-generated mock methods for GetOutputs and Sync follow the established patterns correctly.

Also applies to: 601-643

internal/mocks/lightning/LightningNode_mock.go (1)

446-506: LGTM!

The auto-generated mock methods for GetOutputs and Sync are correctly implemented.

Also applies to: 1130-1172

internal/onchain/liquid-wallet/wallet_test.go (5)

22-48: LGTM! Well-structured test helpers.

The helper functions provide clean test setup with in-memory database and proper configuration.


116-119: Good test coverage for spent outputs tracking.

The test properly verifies that spent outputs are tracked and cleared after sync, preventing double-spending.

Also applies to: 149-156


189-201: LGTM! Clean wallet creation helper.

The helper function properly handles credential generation and wallet initialization.


203-225: Excellent test for address generation and persistence.

The test thoroughly verifies address uniqueness and proper index persistence.


245-261: Well-designed consolidation test.

The test effectively verifies automatic UTXO consolidation behavior. The balance verification logic correctly accounts for the consolidation transaction.

internal/rpcserver/router.go (5)

72-72: LGTM!

The addition of liquidBackend field to the server struct is appropriate for managing Liquid wallet operations.


1042-1050: LGTM!

The condition correctly ensures both claimAddress and walletId are present before generating the address signature, which is necessary to prove ownership of the claim address.


1751-1776: LGTM!

The fee estimation logic is properly updated to be currency-aware, correctly using the wallet's currency for fee rate estimation and dummy address selection.


2396-2401: LGTM!

The loginWallet method correctly routes between liquid wallet and GDK wallet implementations based on currency and legacy flag.


2029-2036: Good error handling for wallet sync during unlock.

The implementation correctly handles sync failures by logging errors without preventing the wallet from being added. This allows the wallet to be used even if initial sync fails, with the sync loop retrying later.

internal/onchain/liquid-wallet/wallet.go (5)

20-37: LGTM!

The Persister interface and struct definitions are well-designed. The Wallet struct properly includes synchronization primitives and thread-safe spent outputs tracking.


77-131: LGTM!

The NewBlockchainBackend function has proper validation, sensible defaults, and good error handling. The network-specific configuration is well-structured.


226-240: Excellent implementation of the sync loop!

The random jitter (0.75-1.25x interval) is a thoughtful addition to prevent traffic spikes when multiple wallets sync simultaneously. The error handling appropriately logs failures without crashing the sync loop.


413-501: Excellent implementation of spent output tracking!

The caller-controlled locking pattern is correctly implemented:

  • SendToAddress holds write lock throughout the entire operation
  • GetSendFee uses read lock for concurrent access
  • createTransaction operates under the caller's lock protection

This ensures atomicity and prevents double-spending of UTXOs.


336-350: LGTM!

The address generation correctly ensures uniqueness by persisting the incremented index immediately after generating the address. This prevents address reuse even in case of crashes.

Comment thread lwk Outdated
@@ -0,0 +1 @@
Subproject commit 3a6b16400e9b7bf08f48cbff7ed9bfa53db0eb99

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Pin to a signed release tag & track drift with CI

The submodule currently points to an arbitrary commit.
For easier provenance auditing and CVE tracking, consider moving to a signed release tag (e.g. lwk-vX.Y.Z) and add a CI check that fails when the pointer drifts.


🏁 Script executed:

#!/bin/bash
# Fails if the chosen commit does not correspond to any tag.
git ls-remote --tags https://github.com/Blockstream/lwk.git \
  | grep 3a6b16400e9b7bf08f48cbff7ed9bfa53db0eb99 \
  || { echo "Commit is untagged – confirm this is intentional."; exit 1; }

Length of output: 227


Pin the lwk submodule to a signed release tag & enforce drift detection

The current submodule pointer (commit 3a6b16400e9b7bf08f48cbff7ed9bfa53db0eb99) is untagged, making provenance auditing and CVE tracking harder.

• Location:
– File: lwk (submodule declaration at line 1)

• Action items:

  1. Update your submodule reference to use a signed release tag, for example:
    [submodule "lwk"]
      path = lwk
      url  = https://github.com/Blockstream/lwk.git
      tag  = lwk-vX.Y.Z
  2. Add a CI check that fails if the checked‐out commit drifts from any signed tag, for example:
    # Fail if the current submodule commit isn’t tagged
    git ls-remote --tags https://github.com/Blockstream/lwk.git \
      | grep -q "$(git submodule status lwk | awk '{print $1}')" \
      || { echo "❌ lwk submodule is not at a signed tag"; exit 1; }

This ensures clear, auditable provenance and automatic drift detection in CI.

🤖 Prompt for AI Agents
In the lwk file at line 1, the submodule is currently pinned to an untagged
commit, which complicates provenance auditing and CVE tracking. Update the
submodule declaration to reference a signed release tag by adding a 'tag =
lwk-vX.Y.Z' line under the lwk submodule section. Additionally, implement a CI
check script that verifies the checked-out submodule commit matches a signed tag
by using git ls-remote and grep commands, and fail the build if it does not,
ensuring automatic drift detection.

Comment thread .golangci.yml
Comment on lines +118 to 126
//nolint:staticcheck
func (boltz *Boltz) SetSubaccount(walletId uint64, subaccount *uint64) (*boltzrpc.Subaccount, error) {
return boltz.Client.SetSubaccount(boltz.Ctx, &boltzrpc.SetSubaccountRequest{Subaccount: subaccount, WalletId: walletId})
}

//nolint:staticcheck
func (boltz *Boltz) GetSubaccounts(walletId uint64) (*boltzrpc.GetSubaccountsResponse, error) {
return boltz.Client.GetSubaccounts(boltz.Ctx, &boltzrpc.GetSubaccountsRequest{WalletId: walletId})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Mark methods as deprecated and hide from GoDoc

You suppressed staticcheck, but callers (and IDEs) still see these methods as first-class.
Add Go doc // Deprecated: prefix so users migrate away without relying on linters.

-//nolint:staticcheck
-func (boltz *Boltz) SetSubaccount(...){
+// Deprecated: Subaccounts are being removed; prefer GetWallet / CreateWallet.
+//nolint:staticcheck
+func (boltz *Boltz) SetSubaccount(...){

Apply the same to GetSubaccounts.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//nolint:staticcheck
func (boltz *Boltz) SetSubaccount(walletId uint64, subaccount *uint64) (*boltzrpc.Subaccount, error) {
return boltz.Client.SetSubaccount(boltz.Ctx, &boltzrpc.SetSubaccountRequest{Subaccount: subaccount, WalletId: walletId})
}
//nolint:staticcheck
func (boltz *Boltz) GetSubaccounts(walletId uint64) (*boltzrpc.GetSubaccountsResponse, error) {
return boltz.Client.GetSubaccounts(boltz.Ctx, &boltzrpc.GetSubaccountsRequest{WalletId: walletId})
}
// Deprecated: Subaccounts are being removed; prefer GetWallet / CreateWallet.
//nolint:staticcheck
func (boltz *Boltz) SetSubaccount(walletId uint64, subaccount *uint64) (*boltzrpc.Subaccount, error) {
return boltz.Client.SetSubaccount(boltz.Ctx, &boltzrpc.SetSubaccountRequest{Subaccount: subaccount, WalletId: walletId})
}
// Deprecated: Subaccounts are being removed; prefer GetWallet / CreateWallet.
//nolint:staticcheck
func (boltz *Boltz) GetSubaccounts(walletId uint64) (*boltzrpc.GetSubaccountsResponse, error) {
return boltz.Client.GetSubaccounts(boltz.Ctx, &boltzrpc.GetSubaccountsRequest{WalletId: walletId})
}
🤖 Prompt for AI Agents
In pkg/boltzrpc/client/client.go around lines 118 to 126, the SetSubaccount and
GetSubaccounts methods are marked with nolint for staticcheck but lack proper
deprecation comments. Add a Go doc comment starting with "// Deprecated:"
immediately above each method to indicate they are deprecated and guide users to
migrate away. This will make the deprecation visible in GoDoc and IDEs, not just
linters.

Comment thread internal/rpcserver/router.go
Comment thread internal/onchain/liquid-wallet/wallet.go
Comment on lines +186 to +189
msg := strings.ToLower(err.Error())
errors := []string{"transaction already in block chain", "transaction outputs already in utxo set"}
if !slices.Contains(errors, msg) {
handleError("Could not broadcast transaction: " + err.Error())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use error types instead of string comparison for better reliability.

Comparing error strings is fragile and can break if the error messages change. Consider using error types or wrapping these errors in typed errors.

If these are standard errors from the blockchain libraries, check if they expose specific error types. Otherwise, consider creating constants for these error patterns.

🤖 Prompt for AI Agents
In internal/nursery/reverse.go around lines 186 to 189, the code compares error
messages as strings, which is fragile and unreliable. Refactor this by
identifying and using specific error types or error variables provided by the
blockchain libraries instead of string matching. If such types are not
available, define error constants or sentinel errors for these cases and compare
errors using errors.Is or errors.As to improve robustness and maintainability.

Comment on lines +352 to +354
if err := tx.SetReverseSwapClaimTransactionId(swap, output.TxId, 0); err != nil {
return fmt.Errorf("set paid at: %s", err)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix error message to match the operation.

The error message says "set paid at" but the operation is setting the claim transaction ID.

 		if err := tx.SetReverseSwapClaimTransactionId(swap, output.TxId, 0); err != nil {
-			return fmt.Errorf("set paid at: %s", err)
+			return fmt.Errorf("set claim transaction id: %s", err)
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if err := tx.SetReverseSwapClaimTransactionId(swap, output.TxId, 0); err != nil {
return fmt.Errorf("set paid at: %s", err)
}
if err := tx.SetReverseSwapClaimTransactionId(swap, output.TxId, 0); err != nil {
return fmt.Errorf("set claim transaction id: %s", err)
}
🤖 Prompt for AI Agents
In internal/nursery/reverse.go around lines 352 to 354, the error message
incorrectly says "set paid at" while the operation is setting the claim
transaction ID. Update the error message in the fmt.Errorf call to accurately
reflect the operation, such as "set reverse swap claim transaction ID" or a
similar descriptive phrase matching the function
SetReverseSwapClaimTransactionId.

@kilrau kilrau self-requested a review July 11, 2025 17:37

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
internal/onchain/liquid-wallet/wallet.go (1)

270-294: Replace hardcoded consolidation fee rate.

The auto-consolidation logic uses a hardcoded fee rate of 0.1 sat/vbyte which is too low and can cause transactions to be stuck during high fee periods.

🧹 Nitpick comments (3)
internal/onchain/liquid-wallet/wallet_test.go (1)

116-116: Avoid accessing private fields directly in tests.

The test accesses the private spentOutputs field directly, which couples the test to implementation details and makes refactoring harder.

Consider adding a public method to the Wallet type for checking spent outputs status in tests, or use the public API to verify the behavior indirectly through balance checks or transaction attempts.

Also applies to: 149-149, 161-161

internal/onchain/liquid-wallet/wallet.go (2)

226-240: Document the randomization factors for sync interval.

The magic numbers used for randomizing sync intervals (0.75 and 0.5) should be documented or extracted as constants.

+// syncJitterMin and syncJitterRange define the randomization bounds for sync intervals
+// to avoid traffic spikes. The actual interval will be between 75% and 125% of the configured interval.
+const (
+	syncJitterMin   = 0.75
+	syncJitterRange = 0.5
+)
+
 func (w *Wallet) syncLoop(ctx context.Context) {
 	for {
 		// avoid traffic spikes if a lot of wallets are using the same backend
-		sleep := time.Duration(float64(w.backend.cfg.SyncInterval) * (0.75 + rand.Float64()*0.5))
+		sleep := time.Duration(float64(w.backend.cfg.SyncInterval) * (syncJitterMin + rand.Float64()*syncJitterRange))

307-309: Document or implement fee bumping functionality.

The BumpTransactionFee method returns "not implemented". Please either:

  1. Add a comment explaining why fee bumping is not supported for Liquid wallets
  2. Add a TODO comment if this is planned for future implementation
  3. Implement the functionality

Would you like me to help implement fee bumping or create an issue to track this?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e37fed2 and beb13e4.

📒 Files selected for processing (2)
  • internal/onchain/liquid-wallet/wallet.go (1 hunks)
  • internal/onchain/liquid-wallet/wallet_test.go (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.431Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
internal/onchain/liquid-wallet/wallet.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.431Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)

Comment thread internal/onchain/liquid-wallet/wallet_test.go Outdated
}, 30*syncInterval, syncInterval)
}

const someTx = "0200000001015a75b8607566159620994af84594a6b6d903aebbcf59d1382771694bf611eb8a0100000000fdffffff030b59cf2adff4bf2e7d3b8a36816d9fe3c675f2ba6d7198c35195d8d4868578f22808f5bee2a15d01413e36374ff5dc21224b230e9d70f247fcc85712f63fd914074d02aed6c7e3d0008a693a7bccaeb3aac96827cd5af5bbfbd75b2c6a549a0635914116001436532949385ba612cb7b9b3a12e2363666c5a1f40a8c5e056e5c43e77c3f3b49642e1a992e8552b74a9635cdf0e37751709314bf0d09b5b6e450adb2b9f95bc86f0d16279a58734f3f2ed8de73e85489f1ad318b912c02d48e551f064e5e9661f6317f354ccd15184c2b6f0dacf9c3160caf5819aadce11600141961af5494492cb063ddd798b6ac73274f5f65440125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a01000000000000001a00009e0000000000024730440220263f44bba67dfd29e45d6fef6e360e9f9605dbb8438e7b7c663c36dd40126436022054b1018aad29ea345bdcdb8de501e9bbc5f15a66851c8241f132cc42f01bd69c012103797c2f42feaf60680bf31ad2b2edc371a545f3cc96f95a900f5f0cf45aad0d8d004301000185ef088f87f452468e84ea5e7b2750c7664d2ed849bb5b9154414780e367c23c25d180eef6dfc4f8aa5a54116a5567b1f6b6c15059959f4db7872c6d0585ed7ffd4e1060330000000000000001856ba700f8aae132c01f52c60e60029e1341dcd575d1c4aa58d509f791697f9b482aa194f5dd647635d23f2dd019adf96762907d4f8b7047e1af12ff82ba5e69c00dedd7583c2b91ba3ec0040ee9fad69ae0bf58a49827056638fa4564a6e7de3f929ed6e93bbcd405b1cf9d967bebde29fd061da7e70be1464a47806add1f103d8ef9eb5c487cb82befdb60e0c620919ada15f6cf4fc443d2191e984a4c164627270af6a81380599285a4ee01c92c8991203e03478563d850bf86f8ff885216f6830d9cc845cfd1710f34fc61994a6c590085a9e73e637a6d00075d7d5ef893ac963d2ab9d21d9104cfd92d96e4818568411f18e9c28a353cd726e27e7e35d98d37240ab3317e5b5c352c9aa9ccecc3806a83008a89eeb255199c546a3132be478c991f07c12c5b386e03150f7bcb302e8ac84a14ee7544b75f78cd730a17df38cb7beb61b4ac716a69946da88bec0fb417eab843fc16758eb38822716f3ee9d0a77df7a78721fc6e4849d279a3aacfc3750ea8630657e16163e00ba1e26d60adfe9b4302d557e3b5a721f315e9d187701e7f29ff40c7456fb16e2622f7bc980b46d200bf720de5388b27e80fd61839aa1158d96059da6b16ebf857fbdff7559858bf9b56a28ef62f328f16c1f3010c2966ae812a7846570ef186a03fbf246e4b97a30ca1b728550a47d3589d00b8f17b2fb399c7028258f877af9cddb6912604d724fd08a6e1c241e0d52e2d00ba8943f37d15847743272bbc349d419c4efe0eb822e572d5d84c7a609c010cb15f1bdb10c2fa70cfb58e2fb8e360679ec62c2aee7456755032f74cac7defe0030d431182fdfa00af3a36673ecf0cf30fffa4f6266c36c915e76c196a070eb14a097d72fffb4135c30516d54e43d385f73b9c75059d0406c0eb9b53f6737fcb7216486f1247a3513c49195223bc26a1609aed453e6343cc87c4f1224b2c29cdf0059089e79e88193eba1cf9659dc4049752a1c3903141d0aa99a0fbdecb65bb08b21a0ea390402295c28936b6948ffdfb9d2ea3a851630940e9b476d243e408516950e4373da18e2eb3a01e03061754df134abd099e64993c19b9ad20ece54553269db69be5862c77e06ac92e30b2bcc63be9098b0e1d557107b65a19cb7a765a6dff57abf5698e59a00432a7a964f6abe56d418d3c677db439882da38f8a9d29b69ac00676122b18598aa4f3b21b4f0f661219fd02be718ac1c3b3332ab25261e2df57f9493c96120e6393a0e7a9330aaf019f25476bd27e3c44340fe20cc45b82c5bfe0919dbe748eaaca484fc259c140bca1a9b96d96bc05112e9e1995e519360a88e2b8758b7f90fdc70079e9039757f387cf77bbed041ebb92037ecbb4b178a996012f58e0caa50fbeff7977b3779afe413bcee07aee9624ce45f2522d5cff987c9201562bdc8ddfec083c2a74e79323708cef604732c07b767472f94d9d16b6df50105633047e7fc81f533398b4ab7a7b3ce9a9699e57dddad9196862a47d99d8e1548fbd1d404875644b82f1376e323cd743afd17b48a6834edc8abeb169b055d7fef8b127d91d18b99dbce77b3edfe19305ea8aa6a3be2dc849ea905ac8615f8c31ed717f09c963a99c2acb40a470248e2c649c4b55ef7931a642dff38909c1a127ee1778bf6b98ea8aa62a168203d59a23854881bdd521c8b27519f7e51d3b8c13c503d9db8620d50b111fb0181739338c43c0865916f474d92ca64c13d4b00fe72e79b9c799d48ac066e0f33fd4bf8dfa1c0445077827e02e5ba0652e139fc8ca9833454b0dd86b6751c55423e915053866bd22f2a24e2dc1aab797f2413b6f286ff288f7b47ac2d68af8da8ac0c64a0a080ea7e81786cc888f715ad6d649a66ece9129232ecbadfce552e6e98e3b207a3b47671fa90564a1527fb9e28b4ae1b7b958a3abdebfd5eeac9c4e8f9896a403335a5591e4b6a056094a8f6bc744b5b3c01431754286fa85079717fa51421b3a810f3e0ed2d6d1b38a9cc91ff5283cc7b9e1a6c9183f56e159d1a855184672ec3ee0a675289d40fd5766aeb29c03fabd0b5a8cbd4182fdd93ca45a9e002a43728e60123b2e61bf384ac397c30bf8f0ccf5bc58596c0e073f5b0aa3cfcbd4e6a5649317cdabc72012a607564dc4bc2a7745f59ed18a8fc644bbd4349597474847bb8309e2412df79241f1df4626b75ade4fead30326c2584999b0b4c142b1a9307767fd1ba1f080cd63280fc075c36f483f3f67a00444ade453812843f05090c3edb8a8505f9676074f5b4906b8ea8620254d37234147dbe97202596361af7c7d275463ba8a940e0045fa461e4a2875deab7b5efce9474899d126a3cd7844707e69810f61354d92a5dfabce6e6f880a707815b1373329be135405df8166f7a39f5564594767294cce489c92f47039a59615dc4380bca06262820ab13e299ea8c620011c4612014ed4c50e5de23bd15b3ba53b99f00781fc55ad83fa685843776a210e64283ee9476e48b7450ebd7461477a94e93fcc679beccbc71707b3030f14546bc919e5d0875ffce5a99840f0ba1ecccdfbe211faa2853f2ba4d74f760d9b09ae7908d14cdbd0a5fdf040c0fc6b9291389d9f973f5cebe875d54f53a414d881205927722d9c6bdb0ccd5cfb1f371a3ec0198486a54f10a68942fda45522aaa9c07d8345cec7e01baa8ddadb994d4d271b3821c2cba6dcfe2df9b9b43aded6406d1e9b55eaf822dd5e5b684b6d9a8c6d829df1654b033b96730a4cf74f1e1d32f05adb4ba83e23427e6c4049f6633859f0ef3542a3854e2d36bc36a2d0d6d7d24de3f527ded1dcedad5b19be0e1961304de66e73a9d3cfe21802f15f1563eaa436ec783fd11cf9cae7fe9e5168d507c4c59934fc2afb7d2aed192c17d64db828d43e86781d2ee3bc1e327f662442d390f4bb5c1da3b1d4450ada85d8db5c85d275789ba37e47e35ece57931c580da5db2d20d252f223b43d9bd4617d113e39d194305989f44609bb2c45b1b1e14dc62fcbdbb3d07b271abb6f097571d94e913687bcfbb24f9a8320e14e6f564414083dede15eb54cb0bc376117d5b72baddfc42cd274a5690f46ce633894d9574ea21d1599d9fa97b2ad328960e93257b26d932594592c76cfa3854059aad8ffe9c4e1d943772db91450f6c40c18c7c3d94f16b340a12213efdd99f74fdfc2d0d60f8e256804a7fe7af0a97f4f1680d3c855c1351bf97544af0bb738a28ec8e97c7d2e732926d7bcb1ab0d628c987cea474d50f2d0a6e93fac257200e5b50f112f9edf861f1b8752dd0a587e22185d9efff531571273f968f3d3fe111a4762564b1d8c702e2c997716011d1cc4580d3bab762e5787f9f3699a190735f94a4dc8def2989e966275b35ceda21f9cb707953e393d46ee797306144bcacd2c7acd734faea356669c97caf03617146413a85580ca67f5a9cdece60c8e385f5d028c11f919dbc2b3152aa81e785ae69e9ae565f3a3ed42d3063472f5faed7ff139529380f9311f4f8c89ab1868cd90b251624fb5644fa2d5defc456fa14621b79c133b60d49b1a2699dfcfb431459b5a3aa74e378071bed4ad98f74bd9f3e748cb66de8e264930632e592c3f9e3dfb96af9dd212e7f697fcbbd930e6ed377bbe1bbf580584c35e4aa5b2dca6232bc6cee69d47c6ff43825526201ffeb2a48af6594d80ea58be562ddd80ad0c53e9166e6dda5b5f581192a8fca643a371d69703d7e795b9e40573209a6c549f1dd80ad79ec42eb29f64f80c525d6a80734e9afee5b1c3d245c44601a113682ac72bf88144c771bd906c8b005706cfdbedf1d06ff0b52fbd6f848357765c3dbb4677d09c08353bad7e35a0453bba554e5cb074677efc0f1eb4ee6e7aa39a2463453dc0c2720325a03254d95790b1a8493e565b9ccd8bf8d4528f55184e3e4baf837dbd741b28c81030ea50070ec83693afb50d97ae8d2903fa021c2c358d76bcf7b4fe71580db877c8ff9d6a3555a0ce1fab974a6c7d6e6047002a342184209a2363f2ede37faf6596db9964d593c29e933b9e8ad8df31396054aae6a890aa8153460b8178fd3c5eeb071a3b95e3bc9fa9a341b79436c1146fe5c8bfeb6b01aa7dd1e48cb511d7717fe2122710705578aea833c48e38c520fb80cf8ba0e28c0d7599d4a7366f1df7f54327fe11d1a89e4673145344d527ad757453fc420bb69a483825269f83474f384c588b0de49e61fa9b02c7087fe004a721660920048c7607ab2602b2e6c1c59b9b50e70d200aae2d4d89a446f082ed9d6e695bf2706742bbb272be8d324c6584341fe9d71c63ce8b38e8dea9ef1c3a90cc777a1452d2a79679577c804a71a7d37aa5cc8e8cb6c4c0f7500ebeba6942e77765dc35a5e199165cd84e86ddca7df1601533620f4f00bb51bdb798afe8e28d0593ab8df2534d8ee5ccabf57df6ce9d138f51f63a8b6ca2bfc9b2628a8e663bbe59660fb3e40ee25676d1d0ba4ca4be2e59a6f988a716c4ab14afe9c01d5dbad350c0502102ae24820aef01aa3d047c716123a663efa319273753282225d016325711446f3c15cd33f3ba2a9fb2b8d20cb34068be64065b37efce56facb7ff04371f3b2f3f5c1de6cb87282e8bf80c9d0d748d1b6b54b2a5289734a47035d35ae42de768ee3ea17d235db3ad493fff6ebf0d3f1dde3ec7c045d420956555f865d4c3c1f91c1994c95ab6c0b1e36b56c0f419ad9281c2901752ca04231d7a623202d054855f0ba70511a4ca957e50800653316387d6fbaf939b8a53338e9aec607bee81218b440b5cde69ac72cecb77cd188e77c8b6a161b74977bdea693881276c18f9098cd6d56f4b11fc106153a4e1283746f71e584b6018c7084f407814919e41ed7829950ec30a75c1067f7c37579a4bdeac145ca1138b0877df53c2101f328365b650ca1709ba3b0af78e8a97c8acc003811e8de4bcc2b3553ea4319eb4bed5db8b11d6950914650eea444cf86a215e3b96441883cc8555489d0ddfb7a43d327b84d1262bf91a280867dec44781418d9a540fd6c76539d111dbe42e727ecff5836d2f14207c0645bccfc8feee71620dc51b5171a3ac7f43df64ac99cea0c527d900a936444a647fd99f4aaa4512d5ad8d05859db1ede9a0ed929ab9eaca2c17df1ab4fbb96ac7764ad5a4508daef5dd776e049de878774c7943d4ee043552bc1fa1610f4b83c9ff7dc5bf69f2ea7e9bf336e80a4ab989718a5b2e0cbd3d82f0dee44447810724c695af404ae9bf13a350fdd4ec0398fba0701161259069619df55503e187a9a10b11d736da3702563800701dfe5a806558798f374a75ac33d2e6b4349970c5892498d359b70b33e0319d7e3750307e8f188f7c04920a1e70651b938435fd1df9b9d2c83ca2e45e3b43c12fd79e2cb0459e22980d8f15f7f3a4ac6680694b2d62a91c07406e7741f0c1908ae7dd0b284d0b4957fe8ece891fcc860ff9ee05044af00a8013f80eaa30d619660fc5ef58cebd5570bc39d84a3eafad02fc7869da09446e21b4faa6fcb0f53285df51f82d9a266143ca923e1ac125e285f63b9fb3056dd18b487e4d49a87890955fe14e60c7882ea2144b9a81c5be4ad81dd48903381639167182f5dc9346135cfb4161b1624e811c4c666d94b7eb2d9fc975345e67a8fe447d0b611b3221741770674dcce82575ba764ee772ffe406eeb46d20e4e2bae34f2bf8a519a721e1592048694d4b5269cd3db343c25ef3a0ae4d24e616732b53399f60c1ad07688b7f6f21513f96b314f0e02805489732e1c36bd05ca15c4376d9d7b61cda8097cd865e9512d9afcb1d1b0b00991634c8a8fa00e9ad2c417175adb4ca835664eda3e70d69fc93561ba98d48e77633625bddce0d86ffe76b2e7dceacbda657d4301000161bdfecdc508ff0e2ff126f75a31bf15e7f7306fd300856b1c273a27440399a6658bc6c3abf4406e718168f96637f76bc9735a12ab8d4d3ea76d56a91c19b67bfd4e1060330000000000000001c3f46501f2eab8012026167ab8e6150bdc56bcd69c61769534b1e2f59b903ba72330216284e18c9478e284d64c1678376628c1c90779e8f18ccdc67415122f03d4d4ae6ade3307cc5bab6424507399c1e5c6be2fc0cc0df0b1dd19dd2b163f2bff6864d651a63a30290de5101c65735afe42f7a58fb12406d64765bf7788120a8325924ae310d7c2a84391ec9dc8037c96e0553c827003faf43fa8006c6f51a16b00ac0ae28e4a9fe8fe3a4d6204e26b50245dbd50cc4b3ed0c6b9b79c5ca5335338b63e95a87bfecac449d45bdc0820fe5ecc625d8a83b33b744f84bc17d553f95dc02504d2fc58df2a533c5071076f8053f26517067bdbcef786eac100c2338d62bf6b0054f75a234efe4fb204aa674935cd10a53f460a6ca5ffe785e47b33d41202e16fa1988111b14a3fbbfdc9e4ac4cd60c823e69b17c3d1c5a60ad8c33d76279d9089945549fdcd5a5fd8c6fc18d2df713ade66a2c739bba4cdbec8eb60cc1e9c4e126bbad65e9fac3ce4927d823febe6a3ae9c4285204f270e973b95b154f35de906300a2a97bb6181c7da93c907fd4deaff9cf0e272235e483f046a6fd32af7ee9c66eb3f7402988375901b463745ac0b3eb1e1d8e0d4ae8bf178f9cb68a17bb0005955476a166d7f63bf304e4a995fe495ba008c962d97817a2e7407922e73a6d3a887789463d2b2ac5c3de6bad633d23c890b9d8ee02d89da2dc25f22245413efe4738ecdfef34360c1b3d2c1a8c5674e3eebc2054b972a5f4cbbfc25cac7477179b272820f33e81a67f24bf34b5c8d96921f410737703ae98c31b164fd6f3447c289f91fd7e333444dd947a2881784c6c9ee4a69b2061fa4a08acef4fe5695690c6bf863a1a58a7777a6dd2155ac362c4b4eab18771f7fead06d90e217485d940d71851aa911dc6137ac1ad37a898861ea6c25cb3f8d9b6fbc4c69d667ef94b580ee1cb70062f59475201b07ff1dfed42fc14ba2c5e83eea3af3cee0cc20122f2e67ecb2158fec917ab0ec06692651a380f93e2bc9496d1b285fb496c6e9c92bb6551d6c113e93d44a62dc031c7817467c2c296080d0b24eec50ca729957a7c37a36c868335488b054e5410f230317b224ebc8364e32ad6bc53c69faf9d34303d2201d2c41926de7a53d476d7aec6a9fbe8d0a5f6b358dfcc9ffd69b7f1e1ff172bc48c894896b4e88be2cca107e702279b60fe27132730e107cc207806be8430383f200805705efd68145f8a2dadfcc12d75ac6575186d4cd23d1314e276e72a75c8f94c51f538974e45aeb070dab42d7cfaf28f35a3c407f57ba94bcd73db63ed7b675cc9592ca5ae16580bf11d21eaefb8e6e466538c9e3d9bd56f01666b40d594c4a95a342adcaa95b507b3ac01c96b7ef244492c878e62e222cb9f999bd984b3bcd36ce68dfbaf031afe7b8f03e413045a4aff41f0428d9e19652f136eb9b36e485f4c71c2341cc0f474433c8b67c3abc271c573e13ef3023263a921470eb2b9bcb4448bcace53d441af5b8b660016dea16782edab12cf035f74fc143935c93d12b11e177e6a8e23e60157085ab7a62b375d26c9115eb368aa2743355d4824022cdfc774799028cd27aea2edc8866a17118af2a7e153f51413dad79b589a39e16c9b24e038f7a21d7e9fe14539061c3df534f54c6a8bd4d1b2bd69d656ea430d531ebf53acca1b7806ee7e1aff0a653994ae5ae8920eb026b58af68495f603e0f62af8cbe1e3bcdb51998f0fd7173d9e695e35272e1ad26920963d77109b87ba27240db901ab98a54be3f2f2aba0f1d5e9a89a89c6bb735d2afa3b898714700537c88b2f97ff2c684667259a6286cf96118f6560dd5ce986bc885e0128a18d99a17e5159fbb0e0c0015c7cc59a91bbf2a860e00490ed7e7b6cd93ab7825be05db8d6fd9f40b6901e9552863fce1969891c91bf880363a4f2173d7f1570251ca4477fb4f3b20c78c8ab6aa0f1171a9b4cd0cab48058836f82cd78bf2eedf4229353cbb8d953cb308d275a5d8776827e8460ce90d4d37dfaca256b5af719e837d1ebb41c64b380e0c1c980eea533117fcbb1035d5640142a73b11370958f64f97606189b55924f5c04b7afe835a7fa9133a1f9da3835e7a9e4cbb60d50d6eb53003374e4513e0cca18951edf27807cd701f7877edb20973f3bea5a77a5bdefa4744d523df366b2ad43b21564a3f83ab3ae8880e0856f1647f67009baa4882983736a9de6e9c4c0e4b52c49736d8ebb7cc6331e0bcb071fbb1602c653ac12f51381ff0272ff65cfede23b8bdc3bed67639c1cd5a885a41c42771185f02572a8494081382635db315d91c3e82af2365f220b07f85b9acbfab3b0901b8a115d361cdb70ccbfad524c99c52ff85e64f65ebc1467593f941ac42324b479fb731bd4fb46f6271beb72917b06a0c576306ed2ec1ecdd462ee7152cbc3252548edfa7f41704966e2a9465179980c541eaf0bfec29ef9b27337d50de417bdb2ba97cf7cdc96e9a508d6e6eae10de4a8d567af7de9c21b85b5da0d2f30a9e75acb05441cb447115201aaa131b9bec2ae2b8abaab5f580f31f5804e337eb4f17ecbe65aa17cd4f965d4e03c3033214647ac4cf5a01d8d4ef940d250731ecebd6fd269f0a41859a991719ee12f41fe5bf6a95ee01a5e3f9a05c3f87b5209fb70385fc233b0047a5410a51953576cf0d97049d16c9b4e9d8c27df2c3c419ce89b0b418461b364379e1a3d216323b85c8e61aa4e2da9cd0d13dc897e999cb46821167ff5a3658545c47b34effd697dd4de7fb627652ae21487a23cfb1380409eb12f0d82a1760e0190bab7955c4da60bb954360716748c7af1b7f5d9d3dbec56011a0ae81d184a9dd5beab37732d6a38c7c9f98445b04a0eae2bc41a825276bde2e97acc27e4f7e86e83e81d93dcd85097c224699c25f4a33b1e2323c94c3077a55cd8b40762d503bc992c9cdc2a5813c401e26f937d0ca3f4f9d28424d14820f897af1be20f8e0fae261ae7df80a112944a9b443fd743d6295d1410eb04e007d639da4acd9887d3f64652b8251793738024a6982f855af9b4ab6bf4f786dcf4a3378dd7892d385a4450c681f1decebd434c63b815b7773f07d86dd592e5695eba10f812a9935971d9e79b410f9fd90f2154970978e6ac4c7c6648b225d1a6312a36dbcc3c1834431b76a4dcec048fa81ae5ad95962236e6d70220720682f07565ba8b22d663f5ecbc6098f2f94cc81732205b1ef35c2cf31fcd0a5f5160df64c240aff9810e01c5ba7d3b804d985ddde7e38e8bcb945d42ec7c6d811ac3d12a2223d83094d8c7faa27a6d374ec09dc5b0f2ab657408e5cda8393f6ffc135f8d22a749dbba4dc5a90ef03e0a7253458cfc31cf563df0a92593ebc985a5bea991c56d2d2675fcb3ee6a000f33135494c7f667ed1890fd7bafa1c02bbc8d6e42b7ddd9250dcfd2994ecd887fcc54fa04b8e26dd2ac86793f527e1398780bfa12c8f90c77f7e65bcfbb4b60eee30ab15c5e96c400b205b4c80ba4a872cfd58dbb0150190d43a7f9b2e92ba2eac2681a16a13a4516fb5b45919bd117d3f54c84214a504b2ca3b5d9c210e551232c5cff89bb44fee2a6082b80b80955a3b153220391d0ee156c941b52b91705ddcfc5077d20ce00ac479cb909fdf864edd3f3e90cd635b75273b16b52fd43eb2af0fa177f43fd07e4ee61347f6d2dca33e460611f3568e8c5c6959209c496c32671c30bfabb342cf1a754858f98e50e0beed20889cae45715bd68a71a9b6c75b8b95c7a4434c4907690dd0cd0e76d2f443ed9cd0ba31e9e583a9baf5eda443a7e2e7ec7ef3370a5b7327e9df2357237e0b4da62fb1b15004d4d7b7b2e0f0039de4b45636f32118930db4140102276acde7a537c7691b59e719bac1b957d9f79f4cf46732b380e89bad066b0596148b038ec23c0ae5a0c926a65fbfce2f869d997f84e03e3800057755a0d3ac6e598bf072c0b830be2fb58966082c41c8b82bfd6d244ffecef6495a0a554b249493eb687ded543b82a214d18463089efbc101be7b35c79c47e42b471d1097ef23cefacb086480e37be19063bd112c665ffd9e35e326b5f6b87901edc6a2df30a3d272293ab6f1f970ccb9cbb37279abdd21fda5826ca5005d88595109c6c158bbaa9ebcdc8fff3ced6f18ea9ecfea651f52c5c9235fc7b57e7ebdcde73d32292bc9455cb78e30ab93d5a7f602605408fe1901951803f7502b7ad21bb441adf1932fcd9da4a4277129cf9321765c21aec46300be5b8946199b5b2575643e9fe85d21f6de581dfaa4c26eb70be60b073961bf0ccf8a17ca108bd2b747f2b890e6e6160d9ce4b9d7515679881148c8a57a0c66b48dcb9d66def11b2f2b5ce391ec1d4116946adf50a2f155afa7560fed4e8af6509c3b4b9a3b908f086eabd1af81042a81f6daf3ab31493d68e33f867dd4e1492549816898f56fc434c00cb674420f15f79417418e0ea1d797d97eae095a68573060c0d6039b951bdfc1a2565224276e63a7da4e0199c0badef39f4a7c3ab89ad43fc42dd900896a4333ec211bc74cf30f5b680cf098db27e51994d9a462dc40ae063c9b492177c6cde379134faae13f3195e0994d329c8075d66e9f93f13fcdaee84fcf25ce0300bbdc152e197de1a075f4e2c797b6f3ac31398f01df3e9497559f1279713c4cc0a8393ad39efc25ae2327a8d1284e8d12a26a5f6be135958cea150bb16880253fdf28ff874144ec5880fe4df2d08f73c30a349ba4c17156070418b19b2b6c19d0b0c4df72db27522ee4d90ea7afe8d6b0f18303a2cd57ecd49f4ba2b3fc5985cca1713c74a997ad42668c74e2b7c4ac6f9fe202a49b983fdecec6e0fc228498ad39d9ee270ec0b0b5e6a406124f8bd2dbed0799f7779078c51e0446ef1fe5c7c44c587bce68ac788a9d0468feb067e9918faf811b3e79414ba49a5a588b4b1087374dbdc082c011f409bc8b9b49632cdd616833f193416602c2dfb5a31a9ead6cb05477355adec97c96886d649fa0dd5094e71bf437bdbc512330b04accd0215f240b299e87db39bd8d7fa63b2d560c8da139df2e30bf9eb9447080e89f6add5c02b01858c9a042cf615732ff5851b7782fd7c3da902c6ec761ea69a36bda6785f44622701ec8a82100ac9adc2d134ae4ef6ee99dc0fa0a7140b8e0314d3f1e30ac4513ac71e49b3f1fe843f106bc27a04ed05d4f9825fa1c8f3dfe3cddd4e1d7696b1cdc77844faaa3aacc3b5f2e12d2d18ab75eb831ab626a4e4de6fcd11cf7a7029415beb3a7971953982e56c710d0390fb4e11685992182c62def1e20872c454095a1672dc87d0612a3a0305021d6af549d95e2d1dcea03804819a206b1316261a0673e59673f6827de10bd8390c2f9de62d816e09f3dd6934e792be83ce6ccdc7e3d4ec949bf503ed65900cdd7ad6a6507a3cf233d5f5a370c5c1ae8df2678d6f8783f1eb072222c80363631bad809e0408f2cc3ef1fe6635db75008c99a91a6e4e4e4098429be9d95cc0032358ab54489042dbf7b7e08173a5117b986d7d67cb41b2665fc154c2e2e7ebfa77d42861b55af6e44225ee594bf5b5bbd5c1fc3f80d74b6a529567ae80bcbee12dcc97c0b2fc12e2fdcc262c8c21d8c2f94a31a5e90aaef344571ce5b175d1a2d785e8a8884369335a641eeceab244f4a8cf631b3c55f3b77fb29b0c4ce40bbb54a769b5b12e84fe5ee6b96b4d3a32b937497ed92f7df82bc757d1990da05aa38031827047a4d4f2b92a9d84e0e4c901a4e9eb6badcb0fc3359f0eeec3bc95173c6ceac7c3779bf363b6a9cdfaeca084400b17d965134b9337f0d45b4aed7bdeba9192582829c4eafe29c42abbee645b0ce0a49635b6d38ddcb084d8b8cc326058cd6af615260000"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Document or relocate the hardcoded test transaction.

The someTx constant contains a large hex string without any documentation. This makes the test less maintainable.

Consider one of these approaches:

  1. Add a comment explaining what this transaction represents and why it's used
  2. Move it to a test fixtures file
  3. Generate it programmatically in the test setup

Example documentation:

+// someTx is a valid Liquid transaction hex used for testing broadcast functionality.
+// This represents a [describe transaction type/purpose].
 const someTx = "0200000001015a75b8607566159620994af84594a6b6d903aebbcf59d1382771694bf611eb8a0100000000fdffffff030b59cf2adff4bf2e7d3b8a36816d9fe3c675f2ba6d7198c35195d8d4868578f22808f5bee2a15d01413e36374ff5dc21224b230e9d70f247fcc85712f63fd914074d02aed6c7e3d0008a693a7bccaeb3aac96827cd5af5bbfbd75b2c6a549a0635914116001436532949385ba612cb7b9b3a12e2363666c5a1f40a8c5e056e5c43e77c3f3b49642e1a992e8552b74a9635cdf0e37751709314bf0d09b5b6e450adb2b9f95bc86f0d16279a58734f3f2ed8de73e85489f1ad318b912c02d48e551f064e5e9661f6317f354ccd15184c2b6f0dacf9c3160caf5819aadce11600141961af5494492cb063ddd798b6ac73274f5f65440125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a01000000000000001a00009e0000000000024730440220263f44bba67dfd29e45d6fef6e360e9f9605dbb8438e7b7c663c36dd40126436022054b1018aad29ea345bdcdb8de501e9bbc5f15a66851c8241f132cc42f01bd69c012103797c2f42feaf60680bf31ad2b2edc371a545f3cc96f95a900f5f0cf45aad0d8d004301000185ef088f87f452468e84ea5e7b2750c7664d2ed849bb5b9154414780e367c23c25d180eef6dfc4f8aa5a54116a5567b1f6b6c15059959f4db7872c6d0585ed7ffd4e1060330000000000000001856ba700f8aae132c01f52c60e60029e1341dcd575d1c4aa58d509f791697f9b482aa194f5dd647635d23f2dd019adf96762907d4f8b7047e1af12ff82ba5e69c00dedd7583c2b91ba3ec0040ee9fad69ae0bf58a49827056638fa4564a6e7de3f929ed6e93bbcd405b1cf9d967bebde29fd061da7e70be1464a47806add1f103d8ef9eb5c487cb82befdb60e0c620919ada15f6cf4fc443d2191e984a4c164627270af6a81380599285a4ee01c92c8991203e03478563d850bf86f8ff885216f6830d9cc845cfd1710f34fc61994a6c590085a9e73e637a6d00075d7d5ef893ac963d2ab9d21d9104cfd92d96e4818568411f18e9c28a353cd726e27e7e35d98d37240ab3317e5b5c352c9aa9ccecc3806a83008a89eeb255199c546a3132be478c991f07c12c5b386e03150f7bcb302e8ac84a14ee7544b75f78cd730a17df38cb7beb61b4ac716a69946da88bec0fb417eab843fc16758eb38822716f3ee9d0a77df7a78721fc6e4849d279a3aacfc3750ea8630657e16163e00ba1e26d60adfe9b4302d557e3b5a721f315e9d187701e7f29ff40c7456fb16e2622f7bc980b46d200bf720de5388b27e80fd61839aa1158d96059da6b16ebf857fbdff7559858bf9b56a28ef62f328f16c1f3010c2966ae812a7846570ef186a03fbf246e4b97a30ca1b728550a47d3589d00b8f17b2fb399c7028258f877af9cddb6912604d724fd08a6e1c241e0d52e2d00ba8943f37d15847743272bbc349d419c4efe0eb822e572d5d84c7a609c010cb15f1bdb10c2fa70cfb58e2fb8e360679ec62c2aee7456755032f74cac7defe0030d431182fdfa00af3a36673ecf0cf30fffa4f6266c36c915e76c196a070eb14a097d72fffb4135c30516d54e43d385f73b9c75059d0406c0eb9b53f6737fcb7216486f1247a3513c49195223bc26a1609aed453e6343cc87c4f1224b2c29cdf0059089e79e88193eba1cf9659dc4049752a1c3903141d0aa99a0fbdecb65bb08b21a0ea390402295c28936b6948ffdfb9d2ea3a851630940e9b476d243e408516950e4373da18e2eb3a01e03061754df134abd099e64993c19b9ad20ece54553269db69be5862c77e06ac92e30b2bcc63be9098b0e1d557107b65a19cb7a765a6dff57abf5698e59a00432a7a964f6abe56d418d3c677db439882da38f8a9d29b69ac00676122b18598aa4f3b21b4f0f661219fd02be718ac1c3b3332ab25261e2df57f9493c96120e6393a0e7a9330aaf019f25476bd27e3c44340fe20cc45b82c5bfe0919dbe748eaaca484fc259c140bca1a9b96d96bc05112e9e1995e519360a88e2b8758b7f90fdc70079e9039757f387cf77bbed041ebb92037ecbb4b178a996012f58e0caa50fbeff7977b3779afe413bcee07aee9624ce45f2522d5cff987c9201562bdc8ddfec083c2a74e79323708cef604732c07b767472f94d9d16b6df50105633047e7fc81f533398b4ab7a7b3ce9a9699e57dddad9196862a47d99d8e1548fbd1d404875644b82f1376e323cd743afd17b48a6834edc8abeb169b055d7fef8b127d91d18b99dbce77b3edfe19305ea8aa6a3be2dc849ea905ac8615f8c31ed717f09c963a99c2acb40a470248e2c649c4b55ef7931a642dff38909c1a127ee1778bf6b98ea8aa62a168203d59a23854881bdd521c8b27519f7e51d3b8c13c503d9db8620d50b111fb0181739338c43c0865916f474d92ca64c13d4b00fe72e79b9c799d48ac066e0f33fd4bf8dfa1c0445077827e02e5ba0652e139fc8ca9833454b0dd86b6751c55423e915053866bd22f2a24e2dc1aab797f2413b6f286ff288f7b47ac2d68af8da8ac0c64a0a080ea7e81786cc888f715ad6d649a66ece9129232ecbadfce552e6e98e3b207a3b47671fa90564a1527fb9e28b4ae1b7b958a3abdebfd5eeac9c4e8f9896a403335a5591e4b6a056094a8f6bc744b5b3c01431754286fa85079717fa51421b3a810f3e0ed2d6d1b38a9cc91ff5283cc7b9e1a6c9183f56e159d1a855184672ec3ee0a675289d40fd5766aeb29c03fabd0b5a8cbd4182fdd93ca45a9e002a43728e60123b2e61bf384ac397c30bf8f0ccf5bc58596c0e073f5b0aa3cfcbd4e6a5649317cdabc72012a607564dc4bc2a7745f59ed18a8fc644bbd4349597474847bb8309e2412df79241f1df4626b75ade4fead30326c2584999b0b4c142b1a9307767fd1ba1f080cd63280fc075c36f483f3f67a00444ade453812843f05090c3edb8a8505f9676074f5b4906b8ea8620254d37234147dbe97202596361af7c7d275463ba8a940e0045fa461e4a2875deab7b5efce9474899d126a3cd7844707e69810f61354d92a5dfabce6e6f880a707815b1373329be135405df8166f7a39f5564594767294cce489c92f47039a59615dc4380bca06262820ab13e299ea8c620011c4612014ed4c50e5de23bd15b3ba53b99f00781fc55ad83fa685843776a210e64283ee9476e48b7450ebd7461477a94e93fcc679beccbc71707b3030f14546bc919e5d0875ffce5a99840f0ba1ecccdfbe211faa2853f2ba4d74f760d9b09ae7908d14cdbd0a5fdf040c0fc6b9291389d9f973f5cebe875d54f53a414d881205927722d9c6bdb0ccd5cfb1f371a3ec0198486a54f10a68942fda45522aaa9c07d8345cec7e01baa8ddadb994d4d271b3821c2cba6dcfe2df9b9b43aded6406d1e9b55eaf822dd5e5b684b6d9a8c6d829df1654b033b96730a4cf74f1e1d32f05adb4ba83e23427e6c4049f6633859f0ef3542a3854e2d36bc36a2d0d6d7d24de3f527ded1dcedad5b19be0e1961304de66e73a9d3cfe21802f15f1563eaa436ec783fd11cf9cae7fe9e5168d507c4c59934fc2afb7d2aed192c17d64db828d43e86781d2ee3bc1e327f662442d390f4bb5c1da3b1d4450ada85d8db5c85d275789ba37e47e35ece57931c580da5db2d20d252f223b43d9bd4617d113e39d194305989f44609bb2c45b1b1e14dc62fcbdbb3d07b271abb6f097571d94e913687bcfbb24f9a8320e14e6f564414083dede15eb54cb0bc376117d5b72baddfc42cd274a5690f46ce633894d9574ea21d1599d9fa97b2ad328960e93257b26d932594592c76cfa3854059aad8ffe9c4e1d943772db91450f6c40c18c7c3d94f16b340a12213efdd99f74fdfc2d0d60f8e256804a7fe7af0a97f4f1680d3c855c1351bf97544af0bb738a28ec8e97c7d2e732926d7bcb1ab0d628c987cea474d50f2d0a6e93fac257200e5b50f112f9edf861f1b8752dd0a587e22185d9efff531571273f968f3d3fe111a4762564b1d8c702e2c997716011d1cc4580d3bab762e5787f9f3699a190735f94a4dc8def2989e966275b35ceda21f9cb707953e393d46ee797306144bcacd2c7acd734faea356669c97caf03617146413a85580ca67f5a9cdece60c8e385f5d028c11f919dbc2b3152aa81e785ae69e9ae565f3a3ed42d3063472f5faed7ff139529380f9311f4f8c89ab1868cd90b251624fb5644fa2d5defc456fa14621b79c133b60d49b1a2699dfcfb431459b5a3aa74e378071bed4ad98f74bd9f3e748cb66de8e264930632e592c3f9e3dfb96af9dd212e7f697fcbbd930e6ed377bbe1bbf580584c35e4aa5b2dca6232bc6cee69d47c6ff43825526201ffeb2a48af6594d80ea58be562ddd80ad0c53e9166e6dda5b5f581192a8fca643a371d69703d7e795b9e40573209a6c549f1dd80ad79ec42eb29f64f80c525d6a80734e9afee5b1c3d245c44601a113682ac72bf88144c771bd906c8b005706cfdbedf1d06ff0b52fbd6f848357765c3dbb4677d09c08353bad7e35a0453bba554e5cb074677efc0f1eb4ee6e7aa39a2463453dc0c2720325a03254d95790b1a8493e565b9ccd8bf8d4528f55184e3e4baf837dbd741b28c81030ea50070ec83693afb50d97ae8d2903fa021c2c358d76bcf7b4fe71580db877c8ff9d6a3555a0ce1fab974a6c7d6e6047002a342184209a2363f2ede37faf6596db9964d593c29e933b9e8ad8df31396054aae6a890aa8153460b8178fd3c5eeb071a3b95e3bc9fa9a341b79436c1146fe5c8bfeb6b01aa7dd1e48cb511d7717fe2122710705578aea833c48e38c520fb80cf8ba0e28c0d7599d4a7366f1df7f54327fe11d1a89e4673145344d527ad757453fc420bb69a483825269f83474f384c588b0de49e61fa9b02c7087fe004a721660920048c7607ab2602b2e6c1c59b9b50e70d200aae2d4d89a446f082ed9d6e695bf2706742bbb272be8d324c6584341fe9d71c63ce8b38e8dea9ef1c3a90cc777a1452d2a79679577c804a71a7d37aa5cc8e8cb6c4c0f7500ebeba6942e77765dc35a5e199165cd84e86ddca7df1601533620f4f00bb51bdb798afe8e28d0593ab8df2534d8ee5ccabf57df6ce9d138f51f63a8b6ca2bfc9b2628a8e663bbe59660fb3e40ee25676d1d0ba4ca4be2e59a6f988a716c4ab14afe9c01d5dbad350c0502102ae24820aef01aa3d047c716123a663efa319273753282225d016325711446f3c15cd33f3ba2a9fb2b8d20cb34068be64065b37efce56facb7ff04371f3b2f3f5c1de6cb87282e8bf80c9d0d748d1b6b54b2a5289734a47035d35ae42de768ee3ea17d235db3ad493fff6ebf0d3f1dde3ec7c045d420956555f865d4c3c1f91c1994c95ab6c0b1e36b56c0f419ad9281c2901752ca04231d7a623202d054855f0ba70511a4ca957e50800653316387d6fbaf939b8a53338e9aec607bee81218b440b5cde69ac72cecb77cd188e77c8b6a161b74977bdea693881276c18f9098cd6d56f4b11fc106153a4e1283746f71e584b6018c7084f407814919e41ed7829950ec30a75c1067f7c37579a4bdeac145ca1138b0877df53c2101f328365b650ca1709ba3b0af78e8a97c8acc003811e8de4bcc2b3553ea4319eb4bed5db8b11d6950914650eea444cf86a215e3b96441883cc8555489d0ddfb7a43d327b84d1262bf91a280867dec44781418d9a540fd6c76539d111dbe42e727ecff5836d2f14207c0645bccfc8feee71620dc51b5171a3ac7f43df64ac99cea0c527d900a936444a647fd99f4aaa4512d5ad8d05859db1ede9a0ed929ab9eaca2c17df1ab4fbb96ac7764ad5a4508daef5dd776e049de878774c7943d4ee043552bc1fa1610f4b83c9ff7dc5bf69f2ea7e9bf336e80a4ab989718a5b2e0cbd3d82f0dee44447810724c695af404ae9bf13a350fdd4ec0398fba0701161259069619df55503e187a9a10b11d736da3702563800701dfe5a806558798f374a75ac33d2e6b4349970c5892498d359b70b33e0319d7e3750307e8f188f7c04920a1e70651b938435fd1df9b9d2c83ca2e45e3b43c12fd79e2cb0459e22980d8f15f7f3a4ac6680694b2d62a91c07406e7741f0c1908ae7dd0b284d0b4957fe8ece891fcc860ff9ee05044af00a8013f80eaa30d619660fc5ef58cebd5570bc39d84a3eafad02fc7869da09446e21b4faa6fcb0f53285df51f82d9a266143ca923e1ac125e285f63b9fb3056dd18b487e4d49a87890955fe14e60c7882ea2144b9a81c5be4ad81dd48903381639167182f5dc9346135cfb4161b1624e811c4c666d94b7eb2d9fc975345e67a8fe447d0b611b3221741770674dcce82575ba764ee772ffe406eeb46d20e4e2bae34f2bf8a519a721e1592048694d4b5269cd3db343c25ef3a0ae4d24e616732b53399f60c1ad07688b7f6f21513f96b314f0e02805489732e1c36bd05ca15c4376d9d7b61cda8097cd865e9512d9afcb1d1b0b00991634c8a8fa00e9ad2c417175adb4ca835664eda3e70d69fc93561ba98d48e77633625bddce0d86ffe76b2e7dceacbda657d4301000161bdfecdc508ff0e2ff126f75a31bf15e7f7306fd300856b1c273a27440399a6658bc6c3abf4406e718168f96637f76bc9735a12ab8d4d3ea76d56a91c19b67bfd4e1060330000000000000001c3f46501f2eab8012026167ab8e6150bdc56bcd69c61769534b1e2f59b903ba72330216284e18c9478e284d64c1678376628c1c90779e8f18ccdc67415122f03d4d4ae6ade3307cc5bab6424507399c1e5c6be2fc0cc0df0b1dd19dd2b163f2bff6864d651a63a30290de5101c65735afe42f7a58fb12406d64765bf7788120a8325924ae310d7c2a84391ec9dc8037c96e0553c827003faf43fa8006c6f51a16b00ac0ae28e4a9fe8fe3a4d6204e26b50245dbd50cc4b3ed0c6b9b79c5ca5335338b63e95a87bfecac449d45bdc0820fe5ecc625d8a83b33b744f84bc17d553f95dc02504d2fc58df2a533c5071076f8053f26517067bdbcef786eac100c2338d62bf6b0054f75a234efe4fb204aa674935cd10a53f460a6ca5ffe785e47b33d41202e16fa1988111b14a3fbbfdc9e4ac4cd60c823e69b17c3d1c5a60ad8c33d76279d9089945549fdcd5a5fd8c6fc18d2df713ade66a2c739bba4cdbec8eb60cc1e9c4e126bbad65e9fac3ce4927d823febe6a3ae9c4285204f270e973b95b154f35de906300a2a97bb6181c7da93c907fd4deaff9cf0e272235e483f046a6fd32af7ee9c66eb3f7402988375901b463745ac0b3eb1e1d8e0d4ae8bf178f9cb68a17bb0005955476a166d7f63bf304e4a995fe495ba008c962d97817a2e7407922e73a6d3a887789463d2b2ac5c3de6bad633d23c890b9d8ee02d89da2dc25f22245413efe4738ecdfef34360c1b3d2c1a8c5674e3eebc2054b972a5f4cbbfc25cac7477179b272820f33e81a67f24bf34b5c8d96921f410737703ae98c31b164fd6f3447c289f91fd7e333444dd947a2881784c6c9ee4a69b2061fa4a08acef4fe5695690c6bf863a1a58a7777a6dd2155ac362c4b4eab18771f7fead06d90e217485d940d71851aa911dc6137ac1ad37a898861ea6c25cb3f8d9b6fbc4c69d667ef94b580ee1cb70062f59475201b07ff1dfed42fc14ba2c5e83eea3af3cee0cc20122f2e67ecb2158fec917ab0ec06692651a380f93e2bc9496d1b285fb496c6e9c92bb6551d6c113e93d44a62dc031c7817467c2c296080d0b24eec50ca729957a7c37a36c868335488b054e5410f230317b224ebc8364e32ad6bc53c69faf9d34303d2201d2c41926de7a53d476d7aec6a9fbe8d0a5f6b358dfcc9ffd69b7f1e1ff172bc48c894896b4e88be2cca107e702279b60fe27132730e107cc207806be8430383f200805705efd68145f8a2dadfcc12d75ac6575186d4cd23d1314e276e72a75c8f94c51f538974e45aeb070dab42d7cfaf28f35a3c407f57ba94bcd73db63ed7b675cc9592ca5ae16580bf11d21eaefb8e6e466538c9e3d9bd56f01666b40d594c4a95a342adcaa95b507b3ac01c96b7ef244492c878e62e222cb9f999bd984b3bcd36ce68dfbaf031afe7b8f03e413045a4aff41f0428d9e19652f136eb9b36e485f4c71c2341cc0f474433c8b67c3abc271c573e13ef3023263a921470eb2b9bcb4448bcace53d441af5b8b660016dea16782edab12cf035f74fc143935c93d12b11e177e6a8e23e60157085ab7a62b375d26c9115eb368aa2743355d4824022cdfc774799028cd27aea2edc8866a17118af2a7e153f51413dad79b589a39e16c9b24e038f7a21d7e9fe14539061c3df534f54c6a8bd4d1b2bd69d656ea430d531ebf53acca1b7806ee7e1aff0a653994ae5ae8920eb026b58af68495f603e0f62af8cbe1e3bcdb51998f0fd7173d9e695e35272e1ad26920963d77109b87ba27240db901ab98a54be3f2f2aba0f1d5e9a89a89c6bb735d2afa3b898714700537c88b2f97ff2c684667259a6286cf96118f6560dd5ce986bc885e0128a18d99a17e5159fbb0e0c0015c7cc59a91bbf2a860e00490ed7e7b6cd93ab7825be05db8d6fd9f40b6901e9552863fce1969891c91bf880363a4f2173d7f1570251ca4477fb4f3b20c78c8ab6aa0f1171a9b4cd0cab48058836f82cd78bf2eedf4229353cbb8d953cb308d275a5d8776827e8460ce90d4d37dfaca256b5af719e837d1ebb41c64b380e0c1c980eea533117fcbb1035d5640142a73b11370958f64f97606189b55924f5c04b7afe835a7fa9133a1f9da3835e7a9e4cbb60d50d6eb53003374e4513e0cca18951edf27807cd701f7877edb20973f3bea5a77a5bdefa4744d523df366b2ad43b21564a3f83ab3ae8880e0856f1647f67009baa4882983736a9de6e9c4c0e4b52c49736d8ebb7cc6331e0bcb071fbb1602c653ac12f51381ff0272ff65cfede23b8bdc3bed67639c1cd5a885a41c42771185f02572a8494081382635db315d91c3e82af2365f220b07f85b9acbfab3b0901b8a115d361cdb70ccbfad524c99c52ff85e64f65ebc1467593f941ac42324b479fb731bd4fb46f6271beb72917b06a0c576306ed2ec1ecdd462ee7152cbc3252548edfa7f41704966e2a9465179980c541eaf0bfec29ef9b27337d50de417bdb2ba97cf7cdc96e9a508d6e6eae10de4a8d567af7de9c21b85b5da0d2f30a9e75acb05441cb447115201aaa131b9bec2ae2b8abaab5f580f31f5804e337eb4f17ecbe65aa17cd4f965d4e03c3033214647ac4cf5a01d8d4ef940d250731ecebd6fd269f0a41859a991719ee12f41fe5bf6a95ee01a5e3f9a05c3f87b5209fb70385fc233b0047a5410a51953576cf0d97049d16c9b4e9d8c27df2c3c419ce89b0b418461b364379e1a3d216323b85c8e61aa4e2da9cd0d13dc897e999cb46821167ff5a3658545c47b34effd697dd4de7fb627652ae21487a23cfb1380409eb12f0d82a1760e0190bab7955c4da60bb954360716748c7af1b7f5d9d3dbec56011a0ae81d184a9dd5beab37732d6a38c7c9f98445b04a0eae2bc41a825276bde2e97acc27e4f7e86e83e81d93dcd85097c224699c25f4a33b1e2323c94c3077a55cd8b40762d503bc992c9cdc2a5813c401e26f937d0ca3f4f9d28424d14820f897af1be20f8e0fae261ae7df80a112944a9b443fd743d6295d1410eb04e007d639da4acd9887d3f64652b8251793738024a6982f855af9b4ab6bf4f786dcf4a3378dd7892d385a4450c681f1decebd434c63b815b7773f07d86dd592e5695eba10f812a9935971d9e79b410f9fd90f2154970978e6ac4c7c6648b225d1a6312a36dbcc3c1834431b76a4dcec048fa81ae5ad95962236e6d70220720682f07565ba8b22d663f5ecbc6098f2f94cc81732205b1ef35c2cf31fcd0a5f5160df64c240aff9810e01c5ba7d3b804d985ddde7e38e8bcb945d42ec7c6d811ac3d12a2223d83094d8c7faa27a6d374ec09dc5b0f2ab657408e5cda8393f6ffc135f8d22a749dbba4dc5a90ef03e0a7253458cfc31cf563df0a92593ebc985a5bea991c56d2d2675fcb3ee6a000f33135494c7f667ed1890fd7bafa1c02bbc8d6e42b7ddd9250dcfd2994ecd887fcc54fa04b8e26dd2ac86793f527e1398780bfa12c8f90c77f7e65bcfbb4b60eee30ab15c5e96c400b205b4c80ba4a872cfd58dbb0150190d43a7f9b2e92ba2eac2681a16a13a4516fb5b45919bd117d3f54c84214a504b2ca3b5d9c210e551232c5cff89bb44fee2a6082b80b80955a3b153220391d0ee156c941b52b91705ddcfc5077d20ce00ac479cb909fdf864edd3f3e90cd635b75273b16b52fd43eb2af0fa177f43fd07e4ee61347f6d2dca33e460611f3568e8c5c6959209c496c32671c30bfabb342cf1a754858f98e50e0beed20889cae45715bd68a71a9b6c75b8b95c7a4434c4907690dd0cd0e76d2f443ed9cd0ba31e9e583a9baf5eda443a7e2e7ec7ef3370a5b7327e9df2357237e0b4da62fb1b15004d4d7b7b2e0f0039de4b45636f32118930db4140102276acde7a537c7691b59e719bac1b957d9f79f4cf46732b380e89bad066b0596148b038ec23c0ae5a0c926a65fbfce2f869d997f84e03e3800057755a0d3ac6e598bf072c0b830be2fb58966082c41c8b82bfd6d244ffecef6495a0a554b249493eb687ded543b82a214d18463089efbc101be7b35c79c47e42b471d1097ef23cefacb086480e37be19063bd112c665ffd9e35e326b5f6b87901edc6a2df30a3d272293ab6f1f970ccb9cbb37279abdd21fda5826ca5005d88595109c6c158bbaa9ebcdc8fff3ced6f18ea9ecfea651f52c5c9235fc7b57e7ebdcde73d32292bc9455cb78e30ab93d5a7f602605408fe1901951803f7502b7ad21bb441adf1932fcd9da4a4277129cf9321765c21aec46300be5b8946199b5b2575643e9fe85d21f6de581dfaa4c26eb70be60b073961bf0ccf8a17ca108bd2b747f2b890e6e6160d9ce4b9d7515679881148c8a57a0c66b48dcb9d66def11b2f2b5ce391ec1d4116946adf50a2f155afa7560fed4e8af6509c3b4b9a3b908f086eabd1af81042a81f6daf3ab31493d68e33f867dd4e1492549816898f56fc434c00cb674420f15f79417418e0ea1d797d97eae095a68573060c0d6039b951bdfc1a2565224276e63a7da4e0199c0badef39f4a7c3ab89ad43fc42dd900896a4333ec211bc74cf30f5b680cf098db27e51994d9a462dc40ae063c9b492177c6cde379134faae13f3195e0994d329c8075d66e9f93f13fcdaee84fcf25ce0300bbdc152e197de1a075f4e2c797b6f3ac31398f01df3e9497559f1279713c4cc0a8393ad39efc25ae2327a8d1284e8d12a26a5f6be135958cea150bb16880253fdf28ff874144ec5880fe4df2d08f73c30a349ba4c17156070418b19b2b6c19d0b0c4df72db27522ee4d90ea7afe8d6b0f18303a2cd57ecd49f4ba2b3fc5985cca1713c74a997ad42668c74e2b7c4ac6f9fe202a49b983fdecec6e0fc228498ad39d9ee270ec0b0b5e6a406124f8bd2dbed0799f7779078c51e0446ef1fe5c7c44c587bce68ac788a9d0468feb067e9918faf811b3e79414ba49a5a588b4b1087374dbdc082c011f409bc8b9b49632cdd616833f193416602c2dfb5a31a9ead6cb05477355adec97c96886d649fa0dd5094e71bf437bdbc512330b04accd0215f240b299e87db39bd8d7fa63b2d560c8da139df2e30bf9eb9447080e89f6add5c02b01858c9a042cf615732ff5851b7782fd7c3da902c6ec761ea69a36bda6785f44622701ec8a82100ac9adc2d134ae4ef6ee99dc0fa0a7140b8e0314d3f1e30ac4513ac71e49b3f1fe843f106bc27a04ed05d4f9825fa1c8f3dfe3cddd4e1d7696b1cdc77844faaa3aacc3b5f2e12d2d18ab75eb831ab626a4e4de6fcd11cf7a7029415beb3a7971953982e56c710d0390fb4e11685992182c62def1e20872c454095a1672dc87d0612a3a0305021d6af549d95e2d1dcea03804819a206b1316261a0673e59673f6827de10bd8390c2f9de62d816e09f3dd6934e792be83ce6ccdc7e3d4ec949bf503ed65900cdd7ad6a6507a3cf233d5f5a370c5c1ae8df2678d6f8783f1eb072222c80363631bad809e0408f2cc3ef1fe6635db75008c99a91a6e4e4e4098429be9d95cc0032358ab54489042dbf7b7e08173a5117b986d7d67cb41b2665fc154c2e2e7ebfa77d42861b55af6e44225ee594bf5b5bbd5c1fc3f80d74b6a529567ae80bcbee12dcc97c0b2fc12e2fdcc262c8c21d8c2f94a31a5e90aaef344571ce5b175d1a2d785e8a8884369335a641eeceab244f4a8cf631b3c55f3b77fb29b0c4ce40bbb54a769b5b12e84fe5ee6b96b4d3a32b937497ed92f7df82bc757d1990da05aa38031827047a4d4f2b92a9d84e0e4c901a4e9eb6badcb0fc3359f0eeec3bc95173c6ceac7c3779bf363b6a9cdfaeca084400b17d965134b9337f0d45b4aed7bdeba9192582829c4eafe29c42abbee645b0ce0a49635b6d38ddcb084d8b8cc326058cd6af615260000"
🤖 Prompt for AI Agents
In internal/onchain/liquid-wallet/wallet_test.go at line 270, the someTx
constant is a large hardcoded hex string without any explanation. Add a comment
above this constant describing what the transaction represents and why it is
used in the test to improve maintainability. Alternatively, consider moving this
hex string to a dedicated test fixtures file or generate it programmatically
during test setup for better clarity and reuse.

Comment on lines +139 to +153
func convertNetwork(network *boltz.Network) *lwk.Network {
if network == nil {
return nil
}
switch network {
case boltz.Regtest:
return Regtest
case boltz.TestNet:
return Testnet
case boltz.MainNet:
return Mainnet
default:
panic(fmt.Sprintf("unsupported network %v", *network))
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace panic with error return for unsupported networks.

Using panic in production code makes error handling difficult. Consider returning an error instead.

-func convertNetwork(network *boltz.Network) *lwk.Network {
+func convertNetwork(network *boltz.Network) (*lwk.Network, error) {
 	if network == nil {
-		return nil
+		return nil, nil
 	}
 	switch network {
 	case boltz.Regtest:
-		return Regtest
+		return Regtest, nil
 	case boltz.TestNet:
-		return Testnet
+		return Testnet, nil
 	case boltz.MainNet:
-		return Mainnet
+		return Mainnet, nil
 	default:
-		panic(fmt.Sprintf("unsupported network %v", *network))
+		return nil, fmt.Errorf("unsupported network %v", *network)
 	}
 }

Update all callers to handle the error:

-	return lwk.NewSigner(parsed, convertNetwork(network))
+	net, err := convertNetwork(network)
+	if err != nil {
+		return nil, err
+	}
+	return lwk.NewSigner(parsed, net)
🤖 Prompt for AI Agents
In internal/onchain/liquid-wallet/wallet.go lines 139 to 153, replace the panic
call for unsupported networks with returning an error to improve error handling.
Change the function signature to return (*lwk.Network, error), return nil and an
error for unsupported networks instead of panicking, and update all callers of
convertNetwork to handle the error properly.

* feat: populate external outputs in lwk

* fix: set non-btc assets amounts to 0

* fix: use correct fee output in tests
* fix: lwk `GetSendFee` send amount calculation

* fix: check for confirmed balance when funding test wallet

current state can cause flaky tests

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
internal/onchain/liquid-wallet/wallet_test.go (1)

298-298: Document or relocate the hardcoded test transaction.

The someTx constant contains a large hex string without any documentation. This makes the test less maintainable.

internal/onchain/liquid-wallet/wallet.go (1)

140-154: Replace panic with error return for unsupported networks.

Using panic in production code makes error handling difficult. Consider returning an error instead.

🧹 Nitpick comments (3)
internal/rpcserver/rpcserver_test.go (2)

1162-1186: Improve the fundedWallet function logic and error handling.

The rewritten fundedWallet function has improved logic that first attempts to fetch a wallet by name before importing, which is more efficient. However, there are several areas for improvement:

  1. Error handling inconsistency: The function handles the wallet fetch error but doesn't validate the specific error type
  2. Magic numbers: The hardcoded loop count (5) and amounts (10_000_000) should be constants
  3. Potential race condition: The sleep after sending funds might not be sufficient in all cases

Consider these improvements:

func fundedWallet(t *testing.T, client client.Boltz, currency boltzrpc.Currency) *boltzrpc.Wallet {
+	const (
+		fundingIterations = 5
+		fundingAmount     = 10_000_000
+		fundingDelay      = 200 * time.Millisecond
+	)
+	
	params := &boltzrpc.WalletParams{Currency: currency, Name: walletName(currency)}
	wallet, err := client.GetWallet(params.Name)
	if err != nil {
+		// Only import if wallet doesn't exist
+		if !strings.Contains(err.Error(), "not found") {
+			require.NoError(t, err)
+		}
		mnemonic := test.WalletMnemonic
		subaccount := uint64(test.WalletSubaccount)
		creds := &boltzrpc.WalletCredentials{Mnemonic: &mnemonic, Subaccount: &subaccount}
		wallet, err = client.ImportWallet(params, creds)
		require.NoError(t, err)
	}
	if wallet.Balance.Total == 0 {
-		for i := 0; i < 5; i++ {
+		for i := 0; i < fundingIterations; i++ {
			receive, err := client.WalletReceive(wallet.Id)
			require.NoError(t, err)
-			test.SendToAddress(getCli(currency), receive.Address, 10_000_000)
-			time.Sleep(200 * time.Millisecond)
+			test.SendToAddress(getCli(currency), receive.Address, fundingAmount)
+			time.Sleep(fundingDelay)
		}

1884-1898: Improve test logic and remove unnecessary sleep.

The test logic improvements are good, but there are some concerns:

  1. Unnecessary sleep: The 1-second sleep at the end serves no clear purpose and slows down tests
  2. Complex conditional logic: The mining logic could be simplified

Consider these improvements:

	if !tc.zeroconf {
		if tc.currency == boltzrpc.Currency_BTC {
			// for btc, we only check on new blocks, so we mine one here,
			// but the mocked tx provider says that the tx isn't confirmed yet
			test.MineBlock()
		}
		statusStream(boltzrpc.SwapState_PENDING, boltz.TransactionDirectMempool)
	}

	confirmed = true
	test.MineBlock()
	info := statusStream(boltzrpc.SwapState_SUCCESSFUL, boltz.TransactionDirect)
	require.Equal(t, info.ReverseSwap.ClaimAddress, swap.Address)
-	time.Sleep(1 * time.Second)

The sleep appears to be unnecessary and should be removed unless there's a specific timing issue that needs to be addressed.

internal/onchain/liquid-wallet/wallet.go (1)

57-57: Fix typo in comment.

-	// cfg used for buliding this instance
+	// cfg used for building this instance
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae49df8 and 2178897.

📒 Files selected for processing (5)
  • internal/autoswap/chain.go (1 hunks)
  • internal/onchain/liquid-wallet/wallet.go (1 hunks)
  • internal/onchain/liquid-wallet/wallet_test.go (1 hunks)
  • internal/rpcserver/rpcserver_test.go (9 hunks)
  • internal/test/test.go (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • internal/autoswap/chain.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/test/test.go
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.431Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
internal/rpcserver/rpcserver_test.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
internal/onchain/liquid-wallet/wallet_test.go (1)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.431Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
internal/onchain/liquid-wallet/wallet.go (2)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.431Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
🧬 Code Graph Analysis (1)
internal/onchain/liquid-wallet/wallet_test.go (5)
internal/onchain/liquid-wallet/wallet.go (9)
  • Persister (22-25)
  • Wallet (27-37)
  • BlockchainBackend (56-61)
  • NewBlockchainBackend (78-132)
  • Config (44-54)
  • Regtest (136-136)
  • GenerateMnemonic (575-585)
  • DeriveDefaultDescriptor (164-180)
  • NewWallet (182-225)
internal/test/test.go (6)
  • WalletCredentials (52-64)
  • SendToAddress (195-197)
  • LiquidCli (163-165)
  • MineBlock (177-181)
  • FundWallet (66-105)
  • GetNewAddress (191-193)
pkg/boltz/currency.go (1)
  • CurrencyLiquid (12-12)
internal/onchain/onchain.go (3)
  • FeeProvider (38-40)
  • Id (19-19)
  • TxProvider (50-54)
internal/mocks/onchain/TxProvider_mock.go (1)
  • NewMockTxProvider (13-23)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
🔇 Additional comments (9)
internal/rpcserver/rpcserver_test.go (7)

20-21: LGTM: New imports support liquid wallet integration.

The imports for liquid_wallet and wallet packages are appropriate for the PR's objective of integrating liquid wallet functionality and supporting legacy wallet types.


34-34: LGTM: Serializers import supports currency parsing.

The serializers import is needed for the new currency parsing functionality used in the direct reverse swap payment tests.


1741-1747: LGTM: Wallet creation and credential handling improvements.

The changes properly capture the creation response and validate that both mnemonic and core descriptor are non-empty, which ensures the wallet credentials are properly generated.


1752-1753: LGTM: Consistent use of wallet ID and full credentials.

The changes correctly use the created wallet ID for removal and the full credentials object for import, which is more robust than the previous approach.

Also applies to: 1762-1763


1265-1266: LGTM: Proper recommendation validation.

The changes correctly check that the recommendation swap is nil, which aligns with the expected behavior after swap execution.


1849-1849: LGTM: Optional BroadcastTransaction mock expectation.

The .Maybe() call on the BroadcastTransaction expectation is appropriate since the call is optional depending on the test scenario.


3135-3171: LGTM: Comprehensive legacy wallet test coverage.

The new TestLegacyWallet test provides excellent coverage for the legacy wallet functionality:

  1. Type verification: Properly checks that a new wallet returns liquid_wallet.Wallet
  2. Database manipulation: Correctly marks the wallet as legacy
  3. Reinitialization: Properly reinitializes the system to test the change
  4. Legacy behavior: Verifies that the wallet now returns wallet.Wallet

The test aligns well with the retrieved learning about liquid wallet implementation and the PR's objective of supporting legacy wallet types.

internal/onchain/liquid-wallet/wallet.go (2)

271-298: Good implementation of auto-consolidation with proper fee estimation.

The auto-consolidation logic now properly uses the fee provider to estimate fees dynamically, which addresses the previous concern about hardcoded fee rates.


498-516: Correct implementation of the caller-controlled locking pattern.

The SendToAddress method properly holds the write lock throughout the transaction creation, broadcast, and marking inputs as spent, ensuring atomicity as per the established pattern.

@kilrau kilrau left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of nits only, which may also be handled as follow ups, as some are not directly related to this PR:

  • Boltz Client is logging wallet creation, but not deletion
  • boltzcli wallet send command [command options] name destination amount having command options in the middle of all that is unusual and not nice to use. I didn't figure out how to use --sweep:
./boltzcli wallet send --sweep liqui lq1....
amount required
  • wallet list shows unconfirmed balance as 0 until change tx is confirmed. That's not how wallets do it.
    After I send a tx, consuming both of my UTXOs:
wallet list
{
  "wallets": [
    {
      "id": "2",
      "name": "liqui",
      "currency": "LBTC",
      "readonly": false,
      "balance": {
        "total": "0",
        "confirmed": "0",
        "unconfirmed": "0"
      },
      "tenantId": "1"
    }
  ]
}

After my tx confirmed:

{
  "wallets": [
    {
      "id": "2",
      "name": "liqui",
      "currency": "LBTC",
      "readonly": false,
      "balance": {
        "total": "17",
        "confirmed": "0",
        "unconfirmed": "17"
      },
      "tenantId": "1"
    }
  ]
}

@jackstar12

Copy link
Copy Markdown
Member Author
  • wallet list shows unconfirmed balance as 0 until change tx is confirmed. That's not how wallets do it.
    After I send a tx, consuming both of my UTXOs:

Not until the change TX is confirmed, but until it gets picked up by the wallet - your example shows the balance in the unconfirmed section. This is a result of us immediately marking the utxos as spent internally, deducting them from the balance but the change tx not being picked up immediately since we sync on interval.

@jackstar12

Copy link
Copy Markdown
Member Author

[ ] boltzcli wallet send command [command options] name destination amount having command options in the middle of all that is unusual and not nice to use. I didn't figure out how to use --sweep:

Your shown usage here is actually right and is already fixed on master. The options being forced before the positional arguments comes from the CLI lib, which is actually somewhat common.

@kilrau

kilrau commented Jul 16, 2025

Copy link
Copy Markdown
Member
  • wallet list shows unconfirmed balance as 0 until change tx is confirmed. That's not how wallets do it.
    After I send a tx, consuming both of my UTXOs:

Not until the change TX is confirmed, but until it gets picked up by the wallet - your example shows the balance in the unconfirmed section. This is a result of us immediately marking the utxos as spent internally, deducting them from the balance but the change tx not being picked up immediately since we sync on interval.

I see, now looking at it again, you are right. I just wonder how GDK handled change txes as it was picking up transactions not exactly timely either, but there was never a balance drop of the entire UTXO visible anywhere. As LWK knows the change tx, it should handle this smarter and immediately internally and not rely on the change tx to be picked up from an external source for the balance to go back to normal. The current behavior will always cause temporary balance drops, even if LWK picks up the change tx very quickly. Surely an LWK thing, but I am not aware of any other wallet lib that behaves that way, so please look into how e.g. GDK does it and at least open an issue on LWK side to change this behavior down the road.

Not a blocker for the PR though.

@kilrau

kilrau commented Jul 16, 2025

Copy link
Copy Markdown
Member

[ ] boltzcli wallet send command [command options] name destination amount having command options in the middle of all that is unusual and not nice to use. I didn't figure out how to use --sweep:

Your shown usage here is actually right and is already fixed on master. The options being forced before the positional arguments comes from the CLI lib, which is actually somewhat common.

Goodie!

@kilrau

kilrau commented Jul 16, 2025

Copy link
Copy Markdown
Member
  • Boltz Client is logging wallet creation, but not deletion

This one you'll add?

@jackstar12

Copy link
Copy Markdown
Member Author
  • wallet list shows unconfirmed balance as 0 until change tx is confirmed. That's not how wallets do it.
    After I send a tx, consuming both of my UTXOs:

Not until the change TX is confirmed, but until it gets picked up by the wallet - your example shows the balance in the unconfirmed section. This is a result of us immediately marking the utxos as spent internally, deducting them from the balance but the change tx not being picked up immediately since we sync on interval.

I see, now looking at it again, you are right. I just wonder how GDK handled change txes as it was picking up transactions not exactly timely either, but there was never a balance drop of the entire UTXO visible anywhere. As LWK knows the change tx, it should handle this smarter and immediately internally and not rely on the change tx to be picked up from an external source for the balance to go back to normal. The current behavior will always cause temporary balance drops, even if LWK picks up the change tx very quickly. Surely an LWK thing, but I am not aware of any other wallet lib that behaves that way, so please look into how e.g. GDK does it and at least open an issue on LWK side to change this behavior down the road.

Not a blocker for the PR though.

This behaviour is a result of our own logic. We immediately mark the utxos as spent to prevent potential double spend errors. If we weren't doing that, the balance would simply stay constant until the TX is picked up by the wallet, similar to GDK. But we can optimize that by giving LWK the transaction right after broadcasting it, so it can update its internal state. Lets bump the sync interval to 15s for first pre-release and get that done for official release

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
internal/rpcserver/rpcserver_test.go (1)

1897-1897: Consider if the sleep duration is sufficient for test stability.

A 1-second sleep after successful reverse swap state assertion may be addressing timing issues, but consider if this duration is adequate for all test environments.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6eb6965 and da94b11.

📒 Files selected for processing (3)
  • docs/grpc.md (1 hunks)
  • internal/rpcserver/router.go (19 hunks)
  • internal/rpcserver/rpcserver_test.go (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/grpc.md
  • internal/rpcserver/router.go
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#502
File: internal/onchain/liquid-wallet/wallet.go:425-442
Timestamp: 2025-07-11T16:16:33.431Z
Learning: In liquid-wallet implementation, the spentOutputs map uses caller-controlled locking pattern where SendToAddress holds write lock from createTransaction call through broadcast and marking inputs as spent to ensure atomicity. The createTransaction method operates under this protection without additional locking to maintain the atomic operation.
internal/rpcserver/rpcserver_test.go (2)
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#487
File: internal/rpcserver/router.go:2304-2314
Timestamp: 2025-07-16T09:46:41.587Z
Learning: In the boltz-client codebase, RPC methods are protected by the macaroon permission system defined in internal/macaroons/permissions.go. Methods like GetSwapMnemonic and SetSwapMnemonic are already restricted to admin entities through the RPCServerPermissions map, so explicit isAdmin(ctx) checks are not needed in these methods.
Learnt from: jackstar12
PR: BoltzExchange/boltz-client#444
File: internal/rpcserver/router.go:2036-2052
Timestamp: 2025-05-26T09:32:25.533Z
Learning: In boltz-client, when handling Boltz backend connectivity during startup, the code intentionally uses different error handling strategies: logger.Fatalf for version incompatibility (fail fast approach) and retry logic for availability issues. Version incompatibility requires immediate human intervention and should not be retried.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CI (ubuntu-latest, 1.24.x)
🔇 Additional comments (8)
internal/rpcserver/rpcserver_test.go (8)

20-21: LGTM! Proper imports for new wallet implementations.

These imports are necessary for the new liquid wallet implementation and legacy wallet support being added in this PR.


34-34: LGTM! Required import for serializers functionality.

The serializers import is needed for the enhanced wallet functionality in the test suite.


1161-1186: Excellent refactoring of fundedWallet helper function.

The new implementation is more efficient and follows a logical flow:

  1. First attempts to get existing wallet by name
  2. Only imports if wallet doesn't exist
  3. Only funds if wallet balance is zero

This eliminates unnecessary wallet imports and provides better test performance.


1741-1762: Improved TestWallet function with better resource management.

The changes properly capture the wallet creation response and use it consistently throughout the test. The credential retrieval with password assertion and using the full credentials object for import are good improvements.


1849-1849: Appropriate use of Maybe() for optional mock expectation.

Adding .Maybe() to the BroadcastTransaction mock makes it optional, which is reasonable since the broadcasting behavior may vary depending on test conditions and swap logic changes.


1884-1892: Enhanced confirmation logic for different currencies.

The conditional logic properly handles BTC vs other currencies for confirmation requirements. Mining a block for BTC when not zero-conf ensures proper transaction confirmation handling.


1265-1266: Correct assertion update for autoswap behavior.

The change from checking length to checking for nil swap aligns with the new autoswap behavior where the swap field can be nil rather than an empty array.


3133-3169: Excellent test coverage for legacy wallet functionality.

The new TestLegacyWallet function properly validates the wallet type switching mechanism:

  1. Verifies initial liquid wallet implementation
  2. Tests database legacy flag modification
  3. Confirms wallet type switches to legacy implementation after reinitialization

This comprehensive test ensures the legacy wallet support works correctly.

@jackstar12 jackstar12 merged commit 4026ccd into master Jul 16, 2025
3 checks passed
@michael1011 michael1011 deleted the feat/lwk branch July 16, 2025 10:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants