feat: don't process sanctioned BTC and SUI address#336
Conversation
Reviewer's GuideAdds sanctions-awareness to the BTC indexer and Sui minting flow by persisting sanctioned BTC/SUI addresses from OpenSanctions into a new table, wiring a scheduled job to refresh that data, checking BTC senders against sanctions before minting, and refactoring BTC network config sharing. Sequence diagram for scheduled sanctions sync jobsequenceDiagram
actor Scheduler
participant Worker as BtcIndexerWorker
participant SanctionJob as processSanctionedAddress
participant OpenSanctionsAPI
participant DB as D1Database
Scheduler->>Worker: cron 0_1_*_*_* scheduled event
Worker->>SanctionJob: processSanctionedAddress(env)
SanctionJob->>OpenSanctionsAPI: HTTP GET targets.nested.json
OpenSanctionsAPI-->>SanctionJob: Streamed JSON lines
loop Stream JSON
SanctionJob->>SanctionJob: Decode chunk and buffer lines
SanctionJob->>SanctionJob: Extract BTC and SUI addresses
end
SanctionJob->>DB: DELETE FROM SanctionedCryptoAddresses
SanctionJob->>DB: INSERT OR IGNORE BTC wallet_address rows
SanctionJob->>DB: INSERT SUI wallet_address rows
DB-->>SanctionJob: Batch completed
SanctionJob-->>Worker: Success
Worker-->>Scheduler: Log cron job finished
Sequence diagram for BTC minting flow with sanctions checksequenceDiagram
actor User
participant BTCNet as BitcoinNetwork
participant Indexer as BtcIndexer
participant SuiClient
participant DB as D1Database
participant SuiChain
User->>BTCNet: Send BTC from wallet_address
BTCNet-->>Indexer: Block with BTC transaction
Indexer->>Indexer: updateConfirmationsAndFinalize()
Indexer->>Indexer: processFinalizedTransactions()
Indexer->>SuiClient: mintNbtcBatch(args)
SuiClient->>SuiClient: extractSenderAddress(tx)
alt SegWit input
SuiClient->>SuiClient: payments.p2wpkh(pubkey, network)
else Legacy input
SuiClient->>SuiClient: script.decompile(scriptSig)
SuiClient->>SuiClient: payments.p2pkh(pubkey, network)
end
SuiClient->>DB: SELECT 1 FROM SanctionedCryptoAddresses WHERE wallet_address = btcAddress AND address_type = BTC
DB-->>SuiClient: Row or null
alt Address sanctioned
SuiClient->>Indexer: Log and skip mint for tx
Indexer-->>User: No nBTC minted for this deposit
else Address not sanctioned
SuiClient->>SuiChain: Submit nBTC mint transaction
SuiChain-->>SuiClient: SuiTxDigest
SuiClient-->>Indexer: Mint result
Indexer-->>User: nBTC minted on Sui
end
Entity relationship diagram for sanctioned crypto addresses tableerDiagram
SanctionedCryptoAddresses {
string wallet_address PK
string address_type
}
Updated class diagram for SuiClient sanctions-aware mintingclassDiagram
class SuiClient {
- Client client
- Signer signer
- NbtcPkgCfg config
- D1Database db
+ SuiClient(config NbtcPkgCfg, mnemonic string, db D1Database)
+ mintNbtcBatch(args MintBatchArg) Promise_MintBatchResult_
- extractSenderAddress(tx Transaction) Promise_string_
- isSanctioned(btcAddress string) Promise_boolean_
}
class NewSuiClientFactory {
+ NewSuiClient(mnemonic string, db D1Database) SuiClientConstructor
}
class Models {
+ btcNetworkCfg : Record_BtcNet_Network_
}
NewSuiClientFactory ..> SuiClient : creates
SuiClient ..> Models : uses btcNetworkCfg
SuiClient ..> D1Database : reads_sanctioned_addresses
class D1Database
class MintBatchArg
class NbtcPkgCfg
class Transaction
class Client
class Signer
SuiClient o-- Client
SuiClient o-- Signer
SuiClient o-- NbtcPkgCfg
SuiClient o-- D1Database
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- In
btcindexer.tstheConfirmingTxCandidateinterface is now declared twice (once withnetworkand once without), which will cause a duplicate identifier error; keep only the updated definition and remove the old one. - The
SuiClientconstructor takesdb?: D1Databasebut then assignsthis.db = db!andisSanctionedassumes it is always present; either makedbrequired everywhere or guard access tothis.dbto avoid a potential runtime failure. - In
processSanctionedAddressyou’re usingStringDecoderandBuffer, which are Node-specific; in a Workers environment you should switch toTextDecoderand work with theUint8Arraychunks directly to ensure this code runs correctly on the target platform.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `btcindexer.ts` the `ConfirmingTxCandidate` interface is now declared twice (once with `network` and once without), which will cause a duplicate identifier error; keep only the updated definition and remove the old one.
- The `SuiClient` constructor takes `db?: D1Database` but then assigns `this.db = db!` and `isSanctioned` assumes it is always present; either make `db` required everywhere or guard access to `this.db` to avoid a potential runtime failure.
- In `processSanctionedAddress` you’re using `StringDecoder` and `Buffer`, which are Node-specific; in a Workers environment you should switch to `TextDecoder` and work with the `Uint8Array` chunks directly to ensure this code runs correctly on the target platform.
## Individual Comments
### Comment 1
<location> `packages/btcindexer/src/sanction.ts:2-11` </location>
<code_context>
+import { StringDecoder } from "string_decoder";
</code_context>
<issue_to_address>
**issue (bug_risk):** Use of Node.js `StringDecoder` and `Buffer` is incompatible with the Cloudflare Workers runtime.
Workers don’t include Node core modules like `string_decoder` or `Buffer`, so this streaming code will not run there. Use the standard Web `TextDecoder` on the `Uint8Array` chunks from `reader.read()`, e.g. `const decoder = new TextDecoder(); buffer += decoder.decode(value, { stream: true });`, and remove all `Buffer` usage.
</issue_to_address>
### Comment 2
<location> `packages/btcindexer/src/sanction.ts:78-87` </location>
<code_context>
+ env.DB.prepare("DELETE FROM SanctionedCryptoAddresses"),
+ ];
+
+ // Add BTC inserts
+ btcAddresses.forEach((addr) => {
+ statements.push(
+ env.DB.prepare(
+ "INSERT OR IGNORE INTO SanctionedCryptoAddresses (wallet_address, address_type) VALUES (?, ?)",
+ ).bind(addr, "BTC"),
+ );
+ });
+
+ // Add SUI inserts
+ suiAddresses.forEach((addr) => {
+ statements.push(
+ env.DB.prepare(
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Inconsistent use of `INSERT OR IGNORE` for BTC vs SUI can cause constraint violations on duplicates.
For BTC you use `INSERT OR IGNORE`, but for SUI you use a plain `INSERT` into a table where `wallet_address` is the primary key. Any duplicate SUI address (or rerun with the same data) will cause the batch to fail with a constraint error. To avoid this and align behaviors, switch SUI inserts to `INSERT OR IGNORE` (or `ON CONFLICT DO NOTHING` when available).
</issue_to_address>
### Comment 3
<location> `packages/btcindexer/src/sui_client.ts:186-195` </location>
<code_context>
return [success, result.digest];
}
+ private async extractSenderAddress(tx: Transaction): Promise<string | null> {
+ try {
+ if (tx.ins.length === 0) return null;
+
+ const firstInput = tx.ins[0];
+ const network = btcNetworkCfg[this.config.btc_network];
+
+ if (firstInput && firstInput.witness && firstInput.witness.length >= 2) {
+ const pubKey = firstInput.witness[1];
+ const { address } = payments.p2wpkh({
+ pubkey: pubKey,
+ network: network,
+ });
+ return address || null;
+ }
+
+ const scriptSig = firstInput?.script;
+ if (scriptSig && scriptSig.length >= 65) {
+ const chunks = script.decompile(scriptSig);
+ if (!chunks) return null;
+
+ const pubKey = chunks[chunks.length - 1] as Buffer;
+
+ const { address } = payments.p2pkh({
+ pubkey: pubKey,
+ network: network,
+ });
+ return address || null;
+ }
+
+ return null;
+ } catch (e) {
+ console.error("Failed to extract sender address", e);
+ return null;
</code_context>
<issue_to_address>
**nitpick:** Falling back to `console.error` here is inconsistent with the rest of the logging approach.
This method is the only place using `console.error` instead of `logger`/`logError`. For consistency and better observability/structured logging—especially given the sanctions impact—please route this through `logError` or `logger.error` and include relevant context.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
d9d9ce2 to
9813c3d
Compare
There was a problem hiding this comment.
Pull request overview
Adds sanctions-awareness to the BTC indexer by ingesting OFAC-related wallet data into D1 and skipping Sui minting when the originating BTC sender address is sanctioned (plus wiring a daily cron to refresh the dataset).
Changes:
- Add a daily scheduled job to download/parse OFAC sanctions data and persist BTC/SUI addresses to a new D1 table.
- Add BTC sender extraction + sanctions lookup to skip mint calls originating from sanctioned wallets.
- Refactor BTC network config into a shared
btcNetworkCfgmodel and wireenv.DBinto the Sui client.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/lib/src/test-helpers/init_db.ts | Adds the new sanctions table to test DB cleanup/init helpers. |
| packages/btcindexer/wrangler.jsonc | Adds a daily cron trigger for sanctions ingestion. |
| packages/btcindexer/src/sui_client.ts | Injects D1 DB handle and checks sanctions before adding mint calls. |
| packages/btcindexer/src/sanction.ts | New: downloads/parses sanctions dataset and updates D1 table. |
| packages/btcindexer/src/models.ts | New shared btcNetworkCfg mapping for bitcoinjs-lib networks. |
| packages/btcindexer/src/index.ts | Routes scheduled events by cron string and runs sanctions ingestion daily. |
| packages/btcindexer/src/btcindexer.ts | Passes env.DB into SuiClient and imports shared btcNetworkCfg. |
| packages/btcindexer/db/migrations/0001_initial_schema.sql | Adds SanctionedCryptoAddresses table + index to schema. |
faabbac to
916b2de
Compare
a4df735 to
3c25424
Compare
robert-zaremba
left a comment
There was a problem hiding this comment.
This doesn't look good
- code is not clear and good for testing
- need to improve code organization
- functions are too complex rather being broken down
984c3c9 to
773d99b
Compare
Signed-off-by: Ravindra Meena <rmeena840@gmail.com>
773d99b to
721b792
Compare
Signed-off-by: Ravindra Meena <rmeena840@gmail.com>
Signed-off-by: Ravindra Meena <rmeena840@gmail.com>
Signed-off-by: Ravindra Meena <rmeena840@gmail.com>
Description
Closes: #323
Author Checklist
All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.
I have...
!to the type prefix if API or client breaking changeCHANGELOG.mdSummary by Sourcery
Integrate sanctions data into the BTC indexer to prevent minting for sanctioned BTC and SUI addresses and wire this into scheduled processing and Sui transaction handling.
New Features:
Enhancements:
Chores: