diff --git a/modules/bitgo/test/v2/unit/wallets.ts b/modules/bitgo/test/v2/unit/wallets.ts index d474f06368..75865a7ace 100644 --- a/modules/bitgo/test/v2/unit/wallets.ts +++ b/modules/bitgo/test/v2/unit/wallets.ts @@ -24,6 +24,7 @@ import { getSharedSecret, BulkWalletShareOptions, KeychainWithEncryptedPrv, + WalletWithKeychains, } from '@bitgo/sdk-core'; import { BitGo } from '../../../src'; import { afterEach } from 'mocha'; @@ -849,7 +850,7 @@ describe('V2 Wallets:', function () { }); }); - describe('Generate TSS MPCv2 wallet:', async function () { + describe.only('Generate TSS MPCv2 wallet:', async function () { const sandbox = sinon.createSandbox(); beforeEach(function () { @@ -858,26 +859,36 @@ describe('V2 Wallets:', function () { eth: { walletCreationSettings: { multiSigTypeVersion: 'MPCv2', + coldMultiSigTypeVersion: 'MPCv2', + custodialMultiSigTypeVersion: 'MPCv2', }, }, bsc: { walletCreationSettings: { multiSigTypeVersion: 'MPCv2', + coldMultiSigTypeVersion: 'MPCv2', + custodialMultiSigTypeVersion: 'MPCv2', }, }, polygon: { walletCreationSettings: { multiSigTypeVersion: 'MPCv2', + coldMultiSigTypeVersion: 'MPCv2', + custodialMultiSigTypeVersion: 'MPCv2', }, }, atom: { walletCreationSettings: { multiSigTypeVersion: 'MPCv2', + coldMultiSigTypeVersion: 'MPCv2', + custodialMultiSigTypeVersion: 'MPCv2', }, }, tia: { walletCreationSettings: { multiSigTypeVersion: 'MPCv2', + coldMultiSigTypeVersion: 'MPCv2', + custodialMultiSigTypeVersion: 'MPCv2', }, }, }, @@ -891,7 +902,7 @@ describe('V2 Wallets:', function () { }); ['hteth', 'tbsc', 'tpolygon', 'ttia', 'tatom'].forEach((coin) => { - it(`should create a new ${coin} TSS MPCv2 wallet`, async function () { + it(`should create a new ${coin} TSS MPCv2 hot wallet`, async function () { const testCoin = bitgo.coin(coin); const stubbedKeychainsTriplet: KeychainsTriplet = { userKeychain: { @@ -942,6 +953,130 @@ describe('V2 Wallets:', function () { params.passphrase ); }); + + it(`should create a new ${coin} TSS MPCv2 cold wallet`, async function () { + const testCoin = bitgo.coin(coin); + const bitgoKeyId = 'key123'; + const commonKeychain = '0xabc'; + + const bitgoKeyNock = nock('https://bitgo.fakeurl') + .get(`/api/v2/${coin}/key/${bitgoKeyId}`) + .times(1) + .reply(200, { + id: 'key123', + pub: 'bitgoPub', + type: 'tss', + source: 'bitgo', + commonKeychain, + }); + + const userKeyNock = nock('https://bitgo.fakeurl') + .post(`/api/v2/${coin}/key`, { + source: 'user', + keyType: 'tss', + commonKeychain, + derivedFromParentWithSeed: '37', + }) + .times(1) + .reply(200, { + id: 'userKey123', + pub: 'userPub', + type: 'tss', + source: 'user', + }); + + const backupKeyNock = nock('https://bitgo.fakeurl') + .post(`/api/v2/${coin}/key`, { + source: 'backup', + keyType: 'tss', + commonKeychain, + derivedFromParentWithSeed: '37', + }) + .times(1) + .reply(200, { + id: 'backupKey123', + pub: 'backupPub', + type: 'tss', + source: 'backup', + }); + + const walletNock = nock('https://bitgo.fakeurl') + .post(`/api/v2/${coin}/wallet`, { + label: 'tss wallet', + m: 2, + n: 3, + keys: ['userKey123', 'backupKey123', 'key123'], + type: 'cold', + multisigType: 'tss', + enterprise: 'enterprise', + walletVersion: 5, + }) + .reply(200); + + const wallets = new Wallets(bitgo, testCoin); + + const params: GenerateWalletOptions = { + label: 'tss wallet', + multisigType: 'tss' as const, + enterprise: 'enterprise', + passcodeEncryptionCode: 'originalPasscodeEncryptionCode', + walletVersion: 5, + type: 'cold', + bitgoKeyId: 'key123', + commonKeychain: '0xabc', + coldDerivationSeed: '37', + }; + + const response = (await wallets.generateWallet(params)) as WalletWithKeychains; + + bitgoKeyNock.isDone().should.be.true(); + userKeyNock.isDone().should.be.true(); + backupKeyNock.isDone().should.be.true(); + walletNock.isDone().should.be.true(); + + should.exist(response.wallet); + should.exist(response.userKeychain); + should.exist(response.backupKeychain); + should.exist(response.bitgoKeychain); + response.responseType.should.equal('WalletWithKeychains'); + response.userKeychain.id.should.equal('userKey123'); + response.backupKeychain.id.should.equal('backupKey123'); + response.bitgoKeychain.id.should.equal('key123'); + }); + + it(`should create a new ${coin} TSS MPCv2 custody wallet`, async function () { + const testCoin = bitgo.coin(coin); + const keys = ['userKey', 'backupKey', 'bitgoKey']; + + const params: GenerateWalletOptions = { + label: 'tss wallet', + passphrase: 'tss password', + multisigType: 'tss' as const, + enterprise: 'enterprise', + passcodeEncryptionCode: 'originalPasscodeEncryptionCode', + walletVersion: 5, + type: 'custodial', + }; + + const walletNock = nock('https://bitgo.fakeurl') + .post(`/api/v2/${coin}/wallet`) + .times(1) + .reply(200, { ...params, keys }); + + const wallets = new Wallets(bitgo, testCoin); + + const response = (await wallets.generateWallet(params)) as WalletWithKeychains; + + walletNock.isDone().should.be.true(); + should.exist(response.wallet); + should.exist(response.userKeychain); + should.exist(response.backupKeychain); + should.exist(response.bitgoKeychain); + response.responseType.should.equal('WalletWithKeychains'); + response.userKeychain.id.should.equal(keys[0]); + response.backupKeychain.id.should.equal(keys[1]); + response.bitgoKeychain.id.should.equal(keys[2]); + }); }); it(`should create a new hteth TSS MPCv2 wallet with walletVersion 6`, async function () { @@ -1057,42 +1192,6 @@ describe('V2 Wallets:', function () { params.passphrase ); }); - - it('should throw for a cold wallet using wallet version 5', async function () { - const hteth = bitgo.coin('hteth'); - const wallets = new Wallets(bitgo, hteth); - - await assert.rejects( - async () => { - await wallets.generateWallet({ - label: 'tss wallet', - multisigType: 'tss', - enterprise: 'enterprise', - walletVersion: 5, - type: 'cold', - }); - }, - { message: 'EVM TSS MPCv2 wallets are not supported for cold wallets' } - ); - }); - - it('should throw for a custodial wallet using wallet version 5', async function () { - const hteth = bitgo.coin('hteth'); - const wallets = new Wallets(bitgo, hteth); - - await assert.rejects( - async () => { - await wallets.generateWallet({ - label: 'tss wallet', - multisigType: 'tss', - enterprise: 'enterprise', - walletVersion: 5, - type: 'custodial', - }); - }, - { message: 'EVM TSS MPCv2 wallets are not supported for custodial wallets' } - ); - }); }); describe('Generate BLS-DKG wallet:', function () { diff --git a/modules/sdk-core/src/bitgo/wallet/wallets.ts b/modules/sdk-core/src/bitgo/wallet/wallets.ts index ad61c6b3b9..9eef8e0ee8 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallets.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallets.ts @@ -313,9 +313,6 @@ export class Wallets implements IWallets { assert(enterprise, 'enterprise is required for TSS wallet'); if (type === 'cold') { - if (params.walletVersion === 5 || params.walletVersion === 6) { - throw new Error('EVM TSS MPCv2 wallets are not supported for cold wallets'); - } // validate assert(params.bitgoKeyId, 'bitgoKeyId is required for SMC TSS wallet'); assert(params.commonKeychain, 'commonKeychain is required for SMC TSS wallet'); @@ -331,9 +328,6 @@ export class Wallets implements IWallets { } if (type === 'custodial') { - if (params.walletVersion === 5 || params.walletVersion === 6) { - throw new Error('EVM TSS MPCv2 wallets are not supported for custodial wallets'); - } return this.generateCustodialMpcWallet({ multisigType: 'tss', label, @@ -1044,6 +1038,15 @@ export class Wallets implements IWallets { const reqId = new RequestTracer(); this.bitgo.setRequestTracer(reqId); + if (multisigType === 'tss' && this.baseCoin.getMPCAlgorithm() === 'ecdsa') { + const tssSettings: TssSettings = await this.bitgo + .get(this.bitgo.microservicesUrl('/api/v2/tss/settings')) + .result(); + const multisigTypeVersion = + tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.coldMultiSigTypeVersion; + walletVersion = this.determineEcdsaMpcWalletVersion(walletVersion, multisigTypeVersion); + } + // Create MPC Keychains const bitgoKeychain = await this.baseCoin.keychains().get({ id: bitgoKeyId }); @@ -1121,6 +1124,15 @@ export class Wallets implements IWallets { const reqId = new RequestTracer(); this.bitgo.setRequestTracer(reqId); + if (multisigType === 'tss' && this.baseCoin.getMPCAlgorithm() === 'ecdsa') { + const tssSettings: TssSettings = await this.bitgo + .get(this.bitgo.microservicesUrl('/api/v2/tss/settings')) + .result(); + const multisigTypeVersion = + tssSettings.coinSettings[this.baseCoin.getFamily()]?.walletCreationSettings?.custodialMultiSigTypeVersion; + walletVersion = this.determineEcdsaMpcWalletVersion(walletVersion, multisigTypeVersion); + } + const finalWalletParams = { label, multisigType,