diff --git a/modules/bitgo/test/v2/unit/unspents.ts b/modules/bitgo/test/v2/unit/unspents.ts index 52b73d1348..f37e295eee 100644 --- a/modules/bitgo/test/v2/unit/unspents.ts +++ b/modules/bitgo/test/v2/unit/unspents.ts @@ -157,4 +157,47 @@ describe('Verify string type is used for value of unspent', function () { buildScope.done(); }); }); + + describe('Unspent Reservations', function () { + after(nock.cleanAll); + + it('should only pass through the whitelisted properties', async function () { + const unspentIds = ['test-test']; + const expireTime = 'boogie'; + + // Create + const createScope = nock(bgUrl) + .post(`/api/v2/wallet/${wallet.id()}/reservedunspents`, { unspentIds, expireTime }) + .reply(200, {}); + await wallet.manageUnspentReservations({ + create: { unspentIds, expireTime, dontIncludeThis: 'this' } as unknown as { + unspentIds: string[]; + expireTime: string; + }, + }); + + // Modify + const modifyScope = nock(bgUrl) + .put(`/api/v2/wallet/${wallet.id()}/reservedunspents`, { unspentIds, changes: { expireTime } }) + .reply(200, {}); + await wallet.manageUnspentReservations({ + modify: { unspentIds, changes: { expireTime }, dontIncludeThis: 'this' } as unknown as { + unspentIds: string[]; + changes: { expireTime: string }; + }, + }); + + // Delete + const deleteScope = nock(bgUrl) + .delete(`/api/v2/wallet/${wallet.id()}/reservedunspents?id=test-test`) + .reply(200, {}); + await wallet.manageUnspentReservations({ + delete: { id: unspentIds[0], dontIncludeThis: 'this' } as unknown as { id: string }, + }); + + createScope.done(); + modifyScope.done(); + deleteScope.done(); + }); + }); }); diff --git a/modules/sdk-core/src/bitgo/wallet/iWallet.ts b/modules/sdk-core/src/bitgo/wallet/iWallet.ts index b80ae7310f..608a8fa652 100644 --- a/modules/sdk-core/src/bitgo/wallet/iWallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/iWallet.ts @@ -297,6 +297,22 @@ export interface UnspentsOptions extends PaginationOptions { chains?: number[]; } +export interface ManageUnspentReservationOptions { + create?: { + unspentIds: string[]; + expireTime: string; + }; + modify?: { + unspentIds: string[]; + changes: { + expireTime: string; + }; + }; + delete?: { + id: string; + }; +} + export interface ConsolidateUnspentsOptions extends WalletSignTransactionOptions { walletPassphrase?: string; xprv?: string; diff --git a/modules/sdk-core/src/bitgo/wallet/wallet.ts b/modules/sdk-core/src/bitgo/wallet/wallet.ts index f57976c3da..9d7a88e28e 100644 --- a/modules/sdk-core/src/bitgo/wallet/wallet.ts +++ b/modules/sdk-core/src/bitgo/wallet/wallet.ts @@ -96,6 +96,7 @@ import { CreateBulkWalletShareListResponse, SharedKeyChain, BulkWalletShareKeychain, + ManageUnspentReservationOptions, } from './iWallet'; import { StakingWallet } from '../staking'; import { Lightning } from '../lightning/custodial'; @@ -740,6 +741,34 @@ export class Wallet implements IWallet { return Array.isArray(buildResponse) ? response : response[0]; } + /** + * Manage the unspent reservations on the wallet + * + * @param params.create - create a new reservation + * @param params.modify - modify an existing reservation + * @param params.delete - delete an existing reservation + */ + async manageUnspentReservations( + params: ManageUnspentReservationOptions + ): Promise<{ unspents: { id: string; walletId: string; expireTime: string; userId?: string }[] }> { + const filteredParams = _.pick(params, ['create', 'modify', 'delete']); + this.bitgo.setRequestTracer(new RequestTracer()); + // The URL cannot contain the coinName, so we remove it from the URL + const url = this.url(`/reservedunspents`).replace(`/${this.baseCoin.getChain()}`, ''); + if (filteredParams.create) { + const filteredCreateParams = _.pick(params.create, ['unspentIds', 'expireTime']); + return this.bitgo.post(url).send(filteredCreateParams).result(); + } else if (filteredParams.modify) { + const filteredModifyParams = _.pick(params.modify, ['unspentIds', 'changes']); + return this.bitgo.put(url).send(filteredModifyParams).result(); + } else if (filteredParams.delete) { + const filteredDeleteParams = _.pick(params.delete, ['id']); + return this.bitgo.del(url).query(filteredDeleteParams).result(); + } else { + throw new Error('Did not detect a creation, modification, or deletion request.'); + } + } + /** * Consolidate unspents on a wallet *