feat(sdk-coin-near): MPCv2 signed hot recovery - #9485
feat(sdk-coin-near): MPCv2 signed hot recovery#9485bitgo-ai-agent-dev[bot] wants to merge 1 commit into
Conversation
ed8bc24 to
d062b1f
Compare
Add MPCv2 detection and signing to Near.recover() alongside the existing MPCv1 path. What changed: - Import getEddsaSigningMaterial and signEddsaMpcV2RecoveryTx from @bitgo/sdk-core in near.ts - Add isMpcv2SigningMaterial() private method that decrypts the user keycard once and returns true when the plaintext is CBOR (MPCv2) - Refactor signRecoveryTransaction() to accept an isMpcV2 boolean; when true it calls signEddsaMpcV2RecoveryTx (MPS DSG) instead of the legacy EDDSAMethods.getTSSSignature path - Call isMpcv2SigningMaterial() once at the top of recover() and thread the isMpcV2 flag into both the native NEAR and NEP141 FT token paths - Add three new unit tests: native MPCv2 signed recovery, NEP141 FT token MPCv2 signed recovery, and bitgoKey/commonKeyChain mismatch Why: NEAR wallets provisioned with the new Silence Labs (MPCv2) key material cannot be recovered with the Zengo-era getTSSSignature path because the keycard format is different (CBOR base64 vs JSON uShare/yShare). This adds the same MPCv2 detection+signing path that was introduced for SOL in WCI-398, enabling hot recovery for MPCv2 NEAR wallets without any new caller-visible parameters. Ticket: WCI-1223 Session-Id: 8de2a998-4754-4499-82b5-49167b8d9fd6 Task-Id: 7e0eb924-0c33-4cc6-9402-c71587bb14a5
d062b1f to
62ceec2
Compare
62ceec2 to
c4df52d
Compare
c4df52d to
e2660bc
Compare
vibhavgo
left a comment
There was a problem hiding this comment.
Code review — local review against the MPCv2 signed hot recovery quality bar established across DOT/POLYX/SUI/ADA. Both helpers (
getEddsaSigningMaterial,signEddsaMpcV2RecoveryTx) are used correctly from@bitgo/sdk-core.
1. isMpcV2 evaluated before isUnsignedSweep guard
const isUnsignedSweep = !params.userKey && !params.backupKey && !params.walletPassphrase;
const isMpcV2 = await this.isMpcv2SigningMaterial(...); // called even on unsigned sweep pathIf walletPassphrase is present but userKey is absent, the throw surfaces from inside the private isMpcv2SigningMaterial helper rather than from the main recover() guard. The error message is correct but the source is unexpected. Prefer gating the call behind !isUnsignedSweep, or add explicit guards in recover() before calling the helper.
2. No MPCv1 regression test
The describe('Recover Transactions (MPCv2):') block has no test asserting that getTSSSignature is called when an MPCv1 keycard is used. A false-positive v2 detection would silently fail with no test catching it. All six prior coins (DOT / POLYX / SUI / ADA / DOT-consolidations / SUI-consolidations) include this test.
3. No guard tests in the MPCv2 describe
Missing:
should throw 'missing userKey'whenwalletPassphraseis set butuserKeyis omittedshould throw 'missing backupKey'whenwalletPassphraseis set butbackupKeyis omitted
The existing guard tests live in the MPCv1 describe and exercise the MPCv1 code path only.
4. No cryptographic signature verification
The MPCv2 tests check result.hasOwnProperty('serializedTx') but do not verify that the produced signature bytes are a valid Ed25519 signature over the signable payload for the derived public key. tweetnacl is already in sdk-coin-near's package.json. The pattern used in prior PRs:
const rawSig = /* extract 64 bytes from result */;
const isValid = nacl.sign.detached.verify(
new Uint8Array(signablePayload),
new Uint8Array(rawSig),
new Uint8Array(Buffer.from(accountId, 'hex'))
);
isValid.should.be.true();Reviewed by Claude Code against the MPCv2 recovery PR series baseline.
What
getEddsaSigningMaterialandsignEddsaMpcV2RecoveryTxfrom@bitgo/sdk-coreinnear.tsisMpcv2SigningMaterial()private method: decrypts the user keycard once and returnstruewhen the plaintext is CBOR-encoded (MPCv2 Silence Labs format) vs JSON (MPCv1 Zengo format)signRecoveryTransaction()to accept anisMpcV2boolean: whentrue, routes tosignEddsaMpcV2RecoveryTx(MPS DSG local signing) instead of the legacyEDDSAMethods.getTSSSignaturepathisMpcV2once at the top ofrecover()and thread the flag through both the native NEAR transfer path and the NEP141 FT token recovery path (recoverNearToken)bitgoKey/commonKeyChainmismatch errorWhy
NEAR wallets provisioned with the new Silence Labs (MPCv2) key material cannot be recovered using the legacy
getTSSSignaturepath because the keycard format is different (CBOR base64 reduced share vs JSONuShare/yShare). This adds the same MPCv2 detection + signing path introduced for SOL in WCI-398, enabling hot signed recovery for MPCv2 NEAR wallets without requiring any new caller-visible parameters. Detection is automatic based on keycard format.Test plan
should route to MPCv2 path for native NEAR recovery when keycard is MPCv2— verifies signed tx returned,getTSSSignaturenot calledshould throw when MPCv2 commonKeyChain does not match bitgoKey— verifies mismatch errorshould route to MPCv2 path for NEP141 FT token recovery when keycard is MPCv2— verifies token recovery routes to MPCv2Ticket: WCI-1223