Skip to content

Commit c9b247d

Browse files
feat(express): type coinSpecific.ofc on WalletResponse codec
Narrow the WalletResponse `coinSpecific` codec from a bare `t.UnknownRecord` to an intersection that adds typed visibility for `coinSpecific.ofc.userKeySigningRequired` while keeping the unknown-record permissiveness intact for unrelated subdocuments (eth, terc20, etc.). Add codec-decode tests covering wallets with non-ofc subdocuments, the typed ofc shape, an empty coinSpecific, and rejection of wrong-typed values. Ticket: WCN-471 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e75172b commit c9b247d

2 files changed

Lines changed: 30 additions & 1 deletion

File tree

modules/express/src/typedRoutes/schemas/wallet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export const WalletResponse = t.partial({
189189
/** Multisig type version (e.g., 'MPCv2') */
190190
multisigTypeVersion: t.string,
191191
/** Coin-specific wallet data */
192-
coinSpecific: t.UnknownRecord,
192+
coinSpecific: t.intersection([t.partial({ ofc: t.partial({ userKeySigningRequired: t.boolean }) }), t.UnknownRecord]),
193193
/** Admin settings including policy */
194194
admin: WalletAdmin,
195195
/** Users with access to this wallet */

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
ExpressWalletUpdateParams,
2121
} from '../../../src/typedRoutes/api/v2/expressWalletUpdate';
2222
import { SignerMacaroonBody, SignerMacaroonParams } from '../../../src/typedRoutes/api/v2/signerMacaroon';
23+
import { WalletResponse } from '../../../src/typedRoutes/schemas/wallet';
2324

2425
export function assertDecode<T>(codec: t.Type<T, unknown>, input: unknown): T {
2526
const result = codec.decode(input);
@@ -306,4 +307,32 @@ describe('io-ts decode tests', function () {
306307
it('express.lightning.signerMacaroon params invalid', function () {
307308
assert.throws(() => assertDecode(t.type(SignerMacaroonParams), { coin: 'lnbtc' }));
308309
});
310+
describe('WalletResponse coinSpecific', function () {
311+
it('decodes wallets with only non-ofc coinSpecific subdocuments', function () {
312+
// coinSpecific carrying an unrelated subdocument (e.g. eth) and no ofc must still decode
313+
const decoded = assertDecode(WalletResponse, {
314+
id: 'wallet123',
315+
coinSpecific: { eth: { baseAddress: '0xabc' } },
316+
});
317+
assert.deepStrictEqual(decoded.coinSpecific, { eth: { baseAddress: '0xabc' } });
318+
});
319+
it('decodes wallets with coinSpecific.ofc.userKeySigningRequired', function () {
320+
const decoded = assertDecode(WalletResponse, {
321+
id: 'wallet123',
322+
coinSpecific: { ofc: { userKeySigningRequired: true } },
323+
});
324+
assert.strictEqual(decoded.coinSpecific?.ofc?.userKeySigningRequired, true);
325+
});
326+
it('decodes wallets with empty coinSpecific', function () {
327+
assertDecode(WalletResponse, { id: 'wallet123', coinSpecific: {} });
328+
});
329+
it('rejects coinSpecific.ofc.userKeySigningRequired of wrong type', function () {
330+
assert.throws(() =>
331+
assertDecode(WalletResponse, {
332+
id: 'wallet123',
333+
coinSpecific: { ofc: { userKeySigningRequired: 'yes' } },
334+
})
335+
);
336+
});
337+
});
309338
});

0 commit comments

Comments
 (0)