diff --git a/modules/bitgo/test/v2/unit/wallets.ts b/modules/bitgo/test/v2/unit/wallets.ts index 8c72b4503c..d9daac8242 100644 --- a/modules/bitgo/test/v2/unit/wallets.ts +++ b/modules/bitgo/test/v2/unit/wallets.ts @@ -1464,7 +1464,7 @@ describe('V2 Wallets:', function () { it('should throw an error if shareOptions is empty', async () => { try { - await wallet.createBulkKeyShares({ shareOptions: [] }); + await wallet.createBulkKeyShares([]); assert.fail('Expected error not thrown'); } catch (error) { assert.strictEqual(error.message, 'shareOptions cannot be empty'); @@ -1472,15 +1472,14 @@ describe('V2 Wallets:', function () { }); it('should skip shareoption if keychain parameters are missing', async () => { - const params = { - shareOptions: [ - { - user: 'testuser@example.com', - permissions: ['spend'], - keychain: { pub: 'pubkey', encryptedPrv: '', fromPubKey: '', toPubKey: '', path: '' }, - }, - ], - }; + const params = [ + { + user: 'testuser@example.com', + permissions: ['spend'], + keychain: { pub: 'pubkey', encryptedPrv: '', fromPubKey: '', toPubKey: '', path: '' }, + }, + ]; + try { await wallet.createBulkKeyShares(params); assert.fail('Expected error not thrown'); @@ -1506,6 +1505,19 @@ describe('V2 Wallets:', function () { }, ], }; + const paramsToSend = [ + { + user: 'testuser@example.com', + permissions: ['spend'], + keychain: { + pub: 'pubkey', + encryptedPrv: 'encryptedPrv', + fromPubKey: 'fromPubKey', + toPubKey: 'toPubKey', + path: 'm/0/0', + }, + }, + ]; nock(bgUrl) .post(`/api/v2/wallet/${walletData.id}/walletshares`, params) .reply(200, { @@ -1527,7 +1539,7 @@ describe('V2 Wallets:', function () { }, ], }); - const result = await wallet.createBulkKeyShares(params); + const result = await wallet.createBulkKeyShares(paramsToSend); assert.strictEqual(result.shares[0].id, 'userId', 'The share ID should match'); assert.strictEqual(result.shares[0].coin, walletData.coin, 'The coin should match'); assert.strictEqual(result.shares[0].wallet, walletData.id, 'The wallet ID should match'); diff --git a/modules/sdk-core/src/bitgo/wallet/iWallet.ts b/modules/sdk-core/src/bitgo/wallet/iWallet.ts index a86a7ac100..2a17cb969c 100644 --- a/modules/sdk-core/src/bitgo/wallet/iWallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/iWallet.ts @@ -485,12 +485,10 @@ export interface ShareWalletOptions { disableEmail?: boolean; } -export interface BulkCreateShareOptions { - shareOptions: Array<{ - user: string; - permissions: string[]; - keychain: BulkWalletShareKeychain; - }>; +export interface BulkCreateShareOption { + user: string; + permissions: string[]; + keychain: BulkWalletShareKeychain; } export interface BulkWalletShareOptions { @@ -804,7 +802,7 @@ export interface IWallet { getPrv(params?: GetPrvOptions): Promise; createShare(params?: CreateShareOptions): Promise; shareWallet(params?: ShareWalletOptions): Promise; - createBulkKeyShares(params?: BulkCreateShareOptions): Promise; + createBulkKeyShares(params?: BulkCreateShareOption[]): Promise; bulkWalletShare(params?: BulkWalletShareOptions): Promise; removeUser(params?: RemoveUserOptions): Promise; prebuildTransaction(params?: PrebuildTransactionOptions): Promise; diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index f723a4aa6e..b3cc30f62f 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -42,7 +42,7 @@ import { CreateAddressOptions, CreatePolicyRuleOptions, CreateShareOptions, - BulkCreateShareOptions, + BulkCreateShareOption, BulkWalletShareOptions, CrossChainUTXO, DeployForwardersOptions, @@ -1486,7 +1486,8 @@ export class Wallet implements IWallet { if (!params.shareOptions || Object.keys(params.shareOptions).length === 0) { throw new Error('shareOptions cannot be empty'); } - const bulkCreateShareOptions: BulkCreateShareOptions = { shareOptions: [] }; + // const bulkCreateShareOptions: BulkCreateShareOptions = { shareOptions: [] }; + const bulkCreateShareOptions: BulkCreateShareOption[] = []; for (const shareOption of params.shareOptions) { common.validateParams(shareOption, ['userId', 'pubKey', 'path'], []); @@ -1537,7 +1538,7 @@ export class Wallet implements IWallet { throw e; } } - bulkCreateShareOptions.shareOptions.push({ + bulkCreateShareOptions.push({ user: shareOption.userId, permissions: shareOption.permissions, keychain: Object.keys(sharedKeychain ?? {}).length === 0 ? undefined : sharedKeychain, @@ -1548,25 +1549,19 @@ export class Wallet implements IWallet { } /** - * Creates bulk key shares for the given wallet. + * Creates bulk wallet share entries for specified share options. + * Filters out share options that do not contain valid keychain information or have missing keychain fields. + * If all share options are invalid or empty, it throws an error. + * Sends a POST request to create the wallet shares for valid share options. * * @async - * @param {BulkCreateShareOptions} [params={ shareOptions: [] }] - The options for creating bulk key shares. - * @param {Array} params.shareOptions - An array of share option objects containing keychain information. - * @param {Object} params.shareOptions[].keychain - The keychain object for each share option. - * @param {string} params.shareOptions[].keychain.pub - The public key of the keychain. - * @param {string} params.shareOptions[].keychain.encryptedPrv - The encrypted private key of the keychain. - * @param {string} params.shareOptions[].keychain.fromPubKey - The sender's public key. - * @param {string} params.shareOptions[].keychain.toPubKey - The recipient's public key. - * @param {string} params.shareOptions[].keychain.path - The derivation path of the keychain. - * @throws {Error} If `shareOptions` is empty or any required keychain parameter is missing. - * @returns {Promise} A promise that resolves with the result of the bulk key shares creation request. - */ - - async createBulkKeyShares( - params: BulkCreateShareOptions = { shareOptions: [] } - ): Promise { - const filteredShareOptions = params.shareOptions.filter((shareOption) => { + * @param {BulkCreateShareOption[]} [params=[]] - The array of share options to process. + * Keychain entries must include the following fields: `pub`, `encryptedPrv`, `fromPubKey`, `toPubKey`, and `path`. + * @returns {Promise} A promise resolving to the result of the wallet shares creation. + * @throws {Error} Throws an error if no valid share options are provided. + */ + async createBulkKeyShares(params: BulkCreateShareOption[] = []): Promise { + const filteredShareOptions = params.filter((shareOption) => { try { if (shareOption.keychain) { // Validate that all keychain fields are present @@ -1581,15 +1576,14 @@ export class Wallet implements IWallet { return false; } }); - params.shareOptions = filteredShareOptions; + params = filteredShareOptions; - if (!params.shareOptions || Object.keys(params.shareOptions).length === 0) { + if (!params || Object.keys(params).length === 0) { throw new Error('shareOptions cannot be empty'); } const url = this.bitgo.url(`/wallet/${this._wallet.id}/walletshares`, 2); - - return this.bitgo.post(url).send(params).result(); + return this.bitgo.post(url).send({ shareOptions: params }).result(); } /**