From 91e6da238dc30735bee0374d27c026b75e68a491 Mon Sep 17 00:00:00 2001 From: Pranav Jain Date: Thu, 6 Aug 2026 16:10:14 -0400 Subject: [PATCH 1/2] refactor(awm): tighten mpc finalize response codec Drop combinedKey from the EdDSA MPC finalize handler return. It's an intermediate value from keyCombine that isn't consumed by any caller (MBE only reads commonKeychain and counterpartyKeyShare), and it was passing through io-ts identity encoding on the non-exact response codec. Wrap MpcFinalizeResponseType in t.exact so the codec matches the declared contract at the JSON boundary. Tests updated accordingly. WCN-1926 --- src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts | 4 ++-- .../handlers/eddsaMPCWalletGenerationFinalize.ts | 1 - .../routers/advancedWalletManagerApiSpec.ts | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts b/src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts index f07715ad..41a9cf44 100644 --- a/src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts +++ b/src/__tests__/api/advancedWalletManager/mpcFinalize.test.ts @@ -316,7 +316,7 @@ describe('MPC Finalize', () => { // Assert the response structure result.should.have.property('statusCode', 200); - result.body.should.have.property('combinedKey'); + result.body.should.not.have.property('combinedKey'); result.body.should.have.property('source', 'user'); result.body.should.have.property( 'commonKeychain', @@ -489,7 +489,7 @@ describe('MPC Finalize', () => { }); result.should.have.property('statusCode', 200); - result.body.should.have.property('combinedKey'); + result.body.should.not.have.property('combinedKey'); result.body.should.have.property('source', 'backup'); result.body.should.have.property( 'commonKeychain', diff --git a/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationFinalize.ts b/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationFinalize.ts index ace93e99..96172b0b 100644 --- a/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationFinalize.ts +++ b/src/advancedWalletManager/handlers/eddsaMPCWalletGenerationFinalize.ts @@ -140,7 +140,6 @@ export async function eddsaFinalize(req: AwmApiSpecRouteRequest<'v1.mpc.key.fina } return { - combinedKey, counterpartyKeyShare: sourceToCounterPartyKeyShare, source, commonKeychain, diff --git a/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts b/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts index 8999ffd7..f487cb2a 100644 --- a/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts +++ b/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts @@ -228,7 +228,7 @@ const MpcFinalizeResponse = { source: t.union([t.literal('user'), t.literal('backup')]), commonKeychain: t.string, }; -const MpcFinalizeResponseType = optionalized(MpcFinalizeResponse); +const MpcFinalizeResponseType = t.exact(optionalized(MpcFinalizeResponse)); export type MpcFinalizeResponseType = t.TypeOf; const MpcV2InitializeRequest = { From 55956d89def551241d0bcf4a1399cb1af705f35c Mon Sep 17 00:00:00 2001 From: Pranav Jain Date: Thu, 6 Aug 2026 16:11:18 -0400 Subject: [PATCH 2/2] refactor(awm): enforce exact response codecs across mpc endpoints Wrap the remaining AWM MPC response codecs in t.exact so any undeclared fields are stripped at the JSON boundary. Applies to initialize/finalize for v1 and v2, plus mpc recovery. SignMpc union is intentionally left as non-exact because its branches use t.any field codecs; tightening those requires narrowing the field shapes and is tracked separately. --- .../routers/advancedWalletManagerApiSpec.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts b/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts index f487cb2a..068bf06b 100644 --- a/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts +++ b/src/advancedWalletManager/routers/advancedWalletManagerApiSpec.ts @@ -107,9 +107,11 @@ const RecoveryMpcRequest = { export type RecoveryMpcRequest = typeof RecoveryMpcRequest; const RecoveryMpcResponse: HttpResponse = { - 200: t.type({ - txHex: t.string, - }), // the full signed tx + 200: t.exact( + t.type({ + txHex: t.string, + }), + ), // the full signed tx ...ErrorResponses, }; @@ -198,7 +200,7 @@ const MpcInitializeResponse = { bitgoPayload: KeyShareType, counterPartyKeyShare: optional(KeyShareType), }; -const MpcInitializeResponseType = optionalized(MpcInitializeResponse); +const MpcInitializeResponseType = t.exact(optionalized(MpcInitializeResponse)); export type MpcInitializeResponseType = t.TypeOf; const BitGoKeychainType = t.type({ @@ -242,7 +244,7 @@ const MpcV2InitializeResponse = { encryptedData: t.string, encryptedDataKey: t.string, }; -const MpcV2InitializeResponseType = t.type(MpcV2InitializeResponse); +const MpcV2InitializeResponseType = t.exact(t.type(MpcV2InitializeResponse)); export type MpcV2InitializeResponseType = t.TypeOf; export type MpcV2RoundState = { @@ -276,7 +278,7 @@ const MpcV2RoundResponse = { broadcastMessage: optional(t.any), p2pMessages: optional(MpcV2RoundMessage), }; -const MpcV2RoundResponseType = optionalized(MpcV2RoundResponse); +const MpcV2RoundResponseType = t.exact(optionalized(MpcV2RoundResponse)); export type MpcV2RoundResponseType = t.TypeOf; const MpcV2FinalizeRequest = { @@ -293,7 +295,7 @@ const MpcV2FinalizeResponse = { commonKeychain: t.string, source: t.union([t.literal('user'), t.literal('backup')]), }; -const MpcV2FinalizeResponseType = t.type(MpcV2FinalizeResponse); +const MpcV2FinalizeResponseType = t.exact(t.type(MpcV2FinalizeResponse)); export type MpcV2FinalizeResponseType = t.TypeOf; const MpcV2RecoveryRequest = { @@ -307,7 +309,7 @@ const MpcV2RecoveryResponse = { txHex: t.string, stringifiedSignature: t.string, }; -const MpcV2RecoveryResponseType = t.type(MpcV2RecoveryResponse); +const MpcV2RecoveryResponseType = t.exact(t.type(MpcV2RecoveryResponse)); export type MpcV2RecoveryResponseType = t.TypeOf; // API Specification