@agoric/ertp 0.16.3-mainnet1B-dev-b0c1f78.0 → 0.16.3-orchestration-dev-096c4e8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +26 -18
- package/src/amountMath.js +57 -63
- package/src/amountStore.js +32 -0
- package/src/index.js +7 -0
- package/src/issuerKit.js +218 -74
- package/src/legacy-payment-helpers.js +23 -28
- package/src/mathHelpers/copyBagMathHelpers.js +2 -4
- package/src/mathHelpers/copySetMathHelpers.js +1 -3
- package/src/mathHelpers/natMathHelpers.js +6 -7
- package/src/payment.js +5 -8
- package/src/paymentLedger.js +84 -91
- package/src/purse.js +83 -24
- package/src/typeGuards.js +37 -43
- package/src/types-ambient.js +237 -314
- package/src/types.js +237 -314
- package/CHANGELOG.md +0 -767
- package/src/amountMath.d.ts +0 -53
- package/src/amountMath.d.ts.map +0 -1
- package/src/displayInfo.d.ts +0 -2
- package/src/displayInfo.d.ts.map +0 -1
- package/src/index.d.ts +0 -4
- package/src/index.d.ts.map +0 -1
- package/src/issuerKit.d.ts +0 -19
- package/src/issuerKit.d.ts.map +0 -1
- package/src/legacy-payment-helpers.d.ts +0 -5
- package/src/legacy-payment-helpers.d.ts.map +0 -1
- package/src/mathHelpers/copyBagMathHelpers.d.ts +0 -5
- package/src/mathHelpers/copyBagMathHelpers.d.ts.map +0 -1
- package/src/mathHelpers/copySetMathHelpers.d.ts +0 -5
- package/src/mathHelpers/copySetMathHelpers.d.ts.map +0 -1
- package/src/mathHelpers/natMathHelpers.d.ts +0 -14
- package/src/mathHelpers/natMathHelpers.d.ts.map +0 -1
- package/src/mathHelpers/setMathHelpers.d.ts +0 -6
- package/src/mathHelpers/setMathHelpers.d.ts.map +0 -1
- package/src/payment.d.ts +0 -3
- package/src/payment.d.ts.map +0 -1
- package/src/paymentLedger.d.ts +0 -3
- package/src/paymentLedger.d.ts.map +0 -1
- package/src/purse.d.ts +0 -13
- package/src/purse.d.ts.map +0 -1
- package/src/transientNotifier.d.ts +0 -5
- package/src/transientNotifier.d.ts.map +0 -1
- package/src/typeGuards.d.ts +0 -42
- package/src/typeGuards.d.ts.map +0 -1
- package/src/types-ambient.d.ts +0 -376
- package/src/types-ambient.d.ts.map +0 -1
- package/src/types.d.ts +0 -376
- package/src/types.d.ts.map +0 -1
package/src/paymentLedger.js
CHANGED
|
@@ -3,11 +3,6 @@
|
|
|
3
3
|
/* eslint-disable no-use-before-define */
|
|
4
4
|
import { isPromise } from '@endo/promise-kit';
|
|
5
5
|
import { mustMatch, M, keyEQ } from '@agoric/store';
|
|
6
|
-
import {
|
|
7
|
-
provideDurableWeakMapStore,
|
|
8
|
-
prepareExo,
|
|
9
|
-
provide,
|
|
10
|
-
} from '@agoric/vat-data';
|
|
11
6
|
import { AmountMath } from './amountMath.js';
|
|
12
7
|
import { preparePaymentKind } from './payment.js';
|
|
13
8
|
import { preparePurseKind } from './purse.js';
|
|
@@ -15,8 +10,6 @@ import { preparePurseKind } from './purse.js';
|
|
|
15
10
|
import '@agoric/store/exported.js';
|
|
16
11
|
import { BrandI, makeIssuerInterfaces } from './typeGuards.js';
|
|
17
12
|
|
|
18
|
-
/** @typedef {import('@agoric/vat-data').Baggage} Baggage */
|
|
19
|
-
|
|
20
13
|
const { details: X, quote: q, Fail } = assert;
|
|
21
14
|
|
|
22
15
|
/**
|
|
@@ -70,29 +63,35 @@ const amountShapeFromElementShape = (brand, assetKind, elementShape) => {
|
|
|
70
63
|
};
|
|
71
64
|
|
|
72
65
|
/**
|
|
73
|
-
* Make the paymentLedger, the source of truth for the balances of
|
|
74
|
-
*
|
|
66
|
+
* Make the paymentLedger, the source of truth for the balances of payments. All
|
|
67
|
+
* minting and transfer authority originates here.
|
|
75
68
|
*
|
|
76
69
|
* @template {AssetKind} K
|
|
77
|
-
* @param {
|
|
70
|
+
* @param {import('@agoric/zone').Zone} issuerZone
|
|
78
71
|
* @param {string} name
|
|
79
72
|
* @param {K} assetKind
|
|
80
73
|
* @param {DisplayInfo<K>} displayInfo
|
|
81
74
|
* @param {Pattern} elementShape
|
|
75
|
+
* @param {RecoverySetsOption} recoverySetsState
|
|
82
76
|
* @param {ShutdownWithFailure} [optShutdownWithFailure]
|
|
83
77
|
* @returns {PaymentLedger<K>}
|
|
84
78
|
*/
|
|
85
79
|
export const preparePaymentLedger = (
|
|
86
|
-
|
|
80
|
+
issuerZone,
|
|
87
81
|
name,
|
|
88
82
|
assetKind,
|
|
89
83
|
displayInfo,
|
|
90
84
|
elementShape,
|
|
85
|
+
recoverySetsState,
|
|
91
86
|
optShutdownWithFailure = undefined,
|
|
92
87
|
) => {
|
|
93
88
|
/** @type {Brand<K>} */
|
|
94
|
-
//
|
|
95
|
-
|
|
89
|
+
// Should be
|
|
90
|
+
// at-ts-expect-error XXX callWhen
|
|
91
|
+
// but ran into the usual disagreement between local lint and CI
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
93
|
+
// @ts-ignore
|
|
94
|
+
const brand = issuerZone.exo(`${name} brand`, BrandI, {
|
|
96
95
|
isMyIssuer(allegedIssuer) {
|
|
97
96
|
// BrandI delays calling this method until `allegedIssuer` is a Remotable
|
|
98
97
|
return allegedIssuer === issuer;
|
|
@@ -121,7 +120,7 @@ export const preparePaymentLedger = (
|
|
|
121
120
|
amountShape,
|
|
122
121
|
);
|
|
123
122
|
|
|
124
|
-
const makePayment = preparePaymentKind(
|
|
123
|
+
const makePayment = preparePaymentKind(issuerZone, name, brand, PaymentI);
|
|
125
124
|
|
|
126
125
|
/** @type {ShutdownWithFailure} */
|
|
127
126
|
const shutdownLedgerWithFailure = reason => {
|
|
@@ -139,48 +138,48 @@ export const preparePaymentLedger = (
|
|
|
139
138
|
};
|
|
140
139
|
|
|
141
140
|
/** @type {WeakMapStore<Payment, Amount>} */
|
|
142
|
-
const paymentLedger =
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
{ valueShape: amountShape },
|
|
146
|
-
);
|
|
141
|
+
const paymentLedger = issuerZone.weakMapStore('paymentLedger', {
|
|
142
|
+
valueShape: amountShape,
|
|
143
|
+
});
|
|
147
144
|
|
|
148
145
|
/**
|
|
149
|
-
* A withdrawn live payment is associated with the recovery set of
|
|
150
|
-
* the purse it was withdrawn from. Let's call these "recoverable"
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
146
|
+
* A (non-empty) withdrawn live payment is associated with the recovery set of
|
|
147
|
+
* the purse it was withdrawn from. Let's call these "recoverable" payments.
|
|
148
|
+
* All recoverable payments are live, but not all live payments are
|
|
149
|
+
* recoverable. We do the bookkeeping for payment recovery with this weakmap
|
|
150
|
+
* from recoverable payments to the recovery set they are in. A bunch of
|
|
151
|
+
* interesting invariants here:
|
|
152
|
+
*
|
|
153
|
+
* - Every payment that is a key in the outer `paymentRecoverySets` weakMap is
|
|
154
|
+
* also in the recovery set indexed by that payment.
|
|
155
|
+
* - Implied by the above but worth stating: the payment is only in at most one
|
|
156
|
+
* recovery set.
|
|
157
|
+
* - A recovery set only contains such payments.
|
|
158
|
+
* - Every purse is associated with exactly one recovery set unique to it.
|
|
159
|
+
* - A purse's recovery set only contains payments withdrawn from that purse and
|
|
160
|
+
* not yet consumed.
|
|
161
|
+
*
|
|
162
|
+
* If `recoverySetsState === 'noRecoverySets'`, then nothing should ever be
|
|
163
|
+
* added to this WeakStore.
|
|
165
164
|
*
|
|
166
165
|
* @type {WeakMapStore<Payment, SetStore<Payment>>}
|
|
167
166
|
*/
|
|
168
|
-
const paymentRecoverySets =
|
|
169
|
-
issuerBaggage,
|
|
170
|
-
'paymentRecoverySets',
|
|
171
|
-
);
|
|
167
|
+
const paymentRecoverySets = issuerZone.weakMapStore('paymentRecoverySets');
|
|
172
168
|
|
|
173
169
|
/**
|
|
174
170
|
* To maintain the invariants listed in the `paymentRecoverySets` comment,
|
|
175
|
-
* `initPayment` should contain the only
|
|
176
|
-
* call to `paymentLedger.init`.
|
|
171
|
+
* `initPayment` should contain the only call to `paymentLedger.init`.
|
|
177
172
|
*
|
|
178
173
|
* @param {Payment} payment
|
|
179
174
|
* @param {Amount} amount
|
|
180
175
|
* @param {SetStore<Payment>} [optRecoverySet]
|
|
181
176
|
*/
|
|
182
177
|
const initPayment = (payment, amount, optRecoverySet = undefined) => {
|
|
183
|
-
if (
|
|
178
|
+
if (recoverySetsState === 'noRecoverySets') {
|
|
179
|
+
optRecoverySet === undefined ||
|
|
180
|
+
Fail`when recoverSetsState === 'noRecoverySets', optRecoverySet must be empty`;
|
|
181
|
+
}
|
|
182
|
+
if (optRecoverySet !== undefined && !AmountMath.isEmpty(amount)) {
|
|
184
183
|
optRecoverySet.add(payment);
|
|
185
184
|
paymentRecoverySets.init(payment, optRecoverySet);
|
|
186
185
|
}
|
|
@@ -189,8 +188,7 @@ export const preparePaymentLedger = (
|
|
|
189
188
|
|
|
190
189
|
/**
|
|
191
190
|
* To maintain the invariants listed in the `paymentRecoverySets` comment,
|
|
192
|
-
* `deletePayment` should contain the only
|
|
193
|
-
* call to `paymentLedger.delete`.
|
|
191
|
+
* `deletePayment` should contain the only call to `paymentLedger.delete`.
|
|
194
192
|
*
|
|
195
193
|
* @param {Payment} payment
|
|
196
194
|
*/
|
|
@@ -203,19 +201,14 @@ export const preparePaymentLedger = (
|
|
|
203
201
|
}
|
|
204
202
|
};
|
|
205
203
|
|
|
206
|
-
/** @type {(left: Amount, right: Amount) => Amount } */
|
|
207
|
-
const add = (left, right) => AmountMath.add(left, right, brand);
|
|
208
|
-
/** @type {(left: Amount, right: Amount) => Amount } */
|
|
209
|
-
const subtract = (left, right) => AmountMath.subtract(left, right, brand);
|
|
210
204
|
/** @type {(allegedAmount: Amount) => Amount} */
|
|
211
205
|
const coerce = allegedAmount => AmountMath.coerce(brand, allegedAmount);
|
|
212
|
-
/** @type {(left: Amount, right: Amount) => boolean
|
|
206
|
+
/** @type {(left: Amount, right: Amount) => boolean} */
|
|
213
207
|
|
|
214
208
|
/**
|
|
215
|
-
* Methods like deposit() have an optional second parameter
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* payment. This helper function does that check.
|
|
209
|
+
* Methods like deposit() have an optional second parameter `optAmountShape`
|
|
210
|
+
* which, if present, is supposed to match the balance of the payment. This
|
|
211
|
+
* helper function does that check.
|
|
219
212
|
*
|
|
220
213
|
* Note: `optAmountShape` is user-supplied with no previous validation.
|
|
221
214
|
*
|
|
@@ -243,17 +236,13 @@ export const preparePaymentLedger = (
|
|
|
243
236
|
/**
|
|
244
237
|
* Used by the purse code to implement purse.deposit
|
|
245
238
|
*
|
|
246
|
-
* @param {
|
|
247
|
-
* before a deposit
|
|
248
|
-
* @param {(newPurseBalance: Amount) => void} updatePurseBalance -
|
|
249
|
-
* commit the purse balance
|
|
239
|
+
* @param {import('./amountStore.js').AmountStore} balanceStore
|
|
250
240
|
* @param {Payment} srcPayment
|
|
251
241
|
* @param {Pattern} [optAmountShape]
|
|
252
242
|
* @returns {Amount}
|
|
253
243
|
*/
|
|
254
244
|
const depositInternal = (
|
|
255
|
-
|
|
256
|
-
updatePurseBalance,
|
|
245
|
+
balanceStore,
|
|
257
246
|
srcPayment,
|
|
258
247
|
optAmountShape = undefined,
|
|
259
248
|
) => {
|
|
@@ -265,13 +254,12 @@ export const preparePaymentLedger = (
|
|
|
265
254
|
assertLivePayment(srcPayment);
|
|
266
255
|
const srcPaymentBalance = paymentLedger.get(srcPayment);
|
|
267
256
|
assertAmountConsistent(srcPaymentBalance, optAmountShape);
|
|
268
|
-
const newPurseBalance = add(srcPaymentBalance, currentBalance);
|
|
269
257
|
try {
|
|
270
258
|
// COMMIT POINT
|
|
271
259
|
// Move the assets in `srcPayment` into this purse, using up the
|
|
272
260
|
// source payment, such that total assets are conserved.
|
|
273
261
|
deletePayment(srcPayment);
|
|
274
|
-
|
|
262
|
+
balanceStore.increment(srcPaymentBalance);
|
|
275
263
|
} catch (err) {
|
|
276
264
|
shutdownLedgerWithFailure(err);
|
|
277
265
|
throw err;
|
|
@@ -282,30 +270,19 @@ export const preparePaymentLedger = (
|
|
|
282
270
|
/**
|
|
283
271
|
* Used by the purse code to implement purse.withdraw
|
|
284
272
|
*
|
|
285
|
-
* @param {
|
|
286
|
-
* before a withdrawal
|
|
287
|
-
* @param {(newPurseBalance: Amount) => void} updatePurseBalance -
|
|
288
|
-
* commit the purse balance
|
|
273
|
+
* @param {import('./amountStore.js').AmountStore} balanceStore
|
|
289
274
|
* @param {Amount} amount - the amount to be withdrawn
|
|
290
|
-
* @param {SetStore<Payment>} recoverySet
|
|
275
|
+
* @param {SetStore<Payment>} [recoverySet]
|
|
291
276
|
* @returns {Payment}
|
|
292
277
|
*/
|
|
293
|
-
const withdrawInternal = (
|
|
294
|
-
currentBalance,
|
|
295
|
-
updatePurseBalance,
|
|
296
|
-
amount,
|
|
297
|
-
recoverySet,
|
|
298
|
-
) => {
|
|
278
|
+
const withdrawInternal = (balanceStore, amount, recoverySet = undefined) => {
|
|
299
279
|
amount = coerce(amount);
|
|
300
|
-
AmountMath.isGTE(currentBalance, amount) ||
|
|
301
|
-
Fail`Withdrawal of ${amount} failed because the purse only contained ${currentBalance}`;
|
|
302
|
-
const newPurseBalance = subtract(currentBalance, amount);
|
|
303
|
-
|
|
304
280
|
const payment = makePayment();
|
|
281
|
+
// COMMIT POINT Move the withdrawn assets from this purse into
|
|
282
|
+
// payment. Total assets must remain conserved.
|
|
283
|
+
balanceStore.decrement(amount) ||
|
|
284
|
+
Fail`Withdrawal of ${amount} failed because the purse only contained ${balanceStore.getAmount()}`;
|
|
305
285
|
try {
|
|
306
|
-
// COMMIT POINT Move the withdrawn assets from this purse into
|
|
307
|
-
// payment. Total assets must remain conserved.
|
|
308
|
-
updatePurseBalance(newPurseBalance);
|
|
309
286
|
initPayment(payment, amount, recoverySet);
|
|
310
287
|
} catch (err) {
|
|
311
288
|
shutdownLedgerWithFailure(err);
|
|
@@ -314,8 +291,10 @@ export const preparePaymentLedger = (
|
|
|
314
291
|
return payment;
|
|
315
292
|
};
|
|
316
293
|
|
|
294
|
+
/** @type {() => Purse<K>} */
|
|
295
|
+
// @ts-expect-error type parameter confusion
|
|
317
296
|
const makeEmptyPurse = preparePurseKind(
|
|
318
|
-
|
|
297
|
+
issuerZone,
|
|
319
298
|
name,
|
|
320
299
|
assetKind,
|
|
321
300
|
brand,
|
|
@@ -324,11 +303,17 @@ export const preparePaymentLedger = (
|
|
|
324
303
|
depositInternal,
|
|
325
304
|
withdrawInternal,
|
|
326
305
|
}),
|
|
306
|
+
recoverySetsState,
|
|
307
|
+
paymentRecoverySets,
|
|
327
308
|
);
|
|
328
309
|
|
|
329
310
|
/** @type {Issuer<K>} */
|
|
330
|
-
//
|
|
331
|
-
|
|
311
|
+
// Should be
|
|
312
|
+
// at-ts-expect-error cast due to callWhen discrepancy
|
|
313
|
+
// but ran into the usual disagreement between local lint and CI
|
|
314
|
+
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
315
|
+
// @ts-ignore
|
|
316
|
+
const issuer = issuerZone.exo(`${name} issuer`, IssuerI, {
|
|
332
317
|
getBrand() {
|
|
333
318
|
return brand;
|
|
334
319
|
},
|
|
@@ -378,23 +363,31 @@ export const preparePaymentLedger = (
|
|
|
378
363
|
/**
|
|
379
364
|
* Provides for the recovery of newly minted but not-yet-deposited payments.
|
|
380
365
|
*
|
|
381
|
-
* Because the `mintRecoveryPurse` is placed in baggage, even if the
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
* @type {Purse<K>}
|
|
366
|
+
* Because the `mintRecoveryPurse` is placed in baggage, even if the caller of
|
|
367
|
+
* `makeIssuerKit` drops it on the floor, it can still be recovered in an
|
|
368
|
+
* emergency upgrade.
|
|
386
369
|
*/
|
|
387
|
-
|
|
388
|
-
|
|
370
|
+
// Should be
|
|
371
|
+
// at-ts-expect-error checked cast
|
|
372
|
+
// but ran into the usual disagreement between local lint and IDE lint.
|
|
373
|
+
// Don't know yet about lint under CI.
|
|
374
|
+
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
375
|
+
// @ts-ignore
|
|
376
|
+
const mintRecoveryPurse = /** @type {Purse<K>} */ (
|
|
377
|
+
issuerZone.makeOnce('mintRecoveryPurse', () => makeEmptyPurse())
|
|
389
378
|
);
|
|
390
379
|
|
|
391
380
|
/** @type {Mint<K>} */
|
|
392
|
-
const mint =
|
|
381
|
+
const mint = issuerZone.exo(`${name} mint`, MintI, {
|
|
393
382
|
getIssuer() {
|
|
394
383
|
return issuer;
|
|
395
384
|
},
|
|
396
385
|
mintPayment(newAmount) {
|
|
397
|
-
//
|
|
386
|
+
// Should be
|
|
387
|
+
// at-ts-expect-error checked cast
|
|
388
|
+
// but ran into the usual disagreement between local lint and CI
|
|
389
|
+
// eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error
|
|
390
|
+
// @ts-ignore
|
|
398
391
|
newAmount = coerce(newAmount);
|
|
399
392
|
mustMatch(newAmount, amountShape, 'minted amount');
|
|
400
393
|
// `rawPayment` is not associated with any recovery set, and
|
package/src/purse.js
CHANGED
|
@@ -1,27 +1,72 @@
|
|
|
1
|
-
import { M } from '@agoric/store';
|
|
2
|
-
import { prepareExoClassKit, makeScalarBigSetStore } from '@agoric/vat-data';
|
|
1
|
+
import { M, makeCopySet } from '@agoric/store';
|
|
3
2
|
import { AmountMath } from './amountMath.js';
|
|
4
3
|
import { makeTransientNotifierKit } from './transientNotifier.js';
|
|
4
|
+
import { makeAmountStore } from './amountStore.js';
|
|
5
5
|
|
|
6
6
|
const { Fail } = assert;
|
|
7
7
|
|
|
8
|
+
const EMPTY_COPY_SET = makeCopySet([]);
|
|
9
|
+
|
|
10
|
+
// TODO Type InterfaceGuard better than InterfaceGuard<any>
|
|
11
|
+
/**
|
|
12
|
+
* @param {import('@agoric/zone').Zone} issuerZone
|
|
13
|
+
* @param {string} name
|
|
14
|
+
* @param {AssetKind} assetKind
|
|
15
|
+
* @param {Brand} brand
|
|
16
|
+
* @param {{
|
|
17
|
+
* purse: import('@endo/patterns').InterfaceGuard<any>;
|
|
18
|
+
* depositFacet: import('@endo/patterns').InterfaceGuard<any>;
|
|
19
|
+
* }} PurseIKit
|
|
20
|
+
* @param {{
|
|
21
|
+
* depositInternal: any;
|
|
22
|
+
* withdrawInternal: any;
|
|
23
|
+
* }} purseMethods
|
|
24
|
+
* @param {RecoverySetsOption} recoverySetsState
|
|
25
|
+
* @param {WeakMapStore<Payment, SetStore<Payment>>} paymentRecoverySets
|
|
26
|
+
*/
|
|
8
27
|
export const preparePurseKind = (
|
|
9
|
-
|
|
28
|
+
issuerZone,
|
|
10
29
|
name,
|
|
11
30
|
assetKind,
|
|
12
31
|
brand,
|
|
13
32
|
PurseIKit,
|
|
14
33
|
purseMethods,
|
|
34
|
+
recoverySetsState,
|
|
35
|
+
paymentRecoverySets,
|
|
15
36
|
) => {
|
|
16
37
|
const amountShape = brand.getAmountShape();
|
|
17
38
|
|
|
18
39
|
// Note: Virtual for high cardinality, but *not* durable, and so
|
|
19
40
|
// broken across an upgrade.
|
|
41
|
+
// TODO propagate zonifying to notifiers, maybe?
|
|
20
42
|
const { provideNotifier, update: updateBalance } = makeTransientNotifierKit();
|
|
21
43
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
44
|
+
/**
|
|
45
|
+
* If `recoverySetsState === 'hasRecoverySets'` (the normal state), then just
|
|
46
|
+
* return `state.recoverySet`.
|
|
47
|
+
*
|
|
48
|
+
* If `recoverySetsState === 'noRecoverySets'`, return `undefined`. Callers
|
|
49
|
+
* must be aware that the `undefined` return happens iff `recoverySetsState
|
|
50
|
+
* === 'noRecoverySets'`, and to avoid storing or retrieving anything from the
|
|
51
|
+
* actual recovery set.
|
|
52
|
+
*
|
|
53
|
+
* @param {{ recoverySet: SetStore<Payment> }} state
|
|
54
|
+
* @returns {SetStore<Payment> | undefined}
|
|
55
|
+
*/
|
|
56
|
+
const maybeRecoverySet = state => {
|
|
57
|
+
const { recoverySet } = state;
|
|
58
|
+
if (recoverySetsState === 'hasRecoverySets') {
|
|
59
|
+
return recoverySet;
|
|
60
|
+
} else {
|
|
61
|
+
recoverySetsState === 'noRecoverySets' ||
|
|
62
|
+
Fail`recoverSetsState must be noRecoverySets if it isn't hasRecoverSets`;
|
|
63
|
+
paymentRecoverySets !== undefined ||
|
|
64
|
+
Fail`paymentRecoverySets must always be defined`;
|
|
65
|
+
recoverySet.getSize() === 0 ||
|
|
66
|
+
Fail`With noRecoverySets, recoverySet must be empty`;
|
|
67
|
+
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
25
70
|
};
|
|
26
71
|
|
|
27
72
|
// - This kind is a pair of purse and depositFacet that have a 1:1
|
|
@@ -31,17 +76,14 @@ export const preparePurseKind = (
|
|
|
31
76
|
// that created depositFacet as needed. But this approach ensures a constant
|
|
32
77
|
// identity for the facet and exercises the multi-faceted object style.
|
|
33
78
|
const { depositInternal, withdrawInternal } = purseMethods;
|
|
34
|
-
const makePurseKit =
|
|
35
|
-
issuerBaggage,
|
|
79
|
+
const makePurseKit = issuerZone.exoClassKit(
|
|
36
80
|
`${name} Purse`,
|
|
37
81
|
PurseIKit,
|
|
38
82
|
() => {
|
|
39
83
|
const currentBalance = AmountMath.makeEmpty(brand, assetKind);
|
|
40
84
|
|
|
41
85
|
/** @type {SetStore<Payment>} */
|
|
42
|
-
const recoverySet =
|
|
43
|
-
durable: true,
|
|
44
|
-
});
|
|
86
|
+
const recoverySet = issuerZone.detached().setStore('recovery set');
|
|
45
87
|
|
|
46
88
|
return {
|
|
47
89
|
currentBalance,
|
|
@@ -54,28 +96,36 @@ export const preparePurseKind = (
|
|
|
54
96
|
// PurseI does *not* delay `deposit` until `srcPayment` is fulfulled.
|
|
55
97
|
// See the comments on PurseI.deposit in typeGuards.js
|
|
56
98
|
const { state } = this;
|
|
99
|
+
const { purse } = this.facets;
|
|
100
|
+
const balanceStore = makeAmountStore(state, 'currentBalance');
|
|
57
101
|
// Note COMMIT POINT within deposit.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
newPurseBalance =>
|
|
61
|
-
updatePurseBalance(state, newPurseBalance, this.facets.purse),
|
|
102
|
+
const srcPaymentBalance = depositInternal(
|
|
103
|
+
balanceStore,
|
|
62
104
|
srcPayment,
|
|
63
105
|
optAmountShape,
|
|
64
106
|
);
|
|
107
|
+
updateBalance(purse, balanceStore.getAmount());
|
|
108
|
+
return srcPaymentBalance;
|
|
65
109
|
},
|
|
66
110
|
withdraw(amount) {
|
|
67
111
|
const { state } = this;
|
|
112
|
+
const { purse } = this.facets;
|
|
113
|
+
|
|
114
|
+
const optRecoverySet = maybeRecoverySet(state);
|
|
115
|
+
const balanceStore = makeAmountStore(state, 'currentBalance');
|
|
68
116
|
// Note COMMIT POINT within withdraw.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
newPurseBalance =>
|
|
72
|
-
updatePurseBalance(state, newPurseBalance, this.facets.purse),
|
|
117
|
+
const payment = withdrawInternal(
|
|
118
|
+
balanceStore,
|
|
73
119
|
amount,
|
|
74
|
-
|
|
120
|
+
optRecoverySet,
|
|
75
121
|
);
|
|
122
|
+
updateBalance(purse, balanceStore.getAmount());
|
|
123
|
+
return payment;
|
|
76
124
|
},
|
|
77
125
|
getCurrentAmount() {
|
|
78
|
-
|
|
126
|
+
const { state } = this;
|
|
127
|
+
const balanceStore = makeAmountStore(state, 'currentBalance');
|
|
128
|
+
return balanceStore.getAmount();
|
|
79
129
|
},
|
|
80
130
|
getCurrentAmountNotifier() {
|
|
81
131
|
return provideNotifier(this.facets.purse);
|
|
@@ -89,18 +139,27 @@ export const preparePurseKind = (
|
|
|
89
139
|
},
|
|
90
140
|
|
|
91
141
|
getRecoverySet() {
|
|
92
|
-
|
|
142
|
+
const { state } = this;
|
|
143
|
+
const optRecoverySet = maybeRecoverySet(state);
|
|
144
|
+
if (optRecoverySet === undefined) {
|
|
145
|
+
return EMPTY_COPY_SET;
|
|
146
|
+
}
|
|
147
|
+
return optRecoverySet.snapshot();
|
|
93
148
|
},
|
|
94
149
|
recoverAll() {
|
|
95
150
|
const { state, facets } = this;
|
|
96
151
|
let amount = AmountMath.makeEmpty(brand, assetKind);
|
|
97
|
-
|
|
152
|
+
const optRecoverySet = maybeRecoverySet(state);
|
|
153
|
+
if (optRecoverySet === undefined) {
|
|
154
|
+
return amount; // empty at this time
|
|
155
|
+
}
|
|
156
|
+
for (const payment of optRecoverySet.keys()) {
|
|
98
157
|
// This does cause deletions from the set while iterating,
|
|
99
158
|
// but this special case is allowed.
|
|
100
159
|
const delta = facets.purse.deposit(payment);
|
|
101
160
|
amount = AmountMath.add(amount, delta, brand);
|
|
102
161
|
}
|
|
103
|
-
|
|
162
|
+
optRecoverySet.getSize() === 0 ||
|
|
104
163
|
Fail`internal: Remaining unrecovered payments: ${facets.purse.getRecoverySet()}`;
|
|
105
164
|
return amount;
|
|
106
165
|
},
|
package/src/typeGuards.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @jessie-check
|
|
2
2
|
|
|
3
|
-
import { M, matches } from '@
|
|
3
|
+
import { M, matches, getInterfaceGuardPayload } from '@endo/patterns';
|
|
4
4
|
|
|
5
5
|
export const BrandShape = M.remotable('Brand');
|
|
6
6
|
export const IssuerShape = M.remotable('Issuer');
|
|
@@ -11,62 +11,56 @@ export const NotifierShape = M.remotable('Notifier');
|
|
|
11
11
|
export const MintShape = M.remotable('Mint');
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* When the AmountValue of an Amount fits the NatValueShape, i.e., when it is
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* set of fungible units.
|
|
14
|
+
* When the AmountValue of an Amount fits the NatValueShape, i.e., when it is a
|
|
15
|
+
* non-negative bigint, then it represents that many units of the fungible asset
|
|
16
|
+
* represented by that amount. The brand of that amount should indeed represent
|
|
17
|
+
* a kind of asset consisting of a countable set of fungible units.
|
|
19
18
|
*/
|
|
20
19
|
const NatValueShape = M.nat();
|
|
21
20
|
|
|
22
21
|
/**
|
|
23
22
|
* When the AmountValue of an Amount fits the CopySetValueShape, i.e., when it
|
|
24
|
-
* is a CopySet, then it represents the set of those
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* from the same asset class.
|
|
23
|
+
* is a CopySet, then it represents the set of those keys, where each key
|
|
24
|
+
* represents some individual non-fungible item, like a concert ticket, from the
|
|
25
|
+
* non-fungible asset class represented by that amount's brand. The amount
|
|
26
|
+
* itself represents the set of these items, as opposed to any of the other
|
|
27
|
+
* items from the same asset class.
|
|
30
28
|
*
|
|
31
|
-
* If a given value class represents concert tickets, it seems bizarre
|
|
32
|
-
*
|
|
33
|
-
* the
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* "You can't always get..."
|
|
29
|
+
* If a given value class represents concert tickets, it seems bizarre that we
|
|
30
|
+
* can form amounts of any key. The hard constraint is that the code that holds
|
|
31
|
+
* the mint for that asset class---the one associated with that brand, only
|
|
32
|
+
* mints the items representing the real units of that asset class as defined by
|
|
33
|
+
* it. Anyone else can put together an amount expressing, for example, that they
|
|
34
|
+
* "want" some items that will never be minted. That want will never be
|
|
35
|
+
* satisfied. "You can't always get..."
|
|
39
36
|
*/
|
|
40
37
|
const CopySetValueShape = M.set();
|
|
41
38
|
|
|
42
39
|
/**
|
|
43
|
-
* When the AmountValue of an Amount fits the SetValueShape, i.e., when it
|
|
44
|
-
*
|
|
40
|
+
* When the AmountValue of an Amount fits the SetValueShape, i.e., when it is a
|
|
41
|
+
* CopyArray of passable Keys. This representation is deprecated.
|
|
45
42
|
*
|
|
46
43
|
* @deprecated Please change from using array-based SetValues to CopySet-based
|
|
47
|
-
*
|
|
44
|
+
* CopySetValues.
|
|
48
45
|
*/
|
|
49
46
|
const SetValueShape = M.arrayOf(M.key());
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
49
|
* When the AmountValue of an Amount fits the CopyBagValueShape, i.e., when it
|
|
53
|
-
* is a CopyBag, then it represents the bag (multiset) of those
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* the bag of these items, as opposed to any of the other items
|
|
60
|
-
* from the same asset class.
|
|
50
|
+
* is a CopyBag, then it represents the bag (multiset) of those keys, where each
|
|
51
|
+
* key represents some individual semi-fungible item, like a concert ticket,
|
|
52
|
+
* from the semi-fungible asset class represented by that amount's brand. The
|
|
53
|
+
* number of times that key appears in the bag is the number of fungible units
|
|
54
|
+
* of that key. The amount itself represents the bag of these items, as opposed
|
|
55
|
+
* to any of the other items from the same asset class.
|
|
61
56
|
*
|
|
62
|
-
* If a given value class represents concert tickets, it seems bizarre
|
|
63
|
-
*
|
|
64
|
-
* the
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* "You can't always get..."
|
|
57
|
+
* If a given value class represents concert tickets, it seems bizarre that we
|
|
58
|
+
* can form amounts of any key. The hard constraint is that the code that holds
|
|
59
|
+
* the mint for that asset class---the one associated with that brand, only
|
|
60
|
+
* mints the items representing the real units of that asset class as defined by
|
|
61
|
+
* it. Anyone else can put together an amount expressing, for example, that they
|
|
62
|
+
* "want" some items that will never be minted. That want will never be
|
|
63
|
+
* satisfied. "You can't always get..."
|
|
70
64
|
*/
|
|
71
65
|
const CopyBagValueShape = M.bag();
|
|
72
66
|
|
|
@@ -106,11 +100,11 @@ export const isCopySetValue = value => matches(value, CopySetValueShape);
|
|
|
106
100
|
harden(isCopySetValue);
|
|
107
101
|
|
|
108
102
|
/**
|
|
109
|
-
* Returns true if value is a pass by copy array structure. Does not
|
|
110
|
-
*
|
|
103
|
+
* Returns true if value is a pass by copy array structure. Does not check for
|
|
104
|
+
* duplicates. To check for duplicates, use setMathHelpers.coerce.
|
|
111
105
|
*
|
|
112
106
|
* @deprecated Please change from using array-based SetValues to CopySet-based
|
|
113
|
-
*
|
|
107
|
+
* CopySetValues.
|
|
114
108
|
* @param {AmountValue} value
|
|
115
109
|
* @returns {value is SetValue}
|
|
116
110
|
*/
|
|
@@ -218,7 +212,7 @@ export const makeIssuerInterfaces = (
|
|
|
218
212
|
});
|
|
219
213
|
|
|
220
214
|
const DepositFacetI = M.interface('DepositFacet', {
|
|
221
|
-
receive: PurseI.methodGuards.deposit,
|
|
215
|
+
receive: getInterfaceGuardPayload(PurseI).methodGuards.deposit,
|
|
222
216
|
});
|
|
223
217
|
|
|
224
218
|
const PurseIKit = harden({
|