Skip to content

feat(flrC): support custom derivation path #8439

Closed
ashutoshkumar-6 wants to merge 1 commit intomasterfrom
fix/SI-287-flr-p-derived-wallet-m0-hardcode
Closed

feat(flrC): support custom derivation path #8439
ashutoshkumar-6 wants to merge 1 commit intomasterfrom
fix/SI-287-flr-p-derived-wallet-m0-hardcode

Conversation

@ashutoshkumar-6
Copy link
Copy Markdown
Contributor

@ashutoshkumar-6 ashutoshkumar-6 commented Apr 8, 2026

Why this PR exists

When a user stakes on FLR P (Flare Platform/UTXO chain), the staking protocol pays rewards to an FLR C (Flare Contract/EVM chain) address. That reward address is derived from the root public key (`m`) of the FLR P wallet's MPC key material — not from the child key at `m/0`.

BitGo's normal FLR C TSS wallet creation derives the base address at `m/0`. This produces a different address than where staking rewards land. To fix this, wallet-platform (bitgo-microservices) creates a special FLR C wallet whose base address is derived from the root key (`m`) of the paired FLR P wallet, and stores `baseAddressDerivationPath: 'm'` in the wallet's `coinSpecific`.

A few utility functions in BitGoJS hardcode `m/0` and need to be updated to respect the wallet's configured derivation path. That is what this PR does.


What changed and why

1. Address verification — `addressVerification.ts`

`verifyMPCWalletAddress` always constructed the path as `m/{index}`, so verifying the base address (index 0) would check `m/0` — which is the wrong key for FLR P-derived wallets. Now reads `baseAddressDerivationPath` from params and uses `m` when it equals `'m'` and index is 0.

2. Recovery — `abstractEthLikeNewCoins.ts`

`recoverTSS` hardcoded `MPC.deriveUnhardened(commonKeyChain, 'm/0')`. For FLR P-derived wallets, recovery would compute the wrong address and sign with the wrong key. Now reads `params.baseAddressDerivationPath` with `'m/0'` as the default.

3. Recovery DSG setup — `ecdsaMPCv2.ts` (`signRecoveryMpcV2`)

The recovery signing function hardcoded `'m/0'` in all three `DklsDsg` / `verifyAndConvertDklsSignature` call-sites. Now accepts an optional `derivationPath` parameter (default `'m/0'`), threaded through from `recoverTSS`.

4. Message signing fallback — `ecdsaMPCv2.ts`

Message signing defaulted to `'m/0'` when no `derivationPath` was present in the TxRequest messages. Now checks `wallet.coinSpecific().baseAddressDerivationPath` as a fallback before `'m/0'`.

5. `generateWallet()` — `wallets.ts` / `iWallets.ts` / `iBaseCoin.ts`

Added optional `sourceFlrpWalletId` to `GenerateWalletOptions`, `GenerateBaseMpcWalletOptions`, and `SupplementGenerateWalletOptions`, threaded through to the `/wallet/add` API call. Without this, SDK users had to call the REST API directly to create an FLR P-derived wallet.


Why this is safe and does not affect any other coin

Every change is opt-in via a new optional field. If the field is absent (which is true for every coin except this specific FLR C wallet type), behaviour is identical to before.

Change Guard condition Fallback for all other coins
Address verification `baseAddressDerivationPath === 'm'` must be explicitly passed `m/{index}` — unchanged
Recovery derivation `params.baseAddressDerivationPath` must be explicitly passed `?? 'm/0'` — unchanged
Recovery DSG 5th arg must be explicitly passed default `= 'm/0'` — unchanged
Message signing `wallet.coinSpecific().baseAddressDerivationPath` must be set on the wallet `
`generateWallet()` `sourceFlrpWalletId` is optional, ignored by all other coin handlers in wallet-platform no-op

`baseAddressDerivationPath` is a new field written to the DB only by wallet-platform's `createTssWalletFromFlrP()` method, exclusively for FLR C wallets derived from FLR P. No existing wallet document in MongoDB has this field, so all other coins — ETH, MATIC, AVAX, and every other TSS coin — continue to use `m/0` exactly as before.

The double-gate in wallet-platform (`isStakingWallet === true && baseAddressDerivationPath === 'm'`) provides an additional layer so the signing path change cannot be triggered by any wallet that was not explicitly created through this new flow.


Test plan

  • 7 new unit tests for `verifyMPCWalletAddress` path-selection logic covering: root path (`m`), standard path (`m/0`), non-zero indices, SMC prefix, and string-index variants
  • All existing address verification and recovery tests pass without regression
  • Manually verify `generateWallet()` passes `sourceFlrpWalletId` through to the API call

Ticket: SI-271

🤖 Generated with Claude Code

@linear
Copy link
Copy Markdown

linear bot commented Apr 8, 2026

@ashutoshkumar-6 ashutoshkumar-6 changed the title fix(sdk-core,abstract-eth): fix hardcoded m/0 for FLR P-derived wallets (SI-287) feat(flrC): support custom derivation path Apr 8, 2026
@ashutoshkumar-6 ashutoshkumar-6 force-pushed the fix/SI-287-flr-p-derived-wallet-m0-hardcode branch 2 times, most recently from df1c1d8 to 4735227 Compare April 9, 2026 07:16
recovery for FLR P-derived wallets

- verifyMPCWalletAddress now reads baseAddressDerivationPath and
  uses 'm' for index 0 when path is 'm'
- abstractEthLikeNewCoins recovery reads derivation path from
  wallet coinSpecific instead of hardcoding 'm/0'
- ecdsaMPCv2 recovery DSG setup reads derivation path from wallet
  coinSpecific instead of hardcoding 'm/0'
- Message signing defaults to wallet's configured derivation path
- generateWallet accepts sourceFlrpWalletId and passes it to API

SI-287
@bitgo-ai-agent-dev bitgo-ai-agent-dev bot force-pushed the fix/SI-287-flr-p-derived-wallet-m0-hardcode branch from 4735227 to 6a3f3dd Compare April 9, 2026 07:41
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.

1 participant