Skip to content

fix: sanctions check#359

Merged
robert-zaremba merged 2 commits intomasterfrom
robert/fix-sanctions
Feb 13, 2026
Merged

fix: sanctions check#359
robert-zaremba merged 2 commits intomasterfrom
robert/fix-sanctions

Conversation

@robert-zaremba
Copy link
Contributor

@robert-zaremba robert-zaremba commented Feb 13, 2026

Summary by Sourcery

Integrate transaction-level sanctions checks into the Bitcoin indexer and simplify the compliance interface while tightening logging and test coverage.

Bug Fixes:

  • Ensure sanctioned Bitcoin transactions are skipped before processing deposits or redeems, preventing sanctioned activity from being minted.

Enhancements:

  • Refactor sanctions filtering from a block/DB-based finalized-transaction pass into a reusable isTxSanctioned helper used during per-block transaction scanning.
  • Simplify and clarify transaction grouping and logging messages for finalized deposit processing.
  • Adjust Bitcoin address extraction utilities to work directly with bitcoinjs Network objects instead of higher-level network enums.

Tests:

  • Replace end-to-end finalized-transaction sanctions test with a focused unit test for isTxSanctioned using real block data.
  • Update test helpers to mock the new compliance API shape for sanctions checks.

Signed-off-by: Robert Zaremba <robert@zaremba.ch>
@robert-zaremba robert-zaremba self-assigned this Feb 13, 2026
@robert-zaremba robert-zaremba requested a review from a team as a code owner February 13, 2026 14:13
Copilot AI review requested due to automatic review settings February 13, 2026 14:13
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 13, 2026

Reviewer's Guide

Implements sanctions checking earlier in the BTC indexing flow by screening raw transactions before mint/redeem detection, simplifies compliance API usage, and updates tests/utilities to match the new interface.

Sequence diagram for early sanctions check during BTC block processing

sequenceDiagram
  participant Indexer
  participant BlockSource
  participant ComplianceService
  participant Storage

  loop each_finalized_block
    Indexer->>BlockSource: fetchAndVerifyBlock(blockHash)
    BlockSource-->>Indexer: block

    loop each_tx_in_block
      Indexer->>ComplianceService: isAnyBtcAddressSanctioned(senderAddresses)
      alt tx_is_sanctioned
        ComplianceService-->>Indexer: true
        Indexer-->>Indexer: skip transaction
      else tx_not_sanctioned
        ComplianceService-->>Indexer: false
        Indexer->>Indexer: detectMintingTx(tx, network, blockInfo)
        alt is_mint_tx
          Indexer->>Storage: storeDeposit(tx)
        else not_mint_tx
          Indexer->>Indexer: detectRedeemTx(tx, trackedRedeems)
          alt is_redeem_tx
            Indexer->>Storage: storeRedeem(tx)
          else not_redeem_tx
            Indexer-->>Indexer: continue
          end
        end
      end
    end

    Indexer->>Storage: filterAlreadyMinted(finalizedTxs)
    Storage-->>Indexer: txsToProcess
    alt has_txs_to_process
      Indexer->>Indexer: groupTransactionsByBlock(txsToProcess)
      Indexer->>Indexer: prepareMintBatches(txsByBlock)
      Indexer->>Indexer: executeMintBatches(batches)
    else no_txs
      Indexer-->>Indexer: return
    end
  end
Loading

Updated class diagram for Indexer sanctions and address utilities

classDiagram
  class Indexer {
    - compliance: ComplianceRpc
    + processFinalizedTransactions(): Promise~void~
    + isTxSanctioned(tx: Transaction, btcNet: Network): Promise~boolean~
    + detectMintingTx(tx: Transaction, network: Network, blockInfo: unknown): Promise~Deposit[]~
    + detectRedeemTx(tx: Transaction, trackedRedeems: string[]): boolean
    - groupTransactionsByBlock(transactions: T[]): Map~string, T[]~
  }

  class ComplianceRpc {
    <<interface>>
    + isAnyBtcAddressSanctioned(btcAddresses: string[]): Promise~boolean~
  }

  class BtcAddressUtils {
    <<utility>>
    + extractSenderAddresses(tx: Transaction, btcNet: Network): string[]
  }

  class Transaction {
  }

  class Network {
  }

  class Deposit {
  }

  Indexer --> ComplianceRpc : uses
  Indexer --> BtcAddressUtils : uses
  BtcAddressUtils --> Transaction : reads
  BtcAddressUtils --> Network : uses
  Indexer --> Transaction : processes
  Indexer --> Network : uses
  Indexer --> Deposit : returns
Loading

File-Level Changes

Change Details Files
Perform sanctions checks directly on transactions during block scanning instead of filtering finalized transactions later.
  • Add isTxSanctioned helper that derives sender addresses from a transaction and calls compliance.isAnyBtcAddressSanctioned
  • Invoke isTxSanctioned in the main block transaction loop to skip sanctioned transactions before mint and redeem detection
  • Remove filterSanctionedTxs and its usage in processFinalizedTransactions, relying on early per-tx checks instead
  • Adjust logging in processFinalizedTransactions to reflect finalized deposits without separate sanctions filtering
packages/btcindexer/src/btcindexer.ts
Refactor BTC address extraction utilities to work directly with bitcoinjs Network instead of custom BtcNet types.
  • Change extractAddressFromInput and extractSenderAddresses signatures to accept bitcoinjs Network
  • Remove btcNetworkCfg/BtcNet dependency from btc-address-utils and use provided Network instance throughout
packages/btcindexer/src/btc-address-utils.ts
Update tests and helpers to reflect the new sanctions checking flow and compliance RPC interface.
  • Replace processFinalizedTransactions sanctions filtering test with a focused isTxSanctioned unit test using real block data
  • Import extractSenderAddresses into tests for potential debugging and clarity
  • Change mockComplianceService to implement isAnyBtcAddressSanctioned instead of isBtcBlocked, with a simple heuristic for flagged addresses
packages/btcindexer/src/btcindexer.test.ts
packages/btcindexer/src/btcindexer.helpers.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In the block transaction loop, deposits.concat(); is a no-op and should either be removed or replaced with deposits = deposits.concat(depositTxs) if concatenation was intended.
  • Consider marking isTxSanctioned as private async for consistency with how it’s used (await this.isTxSanctioned) and with other similar helper methods on the class.
  • In extractSenderAddresses, the parameter name btcNet now represents a Network rather than a BtcNet; renaming it (and matching the helper’s internal naming) would make the type change clearer and reduce confusion.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In the block transaction loop, `deposits.concat();` is a no-op and should either be removed or replaced with `deposits = deposits.concat(depositTxs)` if concatenation was intended.
- Consider marking `isTxSanctioned` as `private async` for consistency with how it’s used (`await this.isTxSanctioned`) and with other similar helper methods on the class.
- In `extractSenderAddresses`, the parameter name `btcNet` now represents a `Network` rather than a `BtcNet`; renaming it (and matching the helper’s internal naming) would make the type change clearer and reduce confusion.

## Individual Comments

### Comment 1
<location> `packages/btcindexer/src/btcindexer.ts:272-274` </location>
<code_context>
+				continue;
+			}
+
+			const depositTxs = await this.detectMintingTx(tx, network, blockInfo);
+			deposits.concat();
+			if (depositTxs.length > 0) {
+				deposits.push(...depositTxs);
+				continue;
</code_context>

<issue_to_address>
**issue:** The `deposits.concat()` call is a no-op and can be removed.

Since `concat` is non-mutating and its return value isn’t used, this line is effectively dead code and can be safely removed to keep the logic clear.
</issue_to_address>

### Comment 2
<location> `packages/btcindexer/src/btcindexer.ts:265-267` </location>
<code_context>
-			const txDeposits = await this.detectMintingTx(tx, network, blockInfo);
-			if (txDeposits.length > 0) {
-				deposits.push(...txDeposits);
+			// TODO: we should better optimise the check. In detectMintingTx and detectRedeemTx
+			// we are checking inputs and outputs, so ideally we don't repeat this.
+			if (await this.isTxSanctioned(tx, network)) {
+				logger.debug({ msg: "tx with sanctioned address", txId: tx.getId() });
+				continue;
</code_context>

<issue_to_address>
**question (bug_risk):** Sanctions are now checked before both mint and redeem detection, which changes behavior for redeems.

This now skips any tx from a sanctioned sender before both `detectMintingTx` and `detectRedeemTx`, so redeems from sanctioned addresses are also ignored. Please confirm whether the requirement is to block both mints and redeems, or only mints / different flows, and consider making that policy explicit or separating the checks if they should differ.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors the sanctions check mechanism in the BTC indexer to filter transactions earlier in the processing pipeline. The sanctions filtering is moved from the minting phase to the block scanning phase, preventing sanctioned transactions from being stored in the database.

Changes:

  • Moved sanctions check from processFinalizedTransactions to scanBlockTransactions for earlier filtering
  • Replaced filterSanctionedTxs method with simpler isTxSanctioned method that uses updated compliance API
  • Simplified btc-address-utils.ts to use Network type directly instead of BtcNet for better type consistency

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
packages/btcindexer/src/btcindexer.ts Refactored sanctions check to filter during block scanning; removed old filterSanctionedTxs method and added new isTxSanctioned method; cleaned up code formatting
packages/btcindexer/src/btcindexer.test.ts Updated test suite to test isTxSanctioned method directly with realistic test data
packages/btcindexer/src/btcindexer.helpers.test.ts Updated mock compliance service to use new isAnyBtcAddressSanctioned API
packages/btcindexer/src/btc-address-utils.ts Simplified function signatures to use Network type directly instead of converting from BtcNet

Signed-off-by: Robert Zaremba <robert@zaremba.ch>
@robert-zaremba robert-zaremba merged commit c333ece into master Feb 13, 2026
11 checks passed
@robert-zaremba robert-zaremba deleted the robert/fix-sanctions branch February 13, 2026 14:19
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