diff --git a/modules/bitgo/test/v2/unit/wallet.ts b/modules/bitgo/test/v2/unit/wallet.ts index 81013ade88..95bff09e69 100644 --- a/modules/bitgo/test/v2/unit/wallet.ts +++ b/modules/bitgo/test/v2/unit/wallet.ts @@ -1994,6 +1994,37 @@ describe('V2 Wallet:', function () { createShareNock.isDone().should.be.True(); }); + it('should share wallet when key fetch returns 404 (cold wallet with missing key records)', async function () { + const userId = '123'; + const email = 'shareto@sdktest.com'; + const permissions = 'view,spend'; + + const getSharingKeyNock = nock(bgUrl).post('/api/v1/user/sharingkey', { email }).reply(200, { userId }); + + // Simulate cold storage wallets where key records do not exist on the backend (404) + const getKeyNock = nock(bgUrl) + .get(`/api/v2/tbtc/key/${coldWallet.keyIds()[0]}`) + .reply(404, { error: 'key not found' }) + .get(`/api/v2/tbtc/key/${coldWallet.keyIds()[1]}`) + .reply(404, { error: 'key not found' }) + .get(`/api/v2/tbtc/key/${coldWallet.keyIds()[2]}`) + .reply(404, { error: 'key not found' }); + + const createShareNock = nock(bgUrl) + .post(`/api/v2/tbtc/wallet/${coldWallet.id()}/share`, { + user: userId, + permissions, + skipKeychain: true, + }) + .reply(200, {}); + + await coldWallet.shareWallet({ email, permissions }); + + getSharingKeyNock.isDone().should.be.True(); + getKeyNock.isDone().should.be.True(); + createShareNock.isDone().should.be.True(); + }); + describe('Hot Wallet Sharing', function () { const userId = '123'; const email = 'shareto@sdktest.com'; diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index 91a2ba63da..68ed106910 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -26,6 +26,7 @@ import { BitGoBase } from '../bitgoBase'; import { getSharedSecret } from '../ecdh'; import { AddressGenerationError, + ApiResponseError, IncorrectPasswordError, MethodNotImplementedError, MissingEncryptedKeychainError, @@ -1714,7 +1715,18 @@ export class Wallet implements IWallet { const params = { id: this._wallet.keys[index] }; - const keychain = await this.baseCoin.keychains().get(params); + let keychain: Keychain; + try { + keychain = await this.baseCoin.keychains().get(params); + } catch (e) { + // A 404 means the key record does not exist on the server (e.g. cold wallet keys + // or KMS-backed keys that have no keychain entry). Treat this the same as a key + // without an encryptedPrv and try the next one. + if (e instanceof ApiResponseError && e.status === 404) { + return tryKeyChain(index + 1); + } + throw e; + } // If we find the prv, then this is probably the user keychain we're looking for if (keychain.encryptedPrv) { return keychain as KeychainWithEncryptedPrv;