Skip to content

feat(abstract-utxo): implement deriveAddress for fixed-script UTXO wallets#9018

Merged
rajangarg047 merged 1 commit into
masterfrom
rajangarg047/wcn-915-btc-derive-address
Jun 16, 2026
Merged

feat(abstract-utxo): implement deriveAddress for fixed-script UTXO wallets#9018
rajangarg047 merged 1 commit into
masterfrom
rajangarg047/wcn-915-btc-derive-address

Conversation

@rajangarg047

Copy link
Copy Markdown
Contributor

Summary

Implements deriveAddress on AbstractUtxoCoin (Stage B), overriding the BaseCoin default. It locally derives a 2-of-3 multisig receive address from the xpub triple + chain/index, delegating to the existing generateAddress used by the isWalletAddress verification path — so derive and verify can never diverge. Offline and key-material-free (public keys only). The chain code selects the script type (P2SH, P2WSH/bech32, P2TR) with an optional format override.

Context

FR-465 Phase 1, Stage B. Builds only on the merged primitive (WCN-912) — independent of the Express endpoint (WCN-914) and the other coin PRs. Per Bullish scope, tests focus on legacy P2SH (chain 0) and bech32 P2WSH (chain 20); Taproot is supported by generateAddress for free but not exercised in fixtures.

Test plan

  • tsc --noEmit clean for abstract-utxo
  • eslint clean (0 errors)
  • 5 new deriveAddress tests pass: derived address matches generateAddress for P2SH (chain 0) and P2WSH (chain 20), derive→verify round-trip for both, and missing-keychains error.
  • CI

🤖 Generated with Claude Code

…llets

Override BaseCoin.deriveAddress on AbstractUtxoCoin to locally derive a 2-of-3
multisig receive address from the xpub triple and a chain/index, delegating to
the existing generateAddress used by the isWalletAddress verification path, so
derive and verify can never diverge. Offline and key-material-free (public keys
only). The chain code selects the script type (P2SH, P2WSH/bech32, P2TR) with an
optional format override.

Adds unit coverage for legacy P2SH (chain 0) and bech32 P2WSH (chain 20):
derived address matches generateAddress, plus a derive->verify round-trip.

WCN-915

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rajangarg047 rajangarg047 requested a review from a team as a code owner June 12, 2026 19:52
@linear-code

linear-code Bot commented Jun 12, 2026

Copy link
Copy Markdown

WCN-915

@rajangarg047 rajangarg047 marked this pull request as draft June 12, 2026 19:52
@rajangarg047 rajangarg047 marked this pull request as ready for review June 15, 2026 18:14
@rajangarg047

Copy link
Copy Markdown
Contributor Author

@claude

@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Claude finished @rajangarg047's task —— View job


Code Review for deriveAddress Implementation

Todo List:

  • Read repository guidelines (CLAUDE.md)
  • Examine the abstractUtxoCoin.ts changes
  • Review the test file changes
  • Analyze implementation for correctness and best practices
  • Check test coverage and quality
  • Provide comprehensive review feedback

Overall Assessment ✅

This is a well-implemented feature that adds deriveAddress functionality to AbstractUtxoCoin. The implementation is clean, follows established patterns, and includes good test coverage.

Positive Aspects

🎯 Excellent Design Decisions

  • Reuses existing generateAddress function: Ensures consistency between derive and verify operations by delegating to the same core function used by isWalletAddress
  • Follows interface contract: Correctly overrides the BaseCoin.deriveAddress method with the proper types from @bitgo/sdk-core
  • Key-material free: Implementation uses only public keys (xpub triple), no private keys required

📚 Clear Documentation

  • Comprehensive JSDoc explaining the purpose, inverse relationship with isWalletAddress, and chain code mappings
  • Good inline comments explaining the type casting rationale (lines 741-742)

🧪 Solid Test Coverage

  • Tests both P2SH (chain 0) and P2WSH (chain 20) as per Bullish scope requirements
  • Round-trip verification: Tests derive→verify flow ensuring consistency
  • Error handling: Tests missing keychains scenario
  • Realism: Uses actual generateAddress for expected values rather than hardcoded strings

Technical Review

Implementation Quality (abstractUtxoCoin.ts:732-748)

async deriveAddress(params: DeriveAddressOptions): Promise<DeriveAddressResult> {
  const { keychains, chain, index, format } = params;

  if (!keychains) {
    throw new Error('missing required param keychains');
  }

  const address = generateAddress(this.name, {
    // fixed-script (multisig) coins derive from the xpub triple via `pub`
    keychains: keychains as { pub: string }[],
    chain,
    index,
    format,
  });

  return { address, chain, index };
}

Strengths:

  • Proper parameter validation
  • Logical delegation to generateAddress
  • Correct return type structure
  • Type casting is safe and documented

Test Quality (address.ts:206-231)

The test suite effectively covers:

  • Functionality: Verifies addresses match generateAddress output
  • Integration: Round-trip derive→verify for both script types
  • Edge cases: Missing keychains error handling
  • Scope adherence: Focus on P2SH and P2WSH per requirements

Potential Considerations

🤔 Type Safety (Minor)

The type cast keychains as { pub: string }[] (line 741) is necessary due to the interface difference between DeriveAddressOptions.keychains and GenerateFixedScriptAddressOptions.keychains. This is acceptable since:

  • The comment explains the reasoning
  • generateAddress validates the structure internally
  • The error handling provides clear feedback if keychains are malformed

🧐 Async vs Sync (Architectural)

The method is marked async to match the BaseCoin interface, but the implementation is synchronous. This is:

  • ✅ Correct for interface compliance
  • ✅ Future-proof for potential async operations
  • ✅ Consistent with the inheritance hierarchy

Security & Best Practices

Security Considerations

  • No exposure of private key material ✓
  • Proper input validation ✓
  • Delegated to trusted generateAddress function ✓
  • No network calls (offline operation) ✓

Code Quality

  • Follows TypeScript best practices ✓
  • Avoids any types per repo guidelines ✓
  • Clear error messages ✓
  • Consistent with existing codebase patterns ✓

Recommendation

✅ APPROVE - This PR is ready for merge. The implementation is solid, well-tested, and follows established patterns. It successfully delivers the Stage B requirements for FR-465 Phase 1.

The code demonstrates good software engineering practices with its focus on reusability, consistency, and clear documentation. The test coverage appropriately focuses on the specified scope while ensuring correctness.


• Branch: rajangarg047/wcn-915-btc-derive-address

@rajangarg047 rajangarg047 merged commit 54ab33e into master Jun 16, 2026
22 checks passed
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