Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions examples/ts/reserve-unspents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ async function releaseUnspentReservation() {
console.log('released ' + JSON.stringify(reserveResult, null, 2));
}

async function listUnspentReservations() {
bitgo.authenticateWithAccessToken({ accessToken });
const wallet = await bitgo.coin(coin).wallets().get({ id: walletId });

const listResult = await wallet.manageUnspentReservations({
list: { limit: 25 },
});
console.log('reserved unspents: ' + JSON.stringify(listResult, null, 2));
}

// createUnspentReservation().catch(console.error);
// modifyUnspentReservation().catch(console.error);
// releaseUnspentReservation().catch(console.error);
// listUnspentReservations().catch(console.error);
54 changes: 54 additions & 0 deletions modules/bitgo/test/v2/unit/unspents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,63 @@ describe('Verify string type is used for value of unspent', function () {
delete: { id: unspentIds[0], dontIncludeThis: 'this' } as unknown as { id: string },
});

// List
const listScope = nock(bgUrl)
.get(`/api/v2/wallet/${wallet.id()}/reservedunspents`)
.query({ limit: '10', prevId: 'prev-123' })
.reply(200, { unspents: [], nextBatchPrevId: undefined });
await wallet.manageUnspentReservations({
list: { limit: 10, prevId: 'prev-123', dontIncludeThis: 'this' } as unknown as {
limit: number;
prevId: string;
},
});

createScope.done();
modifyScope.done();
deleteScope.done();
listScope.done();
});

it('should list reserved unspents with pagination', async function () {
const mockUnspents = [
{ id: 'txid:0', walletId: wallet.id(), expireTime: '2030-01-01T00:00:00.000Z' },
{ id: 'txid:1', walletId: wallet.id(), expireTime: '2030-01-01T00:00:00.000Z' },
];
const mockNextBatchPrevId = 'next-page-cursor';

const listScope = nock(bgUrl)
.get(`/api/v2/wallet/${wallet.id()}/reservedunspents`)
.query({ limit: '2' })
.reply(200, { unspents: mockUnspents, nextBatchPrevId: mockNextBatchPrevId });

const result = await wallet.manageUnspentReservations({ list: { limit: 2 } });

result.should.deepEqual({ unspents: mockUnspents, nextBatchPrevId: mockNextBatchPrevId });
listScope.done();
});

it('should list reserved unspents filtering by expireTimeGt', async function () {
const expireTimeGt = '2025-01-01T00:00:00.000Z';
const mockUnspents = [
{ id: 'txid:2', walletId: wallet.id(), expireTime: '2030-06-01T00:00:00.000Z' },
];

const listScope = nock(bgUrl)
.get(`/api/v2/wallet/${wallet.id()}/reservedunspents`)
.query({ expireTimeGt })
.reply(200, { unspents: mockUnspents });

const result = await wallet.manageUnspentReservations({ list: { expireTimeGt } });

result.should.deepEqual({ unspents: mockUnspents });
listScope.done();
});

it('should throw when no operation is provided', async function () {
await wallet
.manageUnspentReservations({})
.should.be.rejectedWith('Did not detect a creation, modification, deletion, or list request.');
});
});
});
20 changes: 20 additions & 0 deletions modules/sdk-core/src/bitgo/wallet/iWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,13 @@ export interface UnspentsOptions extends PaginationOptions {
unspentIds?: string[];
}

export interface ReservedUnspent {
id: string;
walletId: string;
expireTime: string;
userId?: string;
}

export interface ManageUnspentReservationOptions {
create?: {
unspentIds: string[];
Expand All @@ -509,6 +516,16 @@ export interface ManageUnspentReservationOptions {
delete?: {
id: string;
};
list?: {
limit?: number;
prevId?: string;
expireTimeGt?: string;
};
}

export interface ListReservedUnspentsResponse {
unspents: ReservedUnspent[];
nextBatchPrevId?: string;
}

export interface ConsolidateUnspentsOptions extends WalletSignTransactionOptions {
Expand Down Expand Up @@ -1143,6 +1160,9 @@ export interface IWallet {
unspents(params?: UnspentsOptions): Promise<any>;
consolidateUnspents(params?: ConsolidateUnspentsOptions): Promise<unknown>;
fanoutUnspents(params?: FanoutUnspentsOptions): Promise<unknown>;
manageUnspentReservations(
params: ManageUnspentReservationOptions
): Promise<{ unspents: ReservedUnspent[] } | ListReservedUnspentsResponse>;
updateTokenFlushThresholds(thresholds?: any): Promise<any>;
updateForwarders(forwarderFlags?: any): Promise<any>;
deployForwarders(params: DeployForwardersOptions): Promise<any>;
Expand Down
21 changes: 11 additions & 10 deletions modules/sdk-core/src/bitgo/wallet/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ import {
GetTransferOptions,
GetUserPrvOptions,
type IWallet,
ListReservedUnspentsResponse,
ManageUnspentReservationOptions,
ReservedUnspent,
MaximumSpendable,
MaximumSpendableOptions,
ModifyWebhookOptions,
Expand Down Expand Up @@ -893,16 +895,12 @@ export class Wallet implements IWallet {
* @param params.create - create a new reservation
* @param params.modify - modify an existing reservation
* @param params.delete - delete an existing reservation
* @param params.list - list existing reservations
*/
async manageUnspentReservations(params: ManageUnspentReservationOptions): Promise<{
unspents: {
id: string;
walletId: string;
expireTime: string;
userId?: string;
}[];
}> {
const filteredParams = _.pick(params, ['create', 'modify', 'delete']);
async manageUnspentReservations(
params: ManageUnspentReservationOptions
): Promise<{ unspents: ReservedUnspent[] } | ListReservedUnspentsResponse> {
const filteredParams = _.pick(params, ['create', 'modify', 'delete', 'list']);
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()}`, '');
Expand All @@ -915,8 +913,11 @@ export class Wallet implements IWallet {
} else if (filteredParams.delete) {
const filteredDeleteParams = _.pick(params.delete, ['id']);
return this.bitgo.del(url).query(filteredDeleteParams).result();
} else if (filteredParams.list) {
const filteredListParams = _.pick(params.list, ['limit', 'prevId', 'expireTimeGt']);
return this.bitgo.get(url).query(filteredListParams).result();
} else {
throw new Error('Did not detect a creation, modification, or deletion request.');
throw new Error('Did not detect a creation, modification, deletion, or list request.');
}
}

Expand Down
Loading