Skip to content

Commit 362b0f6

Browse files
committed
refactor: fleshed out the response codec and updated tests to reflect this
TICKET: WP-5416
1 parent 7d34224 commit 362b0f6

File tree

4 files changed

+273
-6
lines changed

4 files changed

+273
-6
lines changed

modules/express/src/typedRoutes/api/v2/expressWalletUpdate.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as t from 'io-ts';
22
import { httpRoute, httpRequest, optional } from '@api-ts/io-ts-http';
33
import { BitgoExpressError } from '../../schemas/error';
4+
import { WalletResponse } from '../../schemas/wallet';
45

56
/**
67
* Parameters for Express Wallet Update
@@ -30,13 +31,13 @@ export const ExpressWalletUpdateBody = {
3031
* Response for Express Wallet Update
3132
*/
3233
export const ExpressWalletUpdateResponse = {
33-
/** Updated Wallet */
34-
200: t.UnknownRecord,
35-
/** Bad Request */
34+
/** Updated Wallet - Returns the wallet with updated Lightning signer configuration */
35+
200: WalletResponse,
36+
/** Bad Request - Invalid parameters or missing required fields */
3637
400: BitgoExpressError,
37-
/** Forbidden */
38+
/** Forbidden - Insufficient permissions to update the wallet */
3839
403: BitgoExpressError,
39-
/** Not Found */
40+
/** Not Found - Wallet not found or invalid coin type */
4041
404: BitgoExpressError,
4142
} as const;
4243

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
import * as t from 'io-ts';
2+
3+
/**
4+
* Wallet user with permissions
5+
*/
6+
export const WalletUser = t.type({
7+
user: t.string,
8+
permissions: t.array(t.string),
9+
});
10+
11+
/**
12+
* Address balance information
13+
*/
14+
export const AddressBalance = t.type({
15+
updated: t.string,
16+
balance: t.number,
17+
balanceString: t.string,
18+
totalReceived: t.number,
19+
totalSent: t.number,
20+
confirmedBalanceString: t.string,
21+
spendableBalanceString: t.string,
22+
});
23+
24+
/**
25+
* Address information
26+
*/
27+
export const ReceiveAddress = t.partial({
28+
/** Address ID */
29+
id: t.string,
30+
/** The actual address string */
31+
address: t.string,
32+
/** Chain index (0 for external, 1 for internal) */
33+
chain: t.number,
34+
/** Address index */
35+
index: t.number,
36+
/** Coin type */
37+
coin: t.string,
38+
/** Wallet ID this address belongs to */
39+
wallet: t.string,
40+
/** Last nonce used */
41+
lastNonce: t.number,
42+
/** Coin-specific address data */
43+
coinSpecific: t.UnknownRecord,
44+
/** Address balance information */
45+
balance: AddressBalance,
46+
/** Address label */
47+
label: t.string,
48+
/** Address type (e.g., 'p2sh', 'p2wsh') */
49+
addressType: t.string,
50+
});
51+
52+
/**
53+
* Policy rule for wallet
54+
*/
55+
export const PolicyRule = t.partial({
56+
/** Rule ID */
57+
id: t.string,
58+
/** Rule type */
59+
type: t.string,
60+
/** Date when rule becomes locked */
61+
lockDate: t.string,
62+
/** Mutability constraint */
63+
mutabilityConstraint: t.string,
64+
/** Coin this rule applies to */
65+
coin: t.string,
66+
/** Rule condition */
67+
condition: t.UnknownRecord,
68+
/** Rule action */
69+
action: t.UnknownRecord,
70+
});
71+
72+
/**
73+
* Wallet policy
74+
*/
75+
export const WalletPolicy = t.partial({
76+
/** Policy ID */
77+
id: t.string,
78+
/** Policy creation date */
79+
date: t.string,
80+
/** Policy version number */
81+
version: t.number,
82+
/** Policy label */
83+
label: t.string,
84+
/** Whether this is the latest version */
85+
latest: t.boolean,
86+
/** Policy rules */
87+
rules: t.array(PolicyRule),
88+
});
89+
90+
/**
91+
* Admin settings for wallet
92+
*/
93+
export const WalletAdmin = t.partial({
94+
policy: WalletPolicy,
95+
});
96+
97+
/**
98+
* Freeze information
99+
*/
100+
export const WalletFreeze = t.partial({
101+
time: t.string,
102+
expires: t.string,
103+
});
104+
105+
/**
106+
* Build defaults for wallet transactions
107+
*/
108+
export const BuildDefaults = t.partial({
109+
minFeeRate: t.number,
110+
maxFeeRate: t.number,
111+
feeMultiplier: t.number,
112+
changeAddressType: t.string,
113+
txFormat: t.string,
114+
});
115+
116+
/**
117+
* Custom change key signatures
118+
*/
119+
export const CustomChangeKeySignatures = t.partial({
120+
user: t.string,
121+
backup: t.string,
122+
bitgo: t.string,
123+
});
124+
125+
/**
126+
* Wallet response data
127+
* Comprehensive wallet information returned from wallet operations
128+
* Based on WalletData interface from sdk-core
129+
*/
130+
export const WalletResponse = t.partial({
131+
/** Wallet ID */
132+
id: t.string,
133+
/** Wallet label/name */
134+
label: t.string,
135+
/** Coin type (e.g., btc, tlnbtc, lnbtc) */
136+
coin: t.string,
137+
/** Array of keychain IDs */
138+
keys: t.array(t.string),
139+
/** Number of signatures required (m in m-of-n) */
140+
m: t.number,
141+
/** Total number of keys (n in m-of-n) */
142+
n: t.number,
143+
/** Number of approvals required for transactions */
144+
approvalsRequired: t.number,
145+
/** Wallet balance as number */
146+
balance: t.number,
147+
/** Confirmed balance as number */
148+
confirmedBalance: t.number,
149+
/** Spendable balance as number */
150+
spendableBalance: t.number,
151+
/** Wallet balance as string */
152+
balanceString: t.string,
153+
/** Confirmed balance as string */
154+
confirmedBalanceString: t.string,
155+
/** Spendable balance as string */
156+
spendableBalanceString: t.string,
157+
/** Number of unspent outputs */
158+
unspentCount: t.number,
159+
/** Enterprise ID this wallet belongs to */
160+
enterprise: t.string,
161+
/** Wallet type (e.g., 'hot', 'cold', 'custodial') */
162+
type: t.string,
163+
/** Wallet subtype (e.g., 'lightningSelfCustody') */
164+
subType: t.string,
165+
/** Multisig type ('onchain' or 'tss') */
166+
multisigType: t.union([t.literal('onchain'), t.literal('tss')]),
167+
/** Multisig type version (e.g., 'MPCv2') */
168+
multisigTypeVersion: t.string,
169+
/** Coin-specific wallet data */
170+
coinSpecific: t.UnknownRecord,
171+
/** Admin settings including policy */
172+
admin: WalletAdmin,
173+
/** Users with access to this wallet */
174+
users: t.array(WalletUser),
175+
/** Receive address information */
176+
receiveAddress: ReceiveAddress,
177+
/** Whether the wallet can be recovered */
178+
recoverable: t.boolean,
179+
/** Tags associated with the wallet */
180+
tags: t.array(t.string),
181+
/** Whether backup key signing is allowed */
182+
allowBackupKeySigning: t.boolean,
183+
/** Build defaults for transactions */
184+
buildDefaults: BuildDefaults,
185+
/** Whether the wallet is cold storage */
186+
isCold: t.boolean,
187+
/** Custodial wallet information */
188+
custodialWallet: t.UnknownRecord,
189+
/** Custodial wallet ID */
190+
custodialWalletId: t.string,
191+
/** Whether the wallet is deleted */
192+
deleted: t.boolean,
193+
/** Whether transaction notifications are disabled */
194+
disableTransactionNotifications: t.boolean,
195+
/** Freeze status */
196+
freeze: WalletFreeze,
197+
/** Node ID for lightning wallets */
198+
nodeId: t.string,
199+
/** Pending approvals for this wallet */
200+
pendingApprovals: t.array(t.UnknownRecord),
201+
/** Start date information */
202+
startDate: t.UnknownRecord,
203+
/** Custom change key signatures */
204+
customChangeKeySignatures: CustomChangeKeySignatures,
205+
/** Wallet which this was migrated from */
206+
migratedFrom: t.string,
207+
/** EVM keyring reference wallet ID */
208+
evmKeyRingReferenceWalletId: t.string,
209+
/** Whether this is a parent wallet */
210+
isParent: t.boolean,
211+
/** Enabled child chains */
212+
enabledChildChains: t.array(t.string),
213+
/** Wallet flags */
214+
walletFlags: t.array(
215+
t.type({
216+
name: t.string,
217+
value: t.string,
218+
})
219+
),
220+
/** Token balances */
221+
tokens: t.array(t.UnknownRecord),
222+
/** NFT balances */
223+
nfts: t.record(t.string, t.UnknownRecord),
224+
/** Unsupported NFT balances */
225+
unsupportedNfts: t.record(t.string, t.UnknownRecord),
226+
});

modules/express/test/unit/clientRoutes/expressWallet.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,23 @@ describe('express.wallet.update (unit)', () => {
4141
];
4242
const wpPut = nock(bgUrl)
4343
.put(`/api/v2/${coin}/wallet/${walletId}`)
44-
.reply(200, { id: walletId, label: 'updated', coinSpecific: {} });
44+
.reply(200, {
45+
id: walletId,
46+
label: 'updated',
47+
coin: coin,
48+
keys: ['key1', 'key2', 'key3'],
49+
approvalsRequired: 1,
50+
balance: 0,
51+
confirmedBalance: 0,
52+
spendableBalance: 0,
53+
balanceString: '0',
54+
confirmedBalanceString: '0',
55+
spendableBalanceString: '0',
56+
enterprise: 'testEnterprise',
57+
multisigType: 'tss',
58+
coinSpecific: {},
59+
pendingApprovals: [],
60+
});
4561

4662
const req = {
4763
bitgo,

modules/express/test/unit/typedRoutes/expressWalletUpdate.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,25 @@ describe('Express Wallet Update Typed Routes Tests', function () {
3333

3434
const updateResponse = {
3535
id: walletId,
36+
label: 'Test Lightning Wallet',
3637
coin,
38+
keys: ['key1', 'key2', 'key3'],
39+
approvalsRequired: 1,
40+
balance: 1000000,
41+
confirmedBalance: 1000000,
42+
spendableBalance: 1000000,
43+
balanceString: '1000000',
44+
confirmedBalanceString: '1000000',
45+
spendableBalanceString: '1000000',
46+
enterprise: 'enterprise123',
47+
multisigType: 'tss' as const,
3748
coinSpecific: {
3849
[coin]: {
3950
signerHost,
4051
signerTlsCert,
4152
},
4253
},
54+
pendingApprovals: [],
4355
};
4456

4557
// Stub bitgo.put() for lightning update
@@ -141,13 +153,25 @@ describe('Express Wallet Update Typed Routes Tests', function () {
141153

142154
const updateResponse = {
143155
id: walletId,
156+
label: 'Mainnet Lightning Wallet',
144157
coin,
158+
keys: ['mainnetKey1', 'mainnetKey2', 'mainnetKey3'],
159+
approvalsRequired: 1,
160+
balance: 5000000,
161+
confirmedBalance: 5000000,
162+
spendableBalance: 5000000,
163+
balanceString: '5000000',
164+
confirmedBalanceString: '5000000',
165+
spendableBalanceString: '5000000',
166+
enterprise: 'mainnetEnterprise456',
167+
multisigType: 'tss' as const,
145168
coinSpecific: {
146169
[coin]: {
147170
signerHost,
148171
signerTlsCert,
149172
},
150173
},
174+
pendingApprovals: [],
151175
};
152176

153177
const putStub = sinon.stub().returns({

0 commit comments

Comments
 (0)