Skip to content

Commit 9fb3e2a

Browse files
committed
fixup! mustBeKey
1 parent 6d08dcc commit 9fb3e2a

File tree

7 files changed

+84
-24
lines changed

7 files changed

+84
-24
lines changed

packages/ERTP/src/purse.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export const preparePurseKind = (
116116
const balanceStore = makeAmountStore(state, 'currentBalance');
117117
let amount;
118118
if (kindOf(amountBound) === 'match:containerHas') {
119-
throw Fail`TODO handle match:containerHas`;
119+
throw Fail`TODO: purse withdraw does not yet support amount patterns`;
120120
} else {
121121
amount = amountBound;
122122
}

packages/store/test/borrow-guards.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,27 @@ export const AmountShape = harden({
1919
value: AmountValueShape,
2020
});
2121

22+
/**
23+
* TODO: need a pattern to test `kindOf(specimen) === 'match:containerHas'` to
24+
* to ensure that the pattern-level invariants are met.
25+
*
26+
* TODO: check all uses of M.tagged to see if they have the same weakness.
27+
*
28+
* @see {HasBound}
29+
*/
30+
export const HasBoundShape = M.tagged('match:containerHas');
31+
32+
/** @see {AmountValueBound} */
33+
const AmountValueBoundShape = M.or(AmountValueShape, HasBoundShape);
34+
35+
/** @see {AmountBound} */
36+
export const AmountBoundShape = harden({
37+
brand: BrandShape,
38+
value: AmountValueBoundShape,
39+
});
40+
2241
const AmountKeywordRecordShape = M.recordOf(M.string(), AmountShape);
23-
const AmountPatternKeywordRecordShape = M.recordOf(M.string(), M.pattern());
42+
const AmountBoundKeywordRecordShape = M.recordOf(M.string(), AmountBoundShape);
2443

2544
const TimerBrandShape = M.remotable('TimerBrand');
2645
const TimestampValueShape = M.nat();
@@ -31,7 +50,7 @@ const TimestampRecordShape = harden({
3150
const TimestampShape = M.or(TimestampRecordShape, TimestampValueShape);
3251

3352
export const FullProposalShape = harden({
34-
want: AmountPatternKeywordRecordShape,
53+
want: AmountBoundKeywordRecordShape,
3554
give: AmountKeywordRecordShape,
3655
// To accept only one, we could use M.or rather than M.partial,
3756
// but the error messages would have been worse. Rather,

packages/zoe/src/contractFacet/reallocate.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { makeScalarMapStore } from '@agoric/vat-data';
33

44
import { assertRightsConserved } from './rightsConservation.js';
55
import { addToAllocation, subtractFromAllocation } from './allocationMath.js';
6+
import { mustBeKey } from '../contractSupport/zoeHelpers.js';
67

78
/**
89
* @import {MapStore} from '@agoric/swingset-liveslots';
@@ -47,11 +48,15 @@ export const makeAllocationMap = transfers => {
4748
allocations.set(seat, [newIncr, decr]);
4849
};
4950

50-
for (const [fromSeat, toSeat, fromAmounts, toAmounts] of transfers) {
51+
for (const [fromSeat, toSeat, fromAmountBounds, toAmounts] of transfers) {
5152
if (fromSeat) {
52-
if (!fromAmounts) {
53+
if (!fromAmountBounds) {
5354
throw Fail`Transfer from ${fromSeat} must say how much`;
5455
}
56+
const fromAmounts = mustBeKey(
57+
fromAmountBounds,
58+
'TODO: atomicRearange does not yet support AmountBounds',
59+
);
5560
decrementAllocation(fromSeat, fromAmounts);
5661
if (toSeat) {
5762
// Conserved transfer between seats
@@ -74,8 +79,8 @@ export const makeAllocationMap = transfers => {
7479
} else {
7580
toSeat || Fail`Transfer must have at least one of fromSeat or toSeat`;
7681
// Transfer only to toSeat
77-
!fromAmounts ||
78-
Fail`Transfer without fromSeat cannot have fromAmounts ${fromAmounts}`;
82+
!fromAmountBounds ||
83+
Fail`Transfer without fromSeat cannot have fromAmountBounds ${fromAmountBounds}`;
7984
toAmounts || Fail`Transfer to ${toSeat} must say how much`;
8085
incrementAllocation(toSeat, toAmounts);
8186
}

packages/zoe/src/contractFacet/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type { Passable } from '@endo/pass-style';
1414
import type { Key, Pattern } from '@endo/patterns';
1515
import type {
1616
AmountKeywordRecord,
17+
AmountBoundKeywordRecord,
1718
ExitRule,
1819
FeeMintAccess,
1920
InvitationDetails,
@@ -120,7 +121,7 @@ export type ZCF<CT = Record<string, unknown>> = {
120121
export type TransferPart = [
121122
fromSeat?: ZCFSeat,
122123
toSeat?: ZCFSeat,
123-
fromAmounts?: AmountKeywordRecord,
124+
fromAmounts?: AmountBoundKeywordRecord,
124125
toAmounts?: AmountKeywordRecord,
125126
];
126127

packages/zoe/src/contractSupport/atomicTransfer.js

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
import { M } from '@agoric/store';
2-
import { AmountKeywordRecordShape, SeatShape } from '../typeGuards.js';
2+
import {
3+
AmountKeywordRecordShape,
4+
AmountBoundKeywordRecordShape,
5+
SeatShape,
6+
} from '../typeGuards.js';
37

48
/**
5-
* @import {TransferPart, ZCF, ZCFSeat} from '@agoric/zoe';
9+
* @import {TransferPart, ZCF, ZCFSeat, AmountBoundKeywordRecord} from '@agoric/zoe';
610
*/
711

812
export const TransferPartShape = M.splitArray(
9-
harden([M.opt(SeatShape), M.opt(SeatShape), M.opt(AmountKeywordRecordShape)]),
13+
harden([
14+
M.opt(SeatShape),
15+
M.opt(SeatShape),
16+
M.opt(AmountBoundKeywordRecordShape),
17+
]),
1018
harden([M.opt(AmountKeywordRecordShape)]),
1119
);
1220

@@ -62,11 +70,11 @@ export const atomicRearrange = (zcf, transfers) => {
6270
* `fromOnly` are non-optional, as otherwise it doesn't make much sense.
6371
*
6472
* @param {ZCFSeat} fromSeat
65-
* @param {AmountKeywordRecord} fromAmounts
73+
* @param {AmountBoundKeywordRecord} fromAmountBounds
6674
* @returns {TransferPart}
6775
*/
68-
export const fromOnly = (fromSeat, fromAmounts) =>
69-
harden([fromSeat, undefined, fromAmounts]);
76+
export const fromOnly = (fromSeat, fromAmountBounds) =>
77+
harden([fromSeat, undefined, fromAmountBounds]);
7078

7179
/**
7280
* Sometimes a TransferPart in an atomicRearrange only expresses what amounts

packages/zoe/src/contractSupport/zoeHelpers.js

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
1-
import { Fail } from '@endo/errors';
1+
import { Fail, b } from '@endo/errors';
22
import { E } from '@endo/eventual-send';
33
import { makePromiseKit } from '@endo/promise-kit';
4-
import { mustMatch, keyEQ } from '@agoric/store';
4+
import { mustMatch, keyEQ, isKey, isPattern } from '@endo/patterns';
55
import { AssetKind } from '@agoric/ertp';
66
import { fromUniqueEntries } from '@agoric/internal';
77
import { satisfiesWant } from '../contractFacet/offerSafety.js';
88
import { atomicTransfer, fromOnly, toOnly } from './atomicTransfer.js';
99

1010
/**
1111
* @import {Pattern} from '@endo/patterns';
12-
* @import {ContractMeta, Invitation, Proposal, ZCF, ZCFSeat} from '@agoric/zoe';
12+
* @import {Invitation, Proposal, ZCF, ZCFSeat, AmountBoundKeywordRecord} from '@agoric/zoe';
1313
*/
1414

1515
export const defaultAcceptanceMsg = `The offer has been accepted. Once the contract has been completed, please check your payout`;
@@ -73,16 +73,42 @@ export const swap = (zcf, leftSeat, rightSeat) => {
7373
return defaultAcceptanceMsg;
7474
};
7575

76+
/**
77+
* @param {AmountBoundKeywordRecord} want
78+
* @param {string} complaint
79+
* @returns {AmountKeywordRecord}
80+
*/
81+
export const mustBeKey = (want, complaint) => {
82+
if (isKey(want)) {
83+
return want;
84+
}
85+
if (isPattern(want)) {
86+
throw Fail`${b(complaint)}: ${want}`;
87+
}
88+
throw Fail`Must be key: ${want}`;
89+
};
90+
harden(mustBeKey);
91+
7692
/** @type {SwapExact} */
7793
export const swapExact = (zcf, leftSeat, rightSeat) => {
7894
try {
95+
const { give: rightGive, want: rightWantBound } = rightSeat.getProposal();
96+
const { give: leftGive, want: leftWantBound } = leftSeat.getProposal();
97+
const rightWant = mustBeKey(
98+
rightWantBound,
99+
'TODO: swapExact does not yet support want patterns',
100+
);
101+
const leftWant = mustBeKey(
102+
leftWantBound,
103+
'TODO: swapExact does not yet support want patterns',
104+
);
79105
zcf.atomicRearrange(
80106
harden([
81-
fromOnly(rightSeat, rightSeat.getProposal().give),
82-
fromOnly(leftSeat, leftSeat.getProposal().give),
107+
fromOnly(rightSeat, rightGive),
108+
fromOnly(leftSeat, leftGive),
83109

84-
toOnly(leftSeat, leftSeat.getProposal().want),
85-
toOnly(rightSeat, rightSeat.getProposal().want),
110+
toOnly(leftSeat, leftWant),
111+
toOnly(rightSeat, rightWant),
86112
]),
87113
);
88114
} catch (err) {

packages/zoe/src/typeGuards.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @jessie-check
22

33
import {
4+
AmountBoundShape,
45
AmountShape,
56
AssetKindShape,
67
BrandShape,
@@ -31,9 +32,9 @@ export const InstallationShape = M.remotable('Installation');
3132
export const SeatShape = M.remotable('Seat');
3233

3334
export const AmountKeywordRecordShape = M.recordOf(KeywordShape, AmountShape);
34-
export const AmountPatternKeywordRecordShape = M.recordOf(
35+
export const AmountBoundKeywordRecordShape = M.recordOf(
3536
KeywordShape,
36-
M.pattern(),
37+
AmountBoundShape,
3738
);
3839
export const PaymentPKeywordRecordShape = M.recordOf(
3940
KeywordShape,
@@ -80,7 +81,7 @@ export const TimerShape = makeHandleShape('timer');
8081
* @see {ProposalRecord} type
8182
*/
8283
export const FullProposalShape = {
83-
want: AmountPatternKeywordRecordShape,
84+
want: AmountBoundKeywordRecordShape,
8485
give: AmountKeywordRecordShape,
8586
// To accept only one, we could use M.or rather than M.splitRecord,
8687
// but the error messages would have been worse. Rather,

0 commit comments

Comments
 (0)