From 2b904b395c0b58a898a7ae9d1c10b35b21adaa04 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Sun, 9 Oct 2022 10:56:01 -0700 Subject: [PATCH] refactor: Use new M.splitArray and M.splitRecord --- packages/ERTP/src/typeGuards.js | 11 ++++--- .../test/unitTests/test-inputValidation.js | 19 +++++------- .../ERTP/test/unitTests/test-issuerObj.js | 6 ++-- packages/ERTP/test/unitTests/test-mintObj.js | 2 +- packages/SwingSet/src/typeGuards.js | 31 ++++++++++--------- packages/casting/src/netconfig.js | 8 ++--- .../test/unitTests/test-paramGovernance.js | 2 +- packages/inter-protocol/src/psm/psm.js | 4 +-- packages/inter-protocol/src/psm/psmCharter.js | 20 ++++++------ .../inter-protocol/src/vaultFactory/params.js | 17 +++++----- packages/inter-protocol/test/psm/test-psm.js | 6 ++-- .../test/vaultFactory/test-vaultFactory.js | 2 +- packages/internal/src/utils.js | 6 ++-- packages/smart-wallet/src/typeGuards.js | 19 ++++++------ packages/smart-wallet/src/walletFactory.js | 4 +-- .../vat-data/test/test-durable-classes.js | 10 +++--- .../vat-data/test/test-virtual-classes.js | 10 +++--- packages/vat-data/test/test-vivify.js | 15 +++++---- packages/vats/src/core/boot-psm.js | 17 +++++----- .../zoe/src/contracts/coveredCall-durable.js | 2 +- packages/zoe/src/contracts/coveredCall.js | 2 +- packages/zoe/src/typeGuards.js | 9 +++--- .../zoe/test/unitTests/test-cleanProposal.js | 16 +++++----- .../test/unitTests/zcf/test-zoeHelpersWZcf.js | 6 ++-- 24 files changed, 125 insertions(+), 119 deletions(-) diff --git a/packages/ERTP/src/typeGuards.js b/packages/ERTP/src/typeGuards.js index c503f27bb506..2d26319aed31 100644 --- a/packages/ERTP/src/typeGuards.js +++ b/packages/ERTP/src/typeGuards.js @@ -124,18 +124,19 @@ export const MAX_ABSOLUTE_DECIMAL_PLACES = 100; export const AssetKindShape = M.or('nat', 'set', 'copySet', 'copyBag'); -export const DisplayInfoShape = M.partial( - harden({ +export const DisplayInfoShape = M.splitRecord( + {}, + { decimalPlaces: M.and( M.gte(-MAX_ABSOLUTE_DECIMAL_PLACES), M.lte(MAX_ABSOLUTE_DECIMAL_PLACES), ), assetKind: AssetKindShape, - }), - harden({ + }, + { // Including this empty `rest` ensures that there are no other // properties beyond those in the `base` record. - }), + }, ); // //////////////////////// Interfaces ///////////////////////////////////////// diff --git a/packages/ERTP/test/unitTests/test-inputValidation.js b/packages/ERTP/test/unitTests/test-inputValidation.js index 791a03d47f2b..90124406701d 100644 --- a/packages/ERTP/test/unitTests/test-inputValidation.js +++ b/packages/ERTP/test/unitTests/test-inputValidation.js @@ -33,7 +33,7 @@ test('makeIssuerKit bad displayInfo.decimalPlaces', async t => { ), { message: - 'displayInfo: optional-parts: decimalPlaces: "hello" - Must be >= -100', + 'displayInfo: optional: decimalPlaces: "hello" - Must be >= -100', }, ); @@ -61,8 +61,7 @@ test('makeIssuerKit bad displayInfo.decimalPlaces', async t => { () => makeIssuerKit('myTokens', AssetKind.NAT, harden({ decimalPlaces: 101 })), { - message: - 'displayInfo: optional-parts: decimalPlaces: 101 - Must be <= 100', + message: 'displayInfo: optional: decimalPlaces: 101 - Must be <= 100', }, ); @@ -70,8 +69,7 @@ test('makeIssuerKit bad displayInfo.decimalPlaces', async t => { () => makeIssuerKit('myTokens', AssetKind.NAT, harden({ decimalPlaces: -101 })), { - message: - 'displayInfo: optional-parts: decimalPlaces: -101 - Must be >= -100', + message: 'displayInfo: optional: decimalPlaces: -101 - Must be >= -100', }, ); }); @@ -89,7 +87,7 @@ test('makeIssuerKit bad displayInfo.assetKind', async t => { ), { message: - 'displayInfo: optional-parts: assetKind: "something" - Must match one of ["nat","set","copySet","copyBag"]', + 'displayInfo: optional: assetKind: "something" - Must match one of ["nat","set","copySet","copyBag"]', }, ); }); @@ -106,8 +104,7 @@ test('makeIssuerKit bad displayInfo.whatever', async t => { }), ), { - message: - 'displayInfo: rest-parts: {"whatever":"something"} - Must be: {}', + message: 'displayInfo: rest: {"whatever":"something"} - Must be: {}', }, ); }); @@ -144,7 +141,7 @@ test('brand.isMyIssuer bad issuer', async t => { // @ts-expect-error Intentional wrong type for testing t.throwsAsync(() => brand.isMyIssuer('not an issuer'), { message: - 'In "isMyIssuer" method of (myTokens brand) arg 0: string "not an issuer" - Must be a remotable (Issuer)', + 'In "isMyIssuer" method of (myTokens brand): args: [0]: string "not an issuer" - Must be a remotable (Issuer)', }); const fakeIssuer = /** @type {Issuer} */ ( /** @type {unknown} */ (Far('myTokens issuer', {})) @@ -187,7 +184,7 @@ test('issuer.combine bad payments array', async t => { // @ts-expect-error Intentional wrong type for testing await t.throwsAsync(() => E(issuer).combine(notAnArray), { message: - 'In "combine" method of (fungible issuer) arg 0: cannot serialize Remotables with non-methods like "length" in {"length":2,"split":"[Function split]"}', + 'In "combine" method of (fungible issuer): cannot serialize Remotables with non-methods like "length" in {"length":2,"split":"[Function split]"}', }); const notAnArray2 = Far('notAnArray2', { @@ -197,7 +194,7 @@ test('issuer.combine bad payments array', async t => { // @ts-expect-error Intentional wrong type for testing await t.throwsAsync(() => E(issuer).combine(notAnArray2), { message: - 'In "combine" method of (fungible issuer) arg 0: remotable "[Alleged: notAnArray2]" - Must be a copyArray', + 'In "combine" method of (fungible issuer): args: [0]: remotable "[Alleged: notAnArray2]" - Must be a copyArray', }); }); diff --git a/packages/ERTP/test/unitTests/test-issuerObj.js b/packages/ERTP/test/unitTests/test-issuerObj.js index 532ee1114b8f..9c3ea754fa34 100644 --- a/packages/ERTP/test/unitTests/test-issuerObj.js +++ b/packages/ERTP/test/unitTests/test-issuerObj.js @@ -43,7 +43,7 @@ test('bad display info', t => { const displayInfo = harden({ somethingUnexpected: 3 }); // @ts-expect-error deliberate invalid arguments for testing t.throws(() => makeIssuerKit('fungible', AssetKind.NAT, displayInfo), { - message: 'displayInfo: rest-parts: {"somethingUnexpected":3} - Must be: {}', + message: 'displayInfo: rest: {"somethingUnexpected":3} - Must be: {}', }); }); @@ -199,7 +199,7 @@ test('purse.deposit promise', async t => { () => E(purse).deposit(exclusivePaymentP, fungible25), { message: - 'In "deposit" method of (fungible Purse purse) arg 0: promise "[Promise]" - Must be a remotable (Payment)', + 'In "deposit" method of (fungible Purse purse): args: [0]: promise "[Promise]" - Must be a remotable (Payment)', }, 'failed to reject a promise for a payment', ); @@ -334,7 +334,7 @@ test('issuer.split bad amount', async t => { _ => E(issuer).split(payment, AmountMath.make(otherBrand, 10n)), { message: - 'In "split" method of (fungible issuer) arg 1: brand: "[Alleged: other fungible brand]" - Must be: "[Alleged: fungible brand]"', + 'In "split" method of (fungible issuer): args: [1]: brand: "[Alleged: other fungible brand]" - Must be: "[Alleged: fungible brand]"', }, 'throws for bad amount', ); diff --git a/packages/ERTP/test/unitTests/test-mintObj.js b/packages/ERTP/test/unitTests/test-mintObj.js index 5945b0b964e7..f16e8d6f1a61 100644 --- a/packages/ERTP/test/unitTests/test-mintObj.js +++ b/packages/ERTP/test/unitTests/test-mintObj.js @@ -45,7 +45,7 @@ test('mint.mintPayment set w strings AssetKind', async t => { const badAmount = AmountMath.make(brand, harden([['badElement']])); t.throws(() => mint.mintPayment(badAmount), { message: - 'In "mintPayment" method of (items mint) arg 0: value: [0]: copyArray ["badElement"] - Must be a string', + 'In "mintPayment" method of (items mint): args: [0]: value: [0]: copyArray ["badElement"] - Must be a string', }); }); diff --git a/packages/SwingSet/src/typeGuards.js b/packages/SwingSet/src/typeGuards.js index 3ffe089b7d81..b3e54d7f3c57 100644 --- a/packages/SwingSet/src/typeGuards.js +++ b/packages/SwingSet/src/typeGuards.js @@ -3,16 +3,17 @@ import { M } from '@agoric/store'; export const ManagerType = M.or('xs-worker', 'local'); // TODO: others -const Bundle = M.split({ moduleType: M.string() }, M.partial({})); +const Bundle = M.splitRecord({ moduleType: M.string() }); + +const partial1 = harden({ + creationOptions: M.splitRecord({}, { critial: M.boolean() }), + parameters: M.recordOf(M.string(), M.any()), +}); -const p1 = M.and( - M.partial({ creationOptions: M.partial({ critial: M.boolean() }) }), - M.partial({ parameters: M.recordOf(M.string(), M.any()) }), -); const SwingSetConfigProperties = M.or( - M.split({ sourceSpec: M.string() }, p1), - M.split({ bundleSpec: M.string() }, p1), - M.split({ bundle: Bundle }, p1), + M.splitRecord({ sourceSpec: M.string() }, partial1), + M.splitRecord({ bundleSpec: M.string() }, partial1), + M.splitRecord({ bundle: Bundle }, partial1), ); const SwingSetConfigDescriptor = M.recordOf( M.string(), @@ -30,11 +31,11 @@ const SwingSetConfigDescriptor = M.recordOf( * in ./types-external.js */ export const SwingSetConfig = M.and( - M.partial({ defaultManagerType: ManagerType }), - M.partial({ includeDevDependencies: M.boolean() }), - M.partial({ defaultReapInterval: M.number() }), // not in type decl - M.partial({ snapshotInterval: M.number() }), - M.partial({ vats: SwingSetConfigDescriptor }), - M.partial({ bootstrap: M.string() }), - M.partial({ bundles: SwingSetConfigDescriptor }), + M.splitRecord({}, { defaultManagerType: ManagerType }), + M.splitRecord({}, { includeDevDependencies: M.boolean() }), + M.splitRecord({}, { defaultReapInterval: M.number() }), // not in type decl + M.splitRecord({}, { snapshotInterval: M.number() }), + M.splitRecord({}, { vats: SwingSetConfigDescriptor }), + M.splitRecord({}, { bootstrap: M.string() }), + M.splitRecord({}, { bundles: SwingSetConfigDescriptor }), ); diff --git a/packages/casting/src/netconfig.js b/packages/casting/src/netconfig.js index 6cb4d04cea18..dd27d92aae8a 100644 --- a/packages/casting/src/netconfig.js +++ b/packages/casting/src/netconfig.js @@ -14,12 +14,12 @@ import { fit, M } from '@agoric/store'; * @property {string[]} [peers] - a list of nodes used to start the p2p gossip (stored in a per-node “address book”, which is a file stored in that node’s data directory) * @property {string[]} [seeds] - nodes which tell you about other peers but don't gossip actual data */ -export const NetworkConfigShape = M.split( - { +export const NetworkConfigShape = M.splitRecord( + harden({ chainName: M.string(), rpcAddrs: M.arrayOf(M.string()), - }, - M.partial({ + }), + harden({ apiAddrs: M.arrayOf(M.string()), gci: M.string(), peers: M.arrayOf(M.string()), diff --git a/packages/governance/test/unitTests/test-paramGovernance.js b/packages/governance/test/unitTests/test-paramGovernance.js index 5752bf9f81ba..34033f5d748d 100644 --- a/packages/governance/test/unitTests/test-paramGovernance.js +++ b/packages/governance/test/unitTests/test-paramGovernance.js @@ -218,7 +218,7 @@ test('multiple params bad change', async t => { ), { message: - 'In "getAmountOf" method of (Zoe Invitation issuer) arg 0: bigint "[13n]" - Must be a remotable (Payment)', + 'In "getAmountOf" method of (Zoe Invitation issuer): args: [0]: bigint "[13n]" - Must be a remotable (Payment)', }, ); }); diff --git a/packages/inter-protocol/src/psm/psm.js b/packages/inter-protocol/src/psm/psm.js index 35bc3e1df452..6cb55dd967ab 100644 --- a/packages/inter-protocol/src/psm/psm.js +++ b/packages/inter-protocol/src/psm/psm.js @@ -276,7 +276,7 @@ export const start = async (zcf, privateArgs, baggage) => { wantmintedHook, 'wantMinted', undefined, - M.split({ + M.splitRecord({ give: { In: anchorAmountShape }, want: M.or({ Out: stableAmountShape }, {}), }), @@ -287,7 +287,7 @@ export const start = async (zcf, privateArgs, baggage) => { giveMintedHook, 'giveMinted', undefined, - M.split({ + M.splitRecord({ give: { In: stableAmountShape }, want: M.or({ Out: anchorAmountShape }, {}), }), diff --git a/packages/inter-protocol/src/psm/psmCharter.js b/packages/inter-protocol/src/psm/psmCharter.js index 926aaa15ea22..3f39ff192e3e 100644 --- a/packages/inter-protocol/src/psm/psmCharter.js +++ b/packages/inter-protocol/src/psm/psmCharter.js @@ -23,17 +23,15 @@ import { E } from '@endo/far'; * @property {Record} params * @property {{paramPath: { key: string }}} [path] */ -const ParamChangesOfferArgsShape = harden( - M.split( - { - deadline: TimestampShape, - instance: InstanceHandleShape, - params: M.recordOf(M.string(), M.any()), - }, - M.partial({ - path: { paramPath: { key: M.string() } }, - }), - ), +const ParamChangesOfferArgsShape = M.splitRecord( + { + deadline: TimestampShape, + instance: InstanceHandleShape, + params: M.recordOf(M.string(), M.any()), + }, + { + path: { paramPath: { key: M.string() } }, + }, ); /** diff --git a/packages/inter-protocol/src/vaultFactory/params.js b/packages/inter-protocol/src/vaultFactory/params.js index 765b94354d0d..be9d7bdfe424 100644 --- a/packages/inter-protocol/src/vaultFactory/params.js +++ b/packages/inter-protocol/src/vaultFactory/params.js @@ -78,16 +78,13 @@ const makeVaultParamManager = (publisherKit, initial) => }); /** @typedef {ReturnType} VaultParamManager */ -export const vaultParamPattern = M.split( - { - liquidationMargin: ratioPattern, - liquidationPenalty: ratioPattern, - interestRate: ratioPattern, - loanFee: ratioPattern, - debtLimit: amountPattern, - }, - M.any(), -); +export const vaultParamPattern = M.splitRecord({ + liquidationMargin: ratioPattern, + liquidationPenalty: ratioPattern, + interestRate: ratioPattern, + loanFee: ratioPattern, + debtLimit: amountPattern, +}); /** * @param {import('@agoric/notifier').StoredPublisherKit} publisherKit diff --git a/packages/inter-protocol/test/psm/test-psm.js b/packages/inter-protocol/test/psm/test-psm.js index 1917ba19a000..e129a7c4b756 100644 --- a/packages/inter-protocol/test/psm/test-psm.js +++ b/packages/inter-protocol/test/psm/test-psm.js @@ -622,7 +622,7 @@ test('wrong give giveMintedInvitation', async t => { ), { message: - '"giveMinted" proposal: required-parts: give: In: brand: "[Alleged: aUSD brand]" - Must be: "[Alleged: IST brand]"', + '"giveMinted" proposal: give: In: brand: "[Alleged: aUSD brand]" - Must be: "[Alleged: IST brand]"', }, ); }); @@ -652,7 +652,7 @@ test('wrong give wantMintedInvitation', async t => { ), { message: - '"wantMinted" proposal: required-parts: give: In: brand: "[Alleged: IST brand]" - Must be: "[Alleged: aUSD brand]"', + '"wantMinted" proposal: give: In: brand: "[Alleged: IST brand]" - Must be: "[Alleged: aUSD brand]"', }, ); }); @@ -674,7 +674,7 @@ test('extra give wantMintedInvitation', async t => { ), { message: - '"wantMinted" proposal: required-parts: give: {"Extra":{"brand":"[Alleged: aUSD brand]","value":"[200000000n]"},"In":{"brand":"[Seen]","value":"[200000000n]"}} - Must not have unexpected properties: ["Extra"]', + '"wantMinted" proposal: give: {"Extra":{"brand":"[Alleged: aUSD brand]","value":"[200000000n]"},"In":{"brand":"[Seen]","value":"[200000000n]"}} - Must not have unexpected properties: ["Extra"]', }, ); }); diff --git a/packages/inter-protocol/test/vaultFactory/test-vaultFactory.js b/packages/inter-protocol/test/vaultFactory/test-vaultFactory.js index 055a04c67c13..d4adbbfa3cbd 100644 --- a/packages/inter-protocol/test/vaultFactory/test-vaultFactory.js +++ b/packages/inter-protocol/test/vaultFactory/test-vaultFactory.js @@ -2516,7 +2516,7 @@ test('addVaultType: extra, unexpected params', async t => { E(vaultFactory).addVaultType(chit.issuer, 'Chit', missingParams), { message: - /initialParamValues: required-parts: .* - Must have missing properties \["interestRate"\]/, + /initialParamValues: .* - Must have missing properties \["interestRate"\]/, }, ); diff --git a/packages/internal/src/utils.js b/packages/internal/src/utils.js index 876f13346e87..e94cb8c2c442 100644 --- a/packages/internal/src/utils.js +++ b/packages/internal/src/utils.js @@ -11,8 +11,8 @@ const { details: X, quote: q } = assert; /** * Throws if multiple entries use the same property name. Otherwise acts - * like `Object.fromEntries`. Use it to protect from property names - * computed from user-provided data. + * like `Object.fromEntries` but hardens the result. + * Use it to protect from property names computed from user-provided data. * * @template K,V * @param {Iterable<[K,V]>} allEntries @@ -20,7 +20,7 @@ const { details: X, quote: q } = assert; */ export const fromUniqueEntries = allEntries => { const entriesArray = [...allEntries]; - const result = fromEntries(entriesArray); + const result = harden(fromEntries(entriesArray)); if (ownKeys(result).length === entriesArray.length) { return result; } diff --git a/packages/smart-wallet/src/typeGuards.js b/packages/smart-wallet/src/typeGuards.js index 774ebc1ce0af..a8b1f324f5de 100644 --- a/packages/smart-wallet/src/typeGuards.js +++ b/packages/smart-wallet/src/typeGuards.js @@ -12,25 +12,25 @@ export const shape = { }, // invitations - ContractInvitationSpec: M.split( + ContractInvitationSpec: M.splitRecord( { source: 'contract', instance: InstanceHandleShape, publicInvitationMaker: M.string(), }, - M.partial({ + { invitationArgs: M.array(), - }), + }, ), - ContinuingInvitationSpec: M.split( + ContinuingInvitationSpec: M.splitRecord( { source: 'continuing', previousOffer: M.or(M.number(), M.string()), invitationMakerName: M.string(), }, - M.partial({ + { invitationArgs: M.array(), - }), + }, ), PurseInvitationSpec: { source: 'purse', @@ -39,24 +39,25 @@ export const shape = { }, // offers - OfferSpec: M.split( + OfferSpec: M.splitRecord( { id: M.or(M.number(), M.string()), // TODO M.unknown() to defer validation invitationSpec: M.any(), proposal: ProposalShape, }, - M.partial({ offerArgs: M.any() }), + { offerArgs: M.any() }, ), // walletFactory - WalletBridgeMsg: M.split( + WalletBridgeMsg: M.splitRecord( { owner: M.string(), type: M.string(), blockHeight: M.number(), blockTime: M.number(), }, + {}, M.or({ action: M.string() }, { spendAction: M.string() }), ), }; diff --git a/packages/smart-wallet/src/walletFactory.js b/packages/smart-wallet/src/walletFactory.js index 33866e85cf58..0208443cf928 100644 --- a/packages/smart-wallet/src/walletFactory.js +++ b/packages/smart-wallet/src/walletFactory.js @@ -17,9 +17,9 @@ import { shape } from './typeGuards.js'; import '@agoric/vats/exported.js'; const PrivateArgsShape = harden( - M.split( + M.splitRecord( { storageNode: M.eref(M.any()) }, - M.partial({ bridgeManager: M.eref(M.any()) }), + { bridgeManager: M.eref(M.any()) }, ), ); diff --git a/packages/vat-data/test/test-durable-classes.js b/packages/vat-data/test/test-durable-classes.js index 0509ade09da9..f54b68028106 100644 --- a/packages/vat-data/test/test-durable-classes.js +++ b/packages/vat-data/test/test-durable-classes.js @@ -42,12 +42,13 @@ test('test defineDurableFarClass', t => { t.is(upCounter.incr(5), 8); t.is(upCounter.incr(1), 9); t.throws(() => upCounter.incr(-3), { - message: 'In "incr" method of (UpCounter) arg 0: -3 - Must be >= 0', + message: + 'In "incr" method of (UpCounter): optional args: [0]: -3 - Must be >= 0', }); // @ts-expect-error the type violation is what we're testing t.throws(() => upCounter.incr('foo'), { message: - 'In "incr" method of (UpCounter) arg 0: string "foo" - Must be a number', + 'In "incr" method of (UpCounter): optional args: [0]: string "foo" - Must be a number', }); }); @@ -80,12 +81,13 @@ test('test defineDurableFarClassKit', t => { t.is(downCounter.decr(), 7); t.is(upCounter.incr(3), 10); t.throws(() => upCounter.incr(-3), { - message: 'In "incr" method of (Counter up) arg 0: -3 - Must be >= 0', + message: + 'In "incr" method of (Counter up): optional args: [0]: -3 - Must be >= 0', }); // @ts-expect-error the type violation is what we're testing t.throws(() => downCounter.decr('foo'), { message: - 'In "decr" method of (Counter down) arg 0: string "foo" - Must be a number', + 'In "decr" method of (Counter down): optional args: [0]: string "foo" - Must be a number', }); // @ts-expect-error the type violation is what we're testing t.throws(() => upCounter.decr(3), { diff --git a/packages/vat-data/test/test-virtual-classes.js b/packages/vat-data/test/test-virtual-classes.js index d4015cc126f1..62fa770a6b41 100644 --- a/packages/vat-data/test/test-virtual-classes.js +++ b/packages/vat-data/test/test-virtual-classes.js @@ -39,12 +39,13 @@ test('test defineVirtualFarClass', t => { t.is(upCounter.incr(5), 8); t.is(upCounter.incr(1), 9); t.throws(() => upCounter.incr(-3), { - message: 'In "incr" method of (UpCounter) arg 0: -3 - Must be >= 0', + message: + 'In "incr" method of (UpCounter): optional args: [0]: -3 - Must be >= 0', }); // @ts-expect-error the type violation is what we're testing t.throws(() => upCounter.incr('foo'), { message: - 'In "incr" method of (UpCounter) arg 0: string "foo" - Must be a number', + 'In "incr" method of (UpCounter): optional args: [0]: string "foo" - Must be a number', }); }); @@ -75,12 +76,13 @@ test('test defineVirtualFarClassKit', t => { t.is(downCounter.decr(), 7); t.is(upCounter.incr(3), 10); t.throws(() => upCounter.incr(-3), { - message: 'In "incr" method of (Counter up) arg 0: -3 - Must be >= 0', + message: + 'In "incr" method of (Counter up): optional args: [0]: -3 - Must be >= 0', }); // @ts-expect-error the type violation is what we're testing t.throws(() => downCounter.decr('foo'), { message: - 'In "decr" method of (Counter down) arg 0: string "foo" - Must be a number', + 'In "decr" method of (Counter down): optional args: [0]: string "foo" - Must be a number', }); // @ts-expect-error the type violation is what we're testing t.throws(() => upCounter.decr(3), { diff --git a/packages/vat-data/test/test-vivify.js b/packages/vat-data/test/test-vivify.js index 8b8090eb860f..2e1fd41591b4 100644 --- a/packages/vat-data/test/test-vivify.js +++ b/packages/vat-data/test/test-vivify.js @@ -43,12 +43,13 @@ test('test vivifyFarClass', t => { t.is(upCounter.incr(5), 8); t.is(upCounter.incr(1), 9); t.throws(() => upCounter.incr(-3), { - message: 'In "incr" method of (UpCounter) arg 0: -3 - Must be >= 0', + message: + 'In "incr" method of (UpCounter): optional args: [0]: -3 - Must be >= 0', }); // @ts-expect-error the type violation is what we're testing t.throws(() => upCounter.incr('foo'), { message: - 'In "incr" method of (UpCounter) arg 0: string "foo" - Must be a number', + 'In "incr" method of (UpCounter): optional args: [0]: string "foo" - Must be a number', }); }); @@ -82,12 +83,13 @@ test('test vivifyFarClassKit', t => { t.is(downCounter.decr(), 7); t.is(upCounter.incr(3), 10); t.throws(() => upCounter.incr(-3), { - message: 'In "incr" method of (Counter up) arg 0: -3 - Must be >= 0', + message: + 'In "incr" method of (Counter up): optional args: [0]: -3 - Must be >= 0', }); // @ts-expect-error the type violation is what we're testing t.throws(() => downCounter.decr('foo'), { message: - 'In "decr" method of (Counter down) arg 0: string "foo" - Must be a number', + 'In "decr" method of (Counter down): optional args: [0]: string "foo" - Must be a number', }); // @ts-expect-error the type violation is what we're testing t.throws(() => upCounter.decr(3), { @@ -108,11 +110,12 @@ test('test vivifyFarInstance', t => { t.is(upCounter.incr(5), 8); t.is(upCounter.incr(1), 9); t.throws(() => upCounter.incr(-3), { - message: 'In "incr" method of (upCounter) arg 0: -3 - Must be >= 0', + message: + 'In "incr" method of (upCounter): optional args: [0]: -3 - Must be >= 0', }); t.throws(() => upCounter.incr('foo'), { message: - 'In "incr" method of (upCounter) arg 0: string "foo" - Must be a number', + 'In "incr" method of (upCounter): optional args: [0]: string "foo" - Must be a number', }); }); diff --git a/packages/vats/src/core/boot-psm.js b/packages/vats/src/core/boot-psm.js index 7449c64c1fa1..5bcc06c50e35 100644 --- a/packages/vats/src/core/boot-psm.js +++ b/packages/vats/src/core/boot-psm.js @@ -92,19 +92,22 @@ export const agoricNamesReserved = harden( * decimalPlaces?: number * }} AnchorOptions */ -const AnchorOptionsShape = M.split( +const AnchorOptionsShape = M.splitRecord( { denom: M.string() }, - M.partial({ + { keyword: M.string(), proposedName: M.string(), decimalPlaces: M.number(), - }), + }, ); -export const ParametersShape = M.partial({ - anchorAssets: M.arrayOf(AnchorOptionsShape), - economicCommitteeAddresses: M.recordOf(M.string(), M.string()), -}); +export const ParametersShape = M.splitRecord( + {}, + { + anchorAssets: M.arrayOf(AnchorOptionsShape), + economicCommitteeAddresses: M.recordOf(M.string(), M.string()), + }, +); /** * Build root object of the PSM-only bootstrap vat. diff --git a/packages/zoe/src/contracts/coveredCall-durable.js b/packages/zoe/src/contracts/coveredCall-durable.js index a2bb891aae03..e1ee291afb83 100644 --- a/packages/zoe/src/contracts/coveredCall-durable.js +++ b/packages/zoe/src/contracts/coveredCall-durable.js @@ -58,7 +58,7 @@ const start = async (zcf, _privateArgs, instanceBaggage) => { const makeOption = sellSeat => { fit( sellSeat.getProposal(), - M.split({ exit: { afterDeadline: M.any() } }), + M.splitRecord({ exit: { afterDeadline: M.any() } }), 'exit afterDeadline', ); const sellSeatExitRule = sellSeat.getProposal().exit; diff --git a/packages/zoe/src/contracts/coveredCall.js b/packages/zoe/src/contracts/coveredCall.js index d3dacce540d6..274af6b14197 100644 --- a/packages/zoe/src/contracts/coveredCall.js +++ b/packages/zoe/src/contracts/coveredCall.js @@ -72,7 +72,7 @@ const start = zcf => { const makeOption = sellSeat => { fit( sellSeat.getProposal(), - M.split({ exit: { afterDeadline: M.any() } }), + M.splitRecord({ exit: { afterDeadline: M.any() } }), 'exit afterDeadline', ); const sellSeatExitRule = sellSeat.getProposal().exit; diff --git a/packages/zoe/src/typeGuards.js b/packages/zoe/src/typeGuards.js index 8fa14f7a9d4a..1129190897ee 100644 --- a/packages/zoe/src/typeGuards.js +++ b/packages/zoe/src/typeGuards.js @@ -27,10 +27,11 @@ export const TimerShape = makeHandleShape('timer'); export const FullProposalShape = harden({ want: AmountPatternKeywordRecordShape, give: AmountKeywordRecordShape, - // To accept only one, we could use M.or rather than M.partial, + // To accept only one, we could use M.or rather than M.splitRecord, // but the error messages would have been worse. Rather, // cleanProposal's assertExit checks that there's exactly one. - exit: M.partial( + exit: M.splitRecord( + {}, { onDemand: null, waived: null, @@ -43,7 +44,7 @@ export const FullProposalShape = harden({ ), }); /** @see {Proposal} type */ -export const ProposalShape = M.partial(FullProposalShape); +export const ProposalShape = M.splitRecord({}, FullProposalShape, {}); export const isOnDemandExitRule = exit => { const [exitKey] = Object.getOwnPropertyNames(exit); @@ -68,7 +69,7 @@ export const isAfterDeadlineExitRule = exit => { return exitKey === 'afterDeadline'; }; -export const InvitationElementShape = M.split({ +export const InvitationElementShape = M.splitRecord({ description: M.string(), handle: InvitationHandleShape, instance: InstanceHandleShape, diff --git a/packages/zoe/test/unitTests/test-cleanProposal.js b/packages/zoe/test/unitTests/test-cleanProposal.js index 9579034c7710..fd55473b2b48 100644 --- a/packages/zoe/test/unitTests/test-cleanProposal.js +++ b/packages/zoe/test/unitTests/test-cleanProposal.js @@ -226,7 +226,7 @@ test('cleanProposal - other wrong stuff', t => { t, { exit: { onDemand: 'foo' } }, 'nat', - 'proposal: exit: optional-parts: {"onDemand":"foo"} - Must be: {"onDemand":null}', + 'proposal: exit: optional: onDemand: "foo" - Must be: null', ); proposeBad( t, @@ -238,43 +238,43 @@ test('cleanProposal - other wrong stuff', t => { t, { exit: { afterDeadline: { timer: 'foo', deadline: 3n } } }, 'nat', - 'proposal: exit: optional-parts: afterDeadline: timer: "foo" - Must match one of ["[match:remotable]","[match:kind]"]', + 'proposal: exit: optional: afterDeadline: timer: "foo" - Must match one of ["[match:remotable]","[match:kind]"]', ); proposeBad( t, { exit: { afterDeadline: { timer, deadline: 'foo' } } }, 'nat', - 'proposal: exit: optional-parts: afterDeadline: deadline: string "foo" - Must be a bigint', + 'proposal: exit: optional: afterDeadline: deadline: string "foo" - Must be a bigint', ); proposeBad( t, { exit: { afterDeadline: { timer, deadline: 3n, extra: 'foo' } } }, 'nat', - 'proposal: exit: optional-parts: afterDeadline: {"timer":"[Alleged: ManualTimer]","deadline":"[3n]","extra":"foo"} - Must not have unexpected properties: ["extra"]', + 'proposal: exit: optional: afterDeadline: {"timer":"[Alleged: ManualTimer]","deadline":"[3n]","extra":"foo"} - Must not have unexpected properties: ["extra"]', ); proposeBad( t, { exit: { afterDeadline: { timer } } }, 'nat', - 'proposal: exit: optional-parts: afterDeadline: {"timer":"[Alleged: ManualTimer]"} - Must have missing properties ["deadline"]', + 'proposal: exit: optional: afterDeadline: {"timer":"[Alleged: ManualTimer]"} - Must have missing properties ["deadline"]', ); proposeBad( t, { exit: { afterDeadline: { deadline: 3n } } }, 'nat', - 'proposal: exit: optional-parts: afterDeadline: {"deadline":"[3n]"} - Must have missing properties ["timer"]', + 'proposal: exit: optional: afterDeadline: {"deadline":"[3n]"} - Must have missing properties ["timer"]', ); proposeBad( t, { exit: { afterDeadline: { timer, deadline: 3 } } }, 'nat', - 'proposal: exit: optional-parts: afterDeadline: deadline: number 3 - Must be a bigint', + 'proposal: exit: optional: afterDeadline: deadline: number 3 - Must be a bigint', ); proposeBad( t, { exit: { afterDeadline: { timer, deadline: -3n } } }, 'nat', - 'proposal: exit: optional-parts: afterDeadline: deadline: "[-3n]" - Must be non-negative', + 'proposal: exit: optional: afterDeadline: deadline: "[-3n]" - Must be non-negative', ); proposeBad(t, { exit: {} }, 'nat', /exit {} should only have one key/); proposeBad( diff --git a/packages/zoe/test/unitTests/zcf/test-zoeHelpersWZcf.js b/packages/zoe/test/unitTests/zcf/test-zoeHelpersWZcf.js index 2b19381fd5ef..b5321ea63721 100644 --- a/packages/zoe/test/unitTests/zcf/test-zoeHelpersWZcf.js +++ b/packages/zoe/test/unitTests/zcf/test-zoeHelpersWZcf.js @@ -304,7 +304,7 @@ test(`zoeHelper with zcf - fit proposal patterns`, async t => { () => fit(proposal, M.split({ want: { C: M.any() } })), { message: - 'required-parts: want: {"A":{"brand":"[Alleged: moola brand]","value":"[20n]"}} - Must have missing properties ["C"]', + 'want: {"A":{"brand":"[Alleged: moola brand]","value":"[20n]"}} - Must have missing properties ["C"]', }, 'empty keywordRecord does not match', ); @@ -314,14 +314,14 @@ test(`zoeHelper with zcf - fit proposal patterns`, async t => { () => fit(proposal, M.split({ give: { c: M.any() } })), { message: - 'required-parts: give: {"B":{"brand":"[Alleged: simoleans brand]","value":"[3n]"}} - Must have missing properties ["c"]', + 'give: {"B":{"brand":"[Alleged: simoleans brand]","value":"[3n]"}} - Must have missing properties ["c"]', }, 'wrong key in keywordRecord does not match', ); t.throws( () => fit(proposal, M.split({ exit: { onDemaind: M.any() } })), { - message: 'required-parts: {} - Must have missing properties ["exit"]', + message: '{} - Must have missing properties ["exit"]', }, 'missing exit rule', );