From 85048c959905ab8e15758bb207f602bb3de46df6 Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 12:50:07 -0700 Subject: [PATCH 1/9] test(sdk-core): cover TradingNetwork request contracts Add coverage for enterprise-scoped URLs and headers, signed allocation preparation, generated identifiers, and allocation endpoint payloads. WEB-000 PT-000000 --- .../unit/bitgo/trading/network/network.ts | 141 ++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 modules/sdk-core/test/unit/bitgo/trading/network/network.ts diff --git a/modules/sdk-core/test/unit/bitgo/trading/network/network.ts b/modules/sdk-core/test/unit/bitgo/trading/network/network.ts new file mode 100644 index 0000000000..eb3ae7cfde --- /dev/null +++ b/modules/sdk-core/test/unit/bitgo/trading/network/network.ts @@ -0,0 +1,141 @@ +/** + * @prettier + */ +import assert from 'assert'; +import sinon from 'sinon'; +import { TradingNetwork } from '../../../../../src/bitgo/trading/network/network'; + +describe('TradingNetwork', function () { + const enterpriseId = 'enterprise-id'; + const walletId = 'wallet-id'; + const signature = 'signature'; + let mockBitGo: any; + let mockWallet: any; + let requestCalls: Array<{ method: string; url: string; headers: Record; body: unknown }>; + let tradingNetwork: TradingNetwork; + let tradingAccount: { signPayload: sinon.SinonStub }; + + beforeEach(function () { + requestCalls = []; + tradingAccount = { signPayload: sinon.stub().resolves(signature) }; + mockWallet = { + id: sinon.stub().returns(walletId), + toTradingAccount: sinon.stub().returns(tradingAccount), + }; + + mockBitGo = { + microservicesUrl: sinon.stub().callsFake((path: string) => `https://microservices.example${path}`), + }; + + for (const method of ['get', 'post', 'put']) { + mockBitGo[method] = sinon.stub().callsFake((url: string) => { + const call: { method: string; url: string; headers: Record; body: unknown } = { + method, + url, + headers: {}, + body: undefined, + }; + requestCalls.push(call); + const request = { + set: sinon.stub().callsFake((name: string, value: string) => { + call.headers[name] = value; + return request; + }), + send: sinon.stub().callsFake((body: unknown) => { + call.body = body; + return request; + }), + result: sinon.stub().resolves({ ok: true }), + }; + return request; + }); + } + + tradingNetwork = new TradingNetwork(enterpriseId, mockWallet, mockBitGo); + }); + + it('uses enterprise-scoped URLs and headers for network reads', async function () { + await tradingNetwork.getBalances({ pageNumber: 2 }); + await tradingNetwork.getSettlementById({ settlementId: 'settlement-id' }); + + assert.deepStrictEqual(requestCalls, [ + { + method: 'get', + url: 'https://microservices.example/api/network/v1/enterprises/enterprise-id/clients/balances', + headers: { 'enterprise-id': enterpriseId }, + body: { pageNumber: 2 }, + }, + { + method: 'get', + url: 'https://microservices.example/api/network/v1/enterprises/enterprise-id/clients/settlements/settlement-id', + headers: { 'enterprise-id': enterpriseId }, + body: {}, + }, + ]); + }); + + it('prepares a signed allocation with generated identifiers', async function () { + const prepared = await tradingNetwork.prepareAllocation({ + connectionId: 'connection-id', + amount: { currency: 'tbtc', quantity: '100' }, + notes: 'test', + }); + + assert.strictEqual(prepared.connectionId, 'connection-id'); + assert.strictEqual(prepared.signature, signature); + assert.match(prepared.clientExternalId, /^[0-9a-f-]{36}$/); + assert.match(prepared.nonce, /^[0-9a-f]{64}$/); + assert.strictEqual(prepared.payload, JSON.stringify({ + connectionId: 'connection-id', + amount: { currency: 'tbtc', quantity: '100' }, + notes: 'test', + clientExternalId: prepared.clientExternalId, + nonce: prepared.nonce, + })); + sinon.assert.calledOnceWithExactly(tradingAccount.signPayload, { + payload: prepared.payload, + walletPassphrase: undefined, + }); + }); + + it('submits allocation and deallocation payloads to connection-scoped endpoints', async function () { + const params = { + connectionId: 'connection-id', + payload: '{}', + signature, + amount: { currency: 'tbtc', quantity: '100' }, + clientExternalId: 'external-id', + nonce: 'nonce', + }; + + await tradingNetwork.createAllocation(params); + await tradingNetwork.createDeallocation(params); + + assert.deepStrictEqual(requestCalls, [ + { + method: 'post', + url: 'https://microservices.example/api/network/v1/enterprises/enterprise-id/clients/connections/connection-id/allocations', + headers: { 'enterprise-id': enterpriseId }, + body: { + payload: params.payload, + signature: params.signature, + amount: params.amount, + clientExternalId: params.clientExternalId, + nonce: params.nonce, + }, + }, + { + method: 'post', + url: 'https://microservices.example/api/network/v1/enterprises/enterprise-id/clients/connections/connection-id/deallocations', + headers: { 'enterprise-id': enterpriseId }, + body: { + payload: params.payload, + signature: params.signature, + amount: params.amount, + clientExternalId: params.clientExternalId, + nonce: params.nonce, + }, + }, + ]); + }); +}); From c0f9da1867ba4ed25eddfae430dbe611f60133af Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 12:58:10 -0700 Subject: [PATCH 2/9] test(sdk-core): use conventional TradingNetwork test name Rename the TradingNetwork contract test to the repository's .test.ts convention and align its assertions with Prettier formatting. WEB-000 PT-000000 --- .../network/{network.ts => network.test.ts} | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) rename modules/sdk-core/test/unit/bitgo/trading/network/{network.ts => network.test.ts} (91%) diff --git a/modules/sdk-core/test/unit/bitgo/trading/network/network.ts b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts similarity index 91% rename from modules/sdk-core/test/unit/bitgo/trading/network/network.ts rename to modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts index eb3ae7cfde..74fac4f178 100644 --- a/modules/sdk-core/test/unit/bitgo/trading/network/network.ts +++ b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts @@ -11,7 +11,12 @@ describe('TradingNetwork', function () { const signature = 'signature'; let mockBitGo: any; let mockWallet: any; - let requestCalls: Array<{ method: string; url: string; headers: Record; body: unknown }>; + let requestCalls: Array<{ + method: string; + url: string; + headers: Record; + body: unknown; + }>; let tradingNetwork: TradingNetwork; let tradingAccount: { signPayload: sinon.SinonStub }; @@ -85,13 +90,16 @@ describe('TradingNetwork', function () { assert.strictEqual(prepared.signature, signature); assert.match(prepared.clientExternalId, /^[0-9a-f-]{36}$/); assert.match(prepared.nonce, /^[0-9a-f]{64}$/); - assert.strictEqual(prepared.payload, JSON.stringify({ - connectionId: 'connection-id', - amount: { currency: 'tbtc', quantity: '100' }, - notes: 'test', - clientExternalId: prepared.clientExternalId, - nonce: prepared.nonce, - })); + assert.strictEqual( + prepared.payload, + JSON.stringify({ + connectionId: 'connection-id', + amount: { currency: 'tbtc', quantity: '100' }, + notes: 'test', + clientExternalId: prepared.clientExternalId, + nonce: prepared.nonce, + }) + ); sinon.assert.calledOnceWithExactly(tradingAccount.signPayload, { payload: prepared.payload, walletPassphrase: undefined, From f49d34aead7b2dbde9ac4d6c3a2fd3d4467b0d3d Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 13:07:20 -0700 Subject: [PATCH 3/9] test(sdk-core): preserve allocation currency literal type Keep the allocation currency inferred as a supported literal so the new TradingNetwork test compiles in package builds. WEB-000 PT-000000 --- .../sdk-core/test/unit/bitgo/trading/network/network.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts index 74fac4f178..78edd112ac 100644 --- a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts +++ b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts @@ -82,7 +82,7 @@ describe('TradingNetwork', function () { it('prepares a signed allocation with generated identifiers', async function () { const prepared = await tradingNetwork.prepareAllocation({ connectionId: 'connection-id', - amount: { currency: 'tbtc', quantity: '100' }, + amount: { currency: 'tbtc', quantity: '100' } as const, notes: 'test', }); From a2aa7fd2153f876a0a8f55cedd60cd999f50ec2f Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 13:28:00 -0700 Subject: [PATCH 4/9] test(sdk-core): keep allocation fixture mutable Use the allocation amount's mutable string shape so the TradingNetwork fixture satisfies PrepareNetworkAllocationParams during package builds. WEB-000 PT-000000 --- .../sdk-core/test/unit/bitgo/trading/network/network.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts index 78edd112ac..65d109ed10 100644 --- a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts +++ b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts @@ -82,7 +82,7 @@ describe('TradingNetwork', function () { it('prepares a signed allocation with generated identifiers', async function () { const prepared = await tradingNetwork.prepareAllocation({ connectionId: 'connection-id', - amount: { currency: 'tbtc', quantity: '100' } as const, + amount: { currency: 'tbtc' as string, quantity: '100' }, notes: 'test', }); From c96aed3d2d7dcde91a7a7c7f4c8bb348751b2b95 Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 13:34:50 -0700 Subject: [PATCH 5/9] test(sdk-core): import TradingNetwork from barrel Use the public trading network module boundary in the contract test instead of importing the implementation file directly. WEB-000 PT-000000 --- .../sdk-core/test/unit/bitgo/trading/network/network.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts index 65d109ed10..58981d5cd5 100644 --- a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts +++ b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts @@ -3,7 +3,7 @@ */ import assert from 'assert'; import sinon from 'sinon'; -import { TradingNetwork } from '../../../../../src/bitgo/trading/network/network'; +import { TradingNetwork } from '../../../../../src/bitgo/trading/network'; describe('TradingNetwork', function () { const enterpriseId = 'enterprise-id'; From 322e56c371c15356a7734c972e85c1143af09c0b Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 13:40:03 -0700 Subject: [PATCH 6/9] test(sdk-core): use package source barrel import Import TradingNetwork through the sdk-core source barrel to match same-package monorepo test conventions. WEB-000 PT-000000 --- .../sdk-core/test/unit/bitgo/trading/network/network.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts index 58981d5cd5..31a07cd0ac 100644 --- a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts +++ b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts @@ -3,7 +3,7 @@ */ import assert from 'assert'; import sinon from 'sinon'; -import { TradingNetwork } from '../../../../../src/bitgo/trading/network'; +import { TradingNetwork } from '../../../../../src'; describe('TradingNetwork', function () { const enterpriseId = 'enterprise-id'; From 393a9cb71e56277aaaca9049da10392bc59a3e51 Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 13:48:17 -0700 Subject: [PATCH 7/9] fix(sdk-core): make prepared allocation ids optional Exclude generated clientExternalId and nonce fields before redeclaring them optional on PrepareNetworkAllocationParams. WEB-000 PT-000000 --- modules/sdk-core/src/bitgo/trading/network/types.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/sdk-core/src/bitgo/trading/network/types.ts b/modules/sdk-core/src/bitgo/trading/network/types.ts index 0d120fa07c..8d2d4aa7fd 100644 --- a/modules/sdk-core/src/bitgo/trading/network/types.ts +++ b/modules/sdk-core/src/bitgo/trading/network/types.ts @@ -124,7 +124,10 @@ export type GetNetworkAllocationByIdResponse = { allocation: NetworkAllocation; }; -export type PrepareNetworkAllocationParams = Omit & { +export type PrepareNetworkAllocationParams = Omit< + CreateNetworkAllocationParams, + 'payload' | 'signature' | 'clientExternalId' | 'nonce' +> & { walletPassphrase?: string; clientExternalId?: string; nonce?: string; From b84a7bdc7f299ce647c6fb64b034ec4386180e77 Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 14:00:29 -0700 Subject: [PATCH 8/9] fix(sdk-core): narrow generated allocation fields Destructure generated allocation identifiers with defaults so the prepared allocation satisfies its required submission contract after type checking. WEB-000 PT-000000 --- .../sdk-core/src/bitgo/trading/network/network.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/modules/sdk-core/src/bitgo/trading/network/network.ts b/modules/sdk-core/src/bitgo/trading/network/network.ts index 90902cac32..7b3e362e4e 100644 --- a/modules/sdk-core/src/bitgo/trading/network/network.ts +++ b/modules/sdk-core/src/bitgo/trading/network/network.ts @@ -120,20 +120,16 @@ export class TradingNetwork implements ITradingNetwork { */ async prepareAllocation({ walletPassphrase, + clientExternalId = uuidV4(), + nonce = crypto.randomBytes(32).toString('hex'), ...body }: PrepareNetworkAllocationParams): Promise { - if (!body.clientExternalId) { - body.clientExternalId = uuidV4(); - } - if (!body.nonce) { - body.nonce = crypto.randomBytes(32).toString('hex'); - } - - const payload = JSON.stringify(body); + const allocation = { ...body, clientExternalId, nonce }; + const payload = JSON.stringify(allocation); const signature = await this.wallet.toTradingAccount().signPayload({ payload, walletPassphrase }); return { - ...body, + ...allocation, payload, signature, }; From 498d7ac2e4bf7a493499cd9e4cdac4ceeba0ce83 Mon Sep 17 00:00:00 2001 From: Michael McShinsky Date: Tue, 4 Aug 2026 14:43:32 -0700 Subject: [PATCH 9/9] test(sdk-core): use supported Sinon assertions Replace unavailable Sinon assert helpers with the assertion style used by sdk-core tests and supported by the repository's Sinon version. WEB-000 PT-000000 --- .../sdk-core/test/unit/bitgo/trading/network/network.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts index 31a07cd0ac..94c0c297e8 100644 --- a/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts +++ b/modules/sdk-core/test/unit/bitgo/trading/network/network.test.ts @@ -100,7 +100,8 @@ describe('TradingNetwork', function () { nonce: prepared.nonce, }) ); - sinon.assert.calledOnceWithExactly(tradingAccount.signPayload, { + tradingAccount.signPayload.calledOnce.should.be.true(); + tradingAccount.signPayload.firstCall.args[0].should.deepEqual({ payload: prepared.payload, walletPassphrase: undefined, });