From 9995cb0954f0a2a2444d16eba03fff113539e4fb Mon Sep 17 00:00:00 2001 From: Marzooqa Naeema Kather Date: Mon, 24 Aug 2026 16:22:36 +0530 Subject: [PATCH] feat(sdk-core): forward enterprise on tss/settings and keychain add calls BitGoJS already had enterprise available at every call site into GET /api/v2/tss/settings and the subsequent EdDSA MPCv2 keychains.add(...) calls, but never sent it, so per-enterprise Flipt rollouts on the wallet-platform side (WCI-830) can never take effect for SDK-driven wallet/key creation. Thread it through: - Keychains.createMpc() and all 5 Wallets GET /tss/settings call sites now send enterprise as a query param when available. - EddsaMPCv2Utils threads enterprise into the user/backup keychain add() calls for both the standard and external-signer paths, including a pre-existing gap where the backup keychain never received it even though the user keychain did. Fully backward compatible: enterprise remains optional everywhere, and omitting it produces the exact same request as before. Existing nock/sinon mocks for GET /tss/settings expected no query string; loosened to .query(true) since none of those tests were asserting on query params, plus two new dedicated tests proving enterprise is forwarded when supplied and omitted when it isn't. Ticket: WCI-1358 --- modules/bitgo/test/v2/unit/keychains.ts | 39 +++++++++++++++++-- modules/bitgo/test/v2/unit/wallets.ts | 7 +++- .../sdk-core/src/bitgo/keychain/keychains.ts | 5 ++- .../src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts | 9 +++-- modules/sdk-core/src/bitgo/wallet/wallets.ts | 5 +++ .../bitgo/wallet/walletsExternalSigner.ts | 9 ++++- 6 files changed, 63 insertions(+), 11 deletions(-) diff --git a/modules/bitgo/test/v2/unit/keychains.ts b/modules/bitgo/test/v2/unit/keychains.ts index 7d612b366b..be45b22e4b 100644 --- a/modules/bitgo/test/v2/unit/keychains.ts +++ b/modules/bitgo/test/v2/unit/keychains.ts @@ -609,7 +609,7 @@ describe('V2 Keychains', function () { ['tbsc'].forEach((coin) => { it('should create ECDSA TSS Keychains', async function () { - nock(bgUrl).get('/api/v2/tss/settings').reply(200, { + nock(bgUrl).get('/api/v2/tss/settings').query(true).reply(200, { coinSettings: {}, }); sandbox.stub(ECDSAUtils.EcdsaUtils.prototype, 'createKeychains').resolves(stubbedKeychainsTriplet); @@ -623,6 +623,38 @@ describe('V2 Keychains', function () { }); }); + ['tsol'].forEach((coin) => { + it('should forward the enterprise param on the GET /tss/settings call', async function () { + // Exact query match (not .query(true)) — this only passes if `enterprise` is actually + // forwarded on the request, proving WCI-1358's fix rather than just tolerating it. + const tssSettingsNock = nock(bgUrl) + .get('/api/v2/tss/settings') + .query({ enterprise: 'enterprise-123' }) + .reply(200, { coinSettings: {} }); + sandbox.stub(EDDSAUtils.default.prototype, 'createKeychains').resolves(stubbedKeychainsTriplet); + await bitgo.coin(coin).keychains().createMpc({ + multisigType: 'tss', + passphrase: 'password', + enterprise: 'enterprise-123', + originalPasscodeEncryptionCode: 'originalPasscodeEncryptionCode', + }); + tssSettingsNock.isDone().should.be.true(); + }); + }); + + ['tsol'].forEach((coin) => { + it('should send no enterprise query param on GET /tss/settings when enterprise is not supplied', async function () { + const tssSettingsNock = nock(bgUrl).get('/api/v2/tss/settings').reply(200, { coinSettings: {} }); + sandbox.stub(EDDSAUtils.default.prototype, 'createKeychains').resolves(stubbedKeychainsTriplet); + await bitgo.coin(coin).keychains().createMpc({ + multisigType: 'tss', + passphrase: 'password', + originalPasscodeEncryptionCode: 'originalPasscodeEncryptionCode', + }); + tssSettingsNock.isDone().should.be.true(); + }); + }); + ['tsol'].forEach((coin) => { it('should pass webauthnInfo to createKeychains for EDDSA TSS', async function () { const webauthnInfo = { @@ -646,7 +678,7 @@ describe('V2 Keychains', function () { ['tbsc'].forEach((coin) => { it('should reject safe root creation when the resolved ceremony is legacy MPCv1', async function () { - nock(bgUrl).get('/api/v2/tss/settings').reply(200, { + nock(bgUrl).get('/api/v2/tss/settings').query(true).reply(200, { coinSettings: {}, }); const createKeychains = sandbox @@ -668,7 +700,7 @@ describe('V2 Keychains', function () { ['tbsc'].forEach((coin) => { it('should pass webauthnInfo to createKeychains for ECDSA TSS', async function () { - nock(bgUrl).get('/api/v2/tss/settings').reply(200, { + nock(bgUrl).get('/api/v2/tss/settings').query(true).reply(200, { coinSettings: {}, }); const webauthnInfo = { @@ -702,6 +734,7 @@ describe('V2 Keychains', function () { beforeEach(function () { nock(bgUrl) .get('/api/v2/tss/settings') + .query(true) .reply(200, { coinSettings: { eth: { diff --git a/modules/bitgo/test/v2/unit/wallets.ts b/modules/bitgo/test/v2/unit/wallets.ts index c9e2c3999f..db3a91e723 100644 --- a/modules/bitgo/test/v2/unit/wallets.ts +++ b/modules/bitgo/test/v2/unit/wallets.ts @@ -570,6 +570,7 @@ describe('V2 Wallets:', function () { beforeEach(function () { nock('https://bitgo.fakeurl') .get(`/api/v2/tss/settings`) + .query(true) .times(2) .reply(200, { coinSettings: { @@ -825,6 +826,7 @@ describe('V2 Wallets:', function () { // FLRP is ECDSA, so generateCustodialMpcWallet fetches TSS settings nock('https://bitgo.fakeurl') .get('/api/v2/tss/settings') + .query(true) .reply(200, { coinSettings: { flrp: { walletCreationSettings: {} } } }); const walletNock = nock('https://bitgo.fakeurl') @@ -1052,7 +1054,7 @@ describe('V2 Wallets:', function () { }, }, }; - nock('https://bitgo.fakeurl').get(`/api/v2/tss/settings`).times(2).reply(200, tssSettings); + nock('https://bitgo.fakeurl').get(`/api/v2/tss/settings`).query(true).times(2).reply(200, tssSettings); }); afterEach(function () { @@ -1424,7 +1426,7 @@ describe('V2 Wallets:', function () { }, }, }; - nock('https://bitgo.fakeurl').get(`/api/v2/tss/settings`).times(2).reply(200, tssSettings); + nock('https://bitgo.fakeurl').get(`/api/v2/tss/settings`).query(true).times(2).reply(200, tssSettings); }); afterEach(function () { @@ -1487,6 +1489,7 @@ describe('V2 Wallets:', function () { nock.cleanAll(); nock('https://bitgo.fakeurl') .get('/api/v2/tss/settings') + .query(true) .reply(200, { coinSettings: { sol: { walletCreationSettings: {} } } }); const testCoin = bitgo.coin('tsol'); diff --git a/modules/sdk-core/src/bitgo/keychain/keychains.ts b/modules/sdk-core/src/bitgo/keychain/keychains.ts index 6eb0f235cf..67ff7bd2fd 100644 --- a/modules/sdk-core/src/bitgo/keychain/keychains.ts +++ b/modules/sdk-core/src/bitgo/keychain/keychains.ts @@ -376,7 +376,10 @@ export class Keychains implements IKeychains { throw new Error('Unsupported multi-sig type'); } - const tssSettings: TssSettings = await this.bitgo.get(this.bitgo.microservicesUrl('/api/v2/tss/settings')).result(); + const tssSettings: TssSettings = await this.bitgo + .get(this.bitgo.microservicesUrl('/api/v2/tss/settings')) + .query({ enterprise: params.enterprise }) + .result(); const multisigTypeVersion = tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.multiSigTypeVersion; diff --git a/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts b/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts index a58a38348b..edb433ac70 100644 --- a/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts +++ b/modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts @@ -214,6 +214,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { params.passphrase, params.originalPasscodeEncryptionCode, params.encryptionVersion, + params.enterprise, params.safeId ); const bitgoKeychainPromise = this.addBitgoKeychain(userCommonKeychain, params.safeId); @@ -313,8 +314,8 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { const keychains = this.baseCoin.keychains(); const [userKeychain, backupKeychain, bitgoKeychain] = await Promise.all([ - keychains.add({ source: 'user', keyType: 'tss' as KeyType, commonKeychain, isMPCv2: true }), - keychains.add({ source: 'backup', keyType: 'tss' as KeyType, commonKeychain, isMPCv2: true }), + keychains.add({ source: 'user', keyType: 'tss' as KeyType, commonKeychain, isMPCv2: true, enterprise }), + keychains.add({ source: 'backup', keyType: 'tss' as KeyType, commonKeychain, isMPCv2: true, enterprise }), this.addBitgoKeychain(commonKeychain), ]); @@ -379,6 +380,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { originalPasscodeEncryptionCode, isMPCv2: true, safeId, + enterprise, }; if (webauthnInfo && participantIndex === MPCv2PartiesEnum.USER && privateMaterialBase64) { @@ -432,6 +434,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { passphrase: string, originalPasscodeEncryptionCode?: string, encryptionVersion?: EncryptionVersion, + enterprise?: string, safeId?: string ): Promise { return this.createParticipantKeychain( @@ -443,7 +446,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils { originalPasscodeEncryptionCode, undefined, encryptionVersion, - undefined, + enterprise, safeId ); } diff --git a/modules/sdk-core/src/bitgo/wallet/wallets.ts b/modules/sdk-core/src/bitgo/wallet/wallets.ts index 770f00c602..6128ac9d17 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallets.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallets.ts @@ -131,6 +131,7 @@ export class Wallets implements IWallets { if (params.multisigType === 'tss' && this.baseCoin.getMPCAlgorithm() === 'ecdsa' && params.walletVersion === 3) { const tssSettings: TssSettings = await this.bitgo .get(this.bitgo.microservicesUrl('/api/v2/tss/settings')) + .query({ enterprise: params.enterprise }) .result(); const multisigTypeVersion = tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.multiSigTypeVersion; @@ -1800,6 +1801,7 @@ export class Wallets implements IWallets { } const tssSettings: TssSettings = await this.bitgo .get(this.bitgo.microservicesUrl('/api/v2/tss/settings')) + .query({ enterprise }) .result(); const multisigTypeVersion = tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.multiSigTypeVersion; @@ -1875,6 +1877,7 @@ export class Wallets implements IWallets { if (multisigType === 'tss' && this.baseCoin.getMPCAlgorithm() === 'ecdsa') { const tssSettings: TssSettings = await this.bitgo .get(this.bitgo.microservicesUrl('/api/v2/tss/settings')) + .query({ enterprise }) .result(); const multisigTypeVersion = tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.multiSigTypeVersion; @@ -1945,6 +1948,7 @@ export class Wallets implements IWallets { if (multisigType === 'tss' && this.baseCoin.getMPCAlgorithm() === 'ecdsa') { const tssSettings: TssSettings = await this.bitgo .get(this.bitgo.microservicesUrl('/api/v2/tss/settings')) + .query({ enterprise }) .result(); multisigTypeVersion = tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.coldMultiSigTypeVersion; @@ -2033,6 +2037,7 @@ export class Wallets implements IWallets { if (multisigType === 'tss' && this.baseCoin.getMPCAlgorithm() === 'ecdsa') { const tssSettings: TssSettings = await this.bitgo .get(this.bitgo.microservicesUrl('/api/v2/tss/settings')) + .query({ enterprise }) .result(); const multisigTypeVersion = tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.custodialMultiSigTypeVersion; diff --git a/modules/sdk-core/test/unit/bitgo/wallet/walletsExternalSigner.ts b/modules/sdk-core/test/unit/bitgo/wallet/walletsExternalSigner.ts index 8b9d9750c5..68d510fc99 100644 --- a/modules/sdk-core/test/unit/bitgo/wallet/walletsExternalSigner.ts +++ b/modules/sdk-core/test/unit/bitgo/wallet/walletsExternalSigner.ts @@ -365,7 +365,9 @@ describe('Wallets - external signer onchain wallet generation', function () { post: sinon.stub().returns({ send: sinon.stub().returns({ result: sinon.stub().resolves({ id: 'tss-wallet-id' }) }), }), - get: sinon.stub().returns({ result: sinon.stub().resolves({ coinSettings: {} }) }), + get: sinon + .stub() + .returns({ query: sinon.stub().returnsThis(), result: sinon.stub().resolves({ coinSettings: {} }) }), setRequestTracer: sinon.stub(), microservicesUrl: sinon.stub().returns('/api/v2/tss/settings'), }; @@ -546,7 +548,9 @@ describe('Wallets - external signer onchain wallet generation', function () { send = sinon.stub().returns({ result: sinon.stub().resolves({ id: 'tss-wallet-id' }) }); const integrationBitGo = { post: sinon.stub().returns({ send }), - get: sinon.stub().returns({ result: sinon.stub().resolves({ coinSettings: {} }) }), + get: sinon + .stub() + .returns({ query: sinon.stub().returnsThis(), result: sinon.stub().resolves({ coinSettings: {} }) }), setRequestTracer: sinon.stub(), microservicesUrl: sinon.stub().returns('/api/v2/tss/settings'), ...bitgoOverrides, @@ -593,6 +597,7 @@ describe('Wallets - external signer onchain wallet generation', function () { { isEVM: sinon.stub().returns(true) }, { get: sinon.stub().returns({ + query: sinon.stub().returnsThis(), result: sinon.stub().resolves({ coinSettings: { eth: { walletCreationSettings: { multiSigTypeVersion: 'MPCv2' } } }, }),