Skip to content

fix: bump polkadot 16 for tDOT recovery signatures - #9562

Merged
vibhavgo merged 1 commit into
masterfrom
feat/sdk-coin-dot/fix-recovery-payload
Aug 25, 2026
Merged

fix: bump polkadot 16 for tDOT recovery signatures#9562
vibhavgo merged 1 commit into
masterfrom
feat/sdk-coin-dot/fix-recovery-payload

Conversation

@vibhavgo

@vibhavgo vibhavgo commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

tDOT Non-BitGo recovery in Wallet Recovery Wizard was producing extrinsics the Asset Hub Westend (westmint) node rejected as InvalidTransaction::BadProof. Native-fee recoveries must sign a payload whose ChargeAssetTxPayment extra is tip + Option<AssetId> with AssetId an XCM Location, and whose CheckMetadataHash extra is mode = 0 (opt out). @polkadot/api@14.1.1 does not encode that payload the way the current runtime hashes it, so MPC signed the wrong bytes.

This PR bumps the Polkadot JS / txwrapper stack that sdk-coin-dot actually resolves at runtime, and makes recover() ignore a non-signing-material second argument that WRW used to pass.

Linear: WCI-1466

Confirmed after the local bump (same encoding this PR publishes): Asset Hub Westend extrinsic 0xef3a54…499f (balances.transfer_all, Success).

Why each change

1. Bump @polkadot/* 16.5.6 + util 14.0.3 + txwrapper 7.5.3

Needed because recover uses live getMaterial() from Asset Hub Westend. The node hashes the SCALE SignedPayload, including signed-extension extras. On Asset Hub, fee payment is ChargeAssetTxPayment: extra is compact tip plus Option<T::AssetId>, and T::AssetId is an XCM Location (native fees = None). Types 14.1.1 treat assetId as TAssetConversion / a double Option, so None vs Some(Here) is wrong and the signature does not match → BadProof.

Setting builder assetId: '0x00' is not a fix: it encodes Some(Here) and fails with 1010 Inability to pay some fees. Keep mode: 0 and omit assetId (native tip only).

polkadot-js 16.x is the line that ships Metadata v16, Asset Hub type augmentation, and the ChargeAsset / CheckMetadataHash extras current westmint expects. util/keyring 14.0.3 is the matching @polkadot/common major for api 16.

2. Root resolutions / overrides (not only sdk-coin-dot/package.json)

Needed because BitGoJS yarn resolutions were still @polkadot/api@14.1.1. A root yarn would hoist 14.x back into the published DOT package even if the module declared 16. The published tarball must resolve api 16.5.6 at runtime. tao / polyx / abstract-substrate still declare 14.1.1; they now hoist 16 via resolutions.

3. recover(params, precomputedMaterial?: EddsaSigningMaterial) type guard

Needed because recoverConsolidations passes real { version: 'v1' | 'v2', ... } as arg 2 so the keycard is not decrypted on every scan. WRW Electron historically called baseCoin.recover(params, openSSLBytes). Published 5.1.0 ignored arg 2. This source already has the second parameter; precomputedMaterial ?? decrypt treats a truthy ArrayBuffer as signing material and JSON.parse(undefined) throws.

Public type stays EddsaSigningMaterial? (same as ADA). Runtime isEddsaSigningMaterial ignores anything that is not v1 userPrv / v2 encryptedUserKey. Do not put ArrayBuffer on the API.

4. Tests on existing unsigned transferAll / recover sweep paths

Needed because ChargeAsset None must stay encoded that way (regression for the 1010 Some(Here) path). Assertions live on the sibling builder/recover tests, not a subclass that exposes createBaseTxInfo.

References (Polkadot / polkadot-js)

Asset Hub fee extra and Location-typed assetId:

Signed payload fields including mode / CheckMetadataHash (opt out with mode = 0):

Why the JS type packages must move with the runtime:

Test Plan

  • yarn workspace @bitgo/sdk-coin-dot run unit-test (with BITGOJS_TEST_PASSWORD)
  • After publish: WRW npm install of the new @bitgo/sdk-coin-dot only (no overlay / file: link)
  • tDOT Non-BitGo recover → submit to Asset Hub Westend → Success on assethub-westend.subscan.io

Ticket: WCI-1466

westmint ChargeAssetTxPayment is tip + Option<Location>. Types 14.1.1 encodes a double Option so MPC signs a payload the node rejects (BadProof).

Co-authored-by: Cursor <cursoragent@cursor.com>
@vibhavgo

Copy link
Copy Markdown
Contributor Author

@claude

@vibhavgo
vibhavgo requested a review from Marzooqa August 25, 2026 11:38
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Claude finished @vibhavgo's task —— View job


Code Review for PR #9562

Reviewing the Polkadot dependency bump and tDOT recovery signature fixes.

Progress

  • Analyze dependency version updates in package.json files
  • Review code changes in dot.ts for type-guard implementation
  • Examine test updates and coverage
  • Check alignment with stated PR goals
  • Provide comprehensive review feedback

Review Summary

This PR successfully addresses the stated goals of bumping Polkadot dependencies to v16.x and fixing tDOT recovery issues. The implementation is well-structured and the changes are appropriate for the problem being solved.

Positive Aspects

Dependency Management:

  • Clean pinning of @PolkaDot dependencies to consistent v16.5.6 across the board
  • Proper use of both resolutions and overrides for comprehensive dependency control
  • Appropriate downgrade of @polkadot/keyring and util dependencies to v14.0.3 for compatibility

Type Safety Implementation:

  • Well-implemented isEddsaSigningMaterial type guard (dot.ts:74-88) with proper version checking
  • Clean separation of MPCv1 vs MPCv2 handling logic
  • Proper type narrowing that allows for safe access to signing material properties

Backwards Compatibility:

  • The new type guard correctly handles both v1 and v2 signing material formats
  • Non-EdDSA materials are appropriately ignored (line 423-425 in recover method)
  • Existing functionality preserved while adding new safety checks

Test Coverage:

  • New test case (dot.ts:618-638) validates the type guard behavior
  • Test confirms that non-signing material (like WRW WASM buffers) is properly ignored
  • Existing test structure maintained with appropriate stubbing

Technical Implementation Review

Type Guard Logic (dot.ts:74-88):

function isEddsaSigningMaterial(value: unknown): value is EddsaSigningMaterial {
  // Proper null/type checking before property access
  if (typeof value !== 'object' || value === null) return false;
  if (!('version' in value)) return false;
  
  // Version-specific validation with appropriate property checks
  if (value.version === 'v1') {
    return 'userPrv' in value && typeof value.userPrv === 'string';
  }
  if (value.version === 'v2') {
    return 'encryptedUserKey' in value && typeof value.encryptedUserKey === 'string';
  }
  return false;
}

This is a robust implementation that follows TypeScript best practices for type guards.

Integration in Recovery Logic:
The type guard is properly integrated in the recover method (lines 423-425), where it only uses precomputed material if it passes the type check, otherwise falls back to standard material detection.

Minor Suggestions

  1. Documentation Enhancement: Consider adding JSDoc comments to the isEddsaSigningMaterial function to document the expected formats and behavior.

  2. Test Coverage: While the new test case is good, consider adding a test case that specifically validates v1 vs v2 signing material detection to ensure both paths work correctly.

  3. Error Handling: The type guard silently returns false for unrecognized versions. This is appropriate for the current use case, but consider if logging unknown versions would be beneficial for debugging.

Test Quality (transferBuilder.ts)

The new test assertion for ChargeAsset assetId validation (lines 413-422) is excellent:

  • Properly validates that the assetId is None (null/undefined) for Asset Hub compatibility
  • Uses appropriate error messaging for debugging
  • Integrates well with existing test structure

Alignment with PR Goals

Polkadot v16 Dependency Bump: Successfully implemented across all relevant packages
tDOT Recovery Fix: Type guard prevents BadProof errors by ensuring proper signing material handling
WRW Compatibility: Non-EdDSA materials are properly ignored as intended
Asset Hub Compatibility: ChargeAsset validation ensures proper transaction format

Conclusion

This is a well-implemented fix that addresses the specific technical requirements while maintaining code quality and backwards compatibility. The dependency version management is thorough, the type safety improvements are robust, and the test coverage appropriately validates the new functionality.

Recommendation:Approve - Ready for merge after successful CI validation.


Job Run: Link | Branch: feat/sdk-coin-dot/fix-recovery-payload

@vibhavgo
vibhavgo marked this pull request as ready for review August 25, 2026 13:19
@vibhavgo
vibhavgo requested review from a team as code owners August 25, 2026 13:19
@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

WCI-1466

@vibhavgo
vibhavgo requested a review from veetragjain August 25, 2026 13:36
@vibhavgo
vibhavgo merged commit 9659afe into master Aug 25, 2026
25 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