@agoric/smart-wallet 0.5.4-u12.0 → 0.5.4-u14.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/CHANGELOG.md +28 -0
- package/package.json +14 -12
- package/src/offerWatcher.d.ts +66 -0
- package/src/offerWatcher.d.ts.map +1 -0
- package/src/offerWatcher.js +294 -0
- package/src/offers.d.ts +0 -24
- package/src/offers.d.ts.map +1 -1
- package/src/offers.js +0 -174
- package/src/proposals/upgrade-wallet-factory2-proposal.d.ts +23 -0
- package/src/proposals/upgrade-wallet-factory2-proposal.d.ts.map +1 -0
- package/src/proposals/upgrade-wallet-factory2-proposal.js +59 -0
- package/src/smartWallet.d.ts +37 -15
- package/src/smartWallet.d.ts.map +1 -1
- package/src/smartWallet.js +556 -144
- package/src/types.d.ts +11 -1
- package/src/walletFactory.d.ts +6 -4
- package/src/walletFactory.d.ts.map +1 -1
- package/src/walletFactory.js +35 -3
- package/src/payments.d.ts +0 -20
- package/src/payments.d.ts.map +0 -1
- package/src/payments.js +0 -89
package/src/smartWallet.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
// @ts-nocheck
|
|
1
|
+
import { E } from '@endo/far';
|
|
3
2
|
import {
|
|
4
3
|
AmountShape,
|
|
5
4
|
BrandShape,
|
|
@@ -8,8 +7,13 @@ import {
|
|
|
8
7
|
PaymentShape,
|
|
9
8
|
PurseShape,
|
|
10
9
|
} from '@agoric/ertp';
|
|
11
|
-
import {
|
|
12
|
-
|
|
10
|
+
import {
|
|
11
|
+
deeplyFulfilledObject,
|
|
12
|
+
makeTracer,
|
|
13
|
+
objectMap,
|
|
14
|
+
StorageNodeShape,
|
|
15
|
+
} from '@agoric/internal';
|
|
16
|
+
import { isUpgradeDisconnection } from '@agoric/internal/src/upgrade-api.js';
|
|
13
17
|
import { M, mustMatch } from '@agoric/store';
|
|
14
18
|
import {
|
|
15
19
|
appendToStoredArray,
|
|
@@ -18,19 +22,25 @@ import {
|
|
|
18
22
|
import {
|
|
19
23
|
makeScalarBigMapStore,
|
|
20
24
|
makeScalarBigWeakMapStore,
|
|
25
|
+
prepareExoClass,
|
|
21
26
|
prepareExoClassKit,
|
|
22
27
|
provide,
|
|
28
|
+
watchPromise,
|
|
23
29
|
} from '@agoric/vat-data';
|
|
24
30
|
import {
|
|
31
|
+
prepareRecorderKit,
|
|
25
32
|
SubscriberShape,
|
|
26
33
|
TopicsRecordShape,
|
|
27
|
-
prepareRecorderKit,
|
|
28
34
|
} from '@agoric/zoe/src/contractSupport/index.js';
|
|
29
|
-
import {
|
|
35
|
+
import {
|
|
36
|
+
AmountKeywordRecordShape,
|
|
37
|
+
PaymentPKeywordRecordShape,
|
|
38
|
+
} from '@agoric/zoe/src/typeGuards.js';
|
|
39
|
+
|
|
30
40
|
import { makeInvitationsHelper } from './invitations.js';
|
|
31
|
-
import { makeOfferExecutor } from './offers.js';
|
|
32
41
|
import { shape } from './typeGuards.js';
|
|
33
42
|
import { objectMapStoragePath } from './utils.js';
|
|
43
|
+
import { prepareOfferWatcher, watchOfferOutcomes } from './offerWatcher.js';
|
|
34
44
|
|
|
35
45
|
const { Fail, quote: q } = assert;
|
|
36
46
|
|
|
@@ -42,17 +52,36 @@ const trace = makeTracer('SmrtWlt');
|
|
|
42
52
|
* @see {@link ../README.md}}
|
|
43
53
|
*/
|
|
44
54
|
|
|
55
|
+
/** @typedef {number | string} OfferId */
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @typedef {{
|
|
59
|
+
* id: OfferId,
|
|
60
|
+
* invitationSpec: import('./invitations').InvitationSpec,
|
|
61
|
+
* proposal: Proposal,
|
|
62
|
+
* offerArgs?: unknown
|
|
63
|
+
* }} OfferSpec
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {{
|
|
68
|
+
* logger: {info: (...args: any[]) => void, error: (...args: any[]) => void},
|
|
69
|
+
* makeOfferWatcher: import('./offerWatcher.js').MakeOfferWatcher,
|
|
70
|
+
* invitationFromSpec: ERef<Invitation>,
|
|
71
|
+
* }} ExecutorPowers
|
|
72
|
+
*/
|
|
73
|
+
|
|
45
74
|
/**
|
|
46
75
|
* @typedef {{
|
|
47
76
|
* method: 'executeOffer'
|
|
48
|
-
* offer:
|
|
77
|
+
* offer: OfferSpec,
|
|
49
78
|
* }} ExecuteOfferAction
|
|
50
79
|
*/
|
|
51
80
|
|
|
52
81
|
/**
|
|
53
82
|
* @typedef {{
|
|
54
83
|
* method: 'tryExitOffer'
|
|
55
|
-
* offerId:
|
|
84
|
+
* offerId: OfferId,
|
|
56
85
|
* }} TryExitOfferAction
|
|
57
86
|
*/
|
|
58
87
|
|
|
@@ -83,7 +112,7 @@ const trace = makeTracer('SmrtWlt');
|
|
|
83
112
|
* purses: Array<{brand: Brand, balance: Amount}>,
|
|
84
113
|
* offerToUsedInvitation: Array<[ offerId: string, usedInvitation: Amount ]>,
|
|
85
114
|
* offerToPublicSubscriberPaths: Array<[ offerId: string, publicTopics: { [subscriberName: string]: string } ]>,
|
|
86
|
-
* liveOffers: Array<[
|
|
115
|
+
* liveOffers: Array<[OfferId, import('./offers.js').OfferStatus]>,
|
|
87
116
|
* }} CurrentWalletRecord
|
|
88
117
|
*/
|
|
89
118
|
|
|
@@ -108,18 +137,15 @@ const trace = makeTracer('SmrtWlt');
|
|
|
108
137
|
* brand: Brand,
|
|
109
138
|
* displayInfo: DisplayInfo,
|
|
110
139
|
* issuer: Issuer,
|
|
111
|
-
* petname: import('./types').Petname
|
|
140
|
+
* petname: import('./types.js').Petname
|
|
112
141
|
* }} BrandDescriptor
|
|
113
142
|
* For use by clients to describe brands to users. Includes `displayInfo` to save a remote call.
|
|
114
143
|
*/
|
|
115
144
|
|
|
116
|
-
// imports
|
|
117
|
-
/** @typedef {import('./types').RemotePurse} RemotePurse */
|
|
118
|
-
|
|
119
145
|
/**
|
|
120
146
|
* @typedef {{
|
|
121
147
|
* address: string,
|
|
122
|
-
* bank: ERef<import('@agoric/vats/src/vat-bank').Bank>,
|
|
148
|
+
* bank: ERef<import('@agoric/vats/src/vat-bank.js').Bank>,
|
|
123
149
|
* currentStorageNode: StorageNode,
|
|
124
150
|
* invitationPurse: Purse<'set'>,
|
|
125
151
|
* walletStorageNode: StorageNode,
|
|
@@ -134,6 +160,7 @@ const trace = makeTracer('SmrtWlt');
|
|
|
134
160
|
* invitationDisplayInfo: DisplayInfo,
|
|
135
161
|
* publicMarshaller: Marshaller,
|
|
136
162
|
* zoe: ERef<ZoeService>,
|
|
163
|
+
* secretWalletFactoryKey: any,
|
|
137
164
|
* }} SharedParams
|
|
138
165
|
*
|
|
139
166
|
* @typedef {ImmutableState & MutableState} State
|
|
@@ -144,14 +171,15 @@ const trace = makeTracer('SmrtWlt');
|
|
|
144
171
|
*
|
|
145
172
|
* @typedef {Readonly<UniqueParams & {
|
|
146
173
|
* paymentQueues: MapStore<Brand, Array<Payment>>,
|
|
147
|
-
* offerToInvitationMakers: MapStore<string, import('./types').
|
|
174
|
+
* offerToInvitationMakers: MapStore<string, import('./types').RemoteInvitationMakers>,
|
|
148
175
|
* offerToPublicSubscriberPaths: MapStore<string, Record<string, string>>,
|
|
149
|
-
* offerToUsedInvitation: MapStore<string, Amount
|
|
150
|
-
* purseBalances: MapStore<
|
|
176
|
+
* offerToUsedInvitation: MapStore<string, Amount<'set'>>,
|
|
177
|
+
* purseBalances: MapStore<Purse, Amount>,
|
|
151
178
|
* updateRecorderKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<UpdateRecord>,
|
|
152
179
|
* currentRecorderKit: import('@agoric/zoe/src/contractSupport/recorder.js').RecorderKit<CurrentWalletRecord>,
|
|
153
|
-
* liveOffers: MapStore<
|
|
154
|
-
* liveOfferSeats:
|
|
180
|
+
* liveOffers: MapStore<OfferId, import('./offers.js').OfferStatus>,
|
|
181
|
+
* liveOfferSeats: MapStore<OfferId, UserSeat<unknown>>,
|
|
182
|
+
* liveOfferPayments: MapStore<OfferId, MapStore<Brand, Payment>>,
|
|
155
183
|
* }>} ImmutableState
|
|
156
184
|
*
|
|
157
185
|
* @typedef {BrandDescriptor & { purse: Purse }} PurseRecord
|
|
@@ -165,7 +193,7 @@ const trace = makeTracer('SmrtWlt');
|
|
|
165
193
|
* TODO: consider moving to nameHub.js?
|
|
166
194
|
*
|
|
167
195
|
* @param {unknown} target - passable Key
|
|
168
|
-
* @param {ERef<NameHub>} nameHub
|
|
196
|
+
* @param {ERef<import('@agoric/vats').NameHub>} nameHub
|
|
169
197
|
*/
|
|
170
198
|
const namesOf = async (target, nameHub) => {
|
|
171
199
|
const entries = await E(nameHub).entries();
|
|
@@ -223,6 +251,12 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
223
251
|
invitationDisplayInfo: DisplayInfoShape,
|
|
224
252
|
publicMarshaller: M.remotable('Marshaller'),
|
|
225
253
|
zoe: M.eref(M.remotable('ZoeService')),
|
|
254
|
+
|
|
255
|
+
// known only to smartWallets and walletFactory, this allows the
|
|
256
|
+
// walletFactory to invoke functions on the self facet that no one else
|
|
257
|
+
// can. Used to protect the upgrade-to-incarnation 2 repair. This can be
|
|
258
|
+
// dropped once the repair has taken place.
|
|
259
|
+
secretWalletFactoryKey: M.any(),
|
|
226
260
|
}),
|
|
227
261
|
);
|
|
228
262
|
|
|
@@ -237,8 +271,62 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
237
271
|
return store;
|
|
238
272
|
});
|
|
239
273
|
|
|
274
|
+
const makeOfferWatcher = prepareOfferWatcher(baggage);
|
|
275
|
+
|
|
276
|
+
const updateShape = {
|
|
277
|
+
value: AmountShape,
|
|
278
|
+
updateCount: M.bigint(),
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const NotifierShape = M.remotable();
|
|
282
|
+
const amountWatcherGuard = M.interface('paymentWatcher', {
|
|
283
|
+
onFulfilled: M.call(updateShape, NotifierShape).returns(),
|
|
284
|
+
onRejected: M.call(M.any(), NotifierShape).returns(M.promise()),
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const prepareAmountWatcher = () =>
|
|
288
|
+
prepareExoClass(
|
|
289
|
+
baggage,
|
|
290
|
+
'AmountWatcher',
|
|
291
|
+
amountWatcherGuard,
|
|
292
|
+
/**
|
|
293
|
+
* @param {Purse} purse
|
|
294
|
+
* @param {ReturnType<makeWalletWithResolvedStorageNodes>['helper']} helper
|
|
295
|
+
*/
|
|
296
|
+
(purse, helper) => ({ purse, helper }),
|
|
297
|
+
{
|
|
298
|
+
/**
|
|
299
|
+
* @param {{ value: Amount, updateCount: bigint | undefined }} updateRecord
|
|
300
|
+
* @param { Notifier<Amount> } notifier
|
|
301
|
+
* @returns {void}
|
|
302
|
+
*/
|
|
303
|
+
onFulfilled(updateRecord, notifier) {
|
|
304
|
+
const { helper, purse } = this.state;
|
|
305
|
+
helper.updateBalance(purse, updateRecord.value);
|
|
306
|
+
helper.watchNextBalance(
|
|
307
|
+
this.self,
|
|
308
|
+
notifier,
|
|
309
|
+
updateRecord.updateCount,
|
|
310
|
+
);
|
|
311
|
+
},
|
|
312
|
+
/**
|
|
313
|
+
* @param {unknown} err
|
|
314
|
+
* @returns {Promise<void>}
|
|
315
|
+
*/
|
|
316
|
+
onRejected(err) {
|
|
317
|
+
const { helper, purse } = this.state;
|
|
318
|
+
if (isUpgradeDisconnection(err)) {
|
|
319
|
+
return helper.watchPurse(purse); // retry
|
|
320
|
+
}
|
|
321
|
+
helper.logWalletError(`failed amount observer`, err);
|
|
322
|
+
throw err;
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
const makeAmountWatcher = prepareAmountWatcher();
|
|
328
|
+
|
|
240
329
|
/**
|
|
241
|
-
*
|
|
242
330
|
* @param {UniqueParams} unique
|
|
243
331
|
* @returns {State}
|
|
244
332
|
*/
|
|
@@ -302,6 +390,9 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
302
390
|
liveOfferSeats: makeScalarBigMapStore('live offer seats', {
|
|
303
391
|
durable: true,
|
|
304
392
|
}),
|
|
393
|
+
liveOfferPayments: makeScalarBigMapStore('live offer payments', {
|
|
394
|
+
durable: true,
|
|
395
|
+
}),
|
|
305
396
|
};
|
|
306
397
|
|
|
307
398
|
return {
|
|
@@ -320,10 +411,34 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
320
411
|
.returns(M.promise()),
|
|
321
412
|
publishCurrentState: M.call().returns(),
|
|
322
413
|
watchPurse: M.call(M.eref(PurseShape)).returns(M.promise()),
|
|
414
|
+
watchNextBalance: M.call(M.any(), NotifierShape, M.bigint()).returns(),
|
|
415
|
+
repairUnwatchedSeats: M.call().returns(M.promise()),
|
|
416
|
+
repairUnwatchedPurses: M.call().returns(M.promise()),
|
|
417
|
+
updateStatus: M.call(M.any()).returns(),
|
|
418
|
+
addContinuingOffer: M.call(
|
|
419
|
+
M.or(M.number(), M.string()),
|
|
420
|
+
AmountShape,
|
|
421
|
+
M.remotable('InvitationMaker'),
|
|
422
|
+
M.or(M.record(), M.undefined()),
|
|
423
|
+
).returns(M.promise()),
|
|
424
|
+
purseForBrand: M.call(BrandShape).returns(M.promise()),
|
|
425
|
+
logWalletInfo: M.call().rest(M.arrayOf(M.any())).returns(),
|
|
426
|
+
logWalletError: M.call().rest(M.arrayOf(M.any())).returns(),
|
|
427
|
+
getLiveOfferPayments: M.call().returns(M.remotable('mapStore')),
|
|
323
428
|
}),
|
|
429
|
+
|
|
324
430
|
deposit: M.interface('depositFacetI', {
|
|
325
431
|
receive: M.callWhen(M.await(M.eref(PaymentShape))).returns(AmountShape),
|
|
326
432
|
}),
|
|
433
|
+
payments: M.interface('payments support', {
|
|
434
|
+
withdrawGive: M.call(
|
|
435
|
+
AmountKeywordRecordShape,
|
|
436
|
+
M.or(M.number(), M.string()),
|
|
437
|
+
).returns(PaymentPKeywordRecordShape),
|
|
438
|
+
tryReclaimingWithdrawnPayments: M.call(
|
|
439
|
+
M.or(M.number(), M.string()),
|
|
440
|
+
).returns(M.promise()),
|
|
441
|
+
}),
|
|
327
442
|
offers: M.interface('offers facet', {
|
|
328
443
|
executeOffer: M.call(shape.OfferSpec).returns(M.promise()),
|
|
329
444
|
tryExitOffer: M.call(M.scalar()).returns(M.promise()),
|
|
@@ -337,9 +452,11 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
337
452
|
getCurrentSubscriber: M.call().returns(SubscriberShape),
|
|
338
453
|
getUpdatesSubscriber: M.call().returns(SubscriberShape),
|
|
339
454
|
getPublicTopics: M.call().returns(TopicsRecordShape),
|
|
455
|
+
repairWalletForIncarnation2: M.call(M.any()).returns(),
|
|
340
456
|
}),
|
|
341
457
|
};
|
|
342
458
|
|
|
459
|
+
// TODO move to top level so its type can be exported
|
|
343
460
|
/**
|
|
344
461
|
* Make the durable object to return, but taking some parameters that are awaited by a wrapping function.
|
|
345
462
|
* This is necessary because the class kit construction helpers, `initState` and `finish` run synchronously
|
|
@@ -360,6 +477,7 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
360
477
|
* @type {(id: string) => void}
|
|
361
478
|
*/
|
|
362
479
|
assertUniqueOfferId(id) {
|
|
480
|
+
const { facets } = this;
|
|
363
481
|
const {
|
|
364
482
|
liveOffers,
|
|
365
483
|
liveOfferSeats,
|
|
@@ -370,13 +488,14 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
370
488
|
const used =
|
|
371
489
|
liveOffers.has(id) ||
|
|
372
490
|
liveOfferSeats.has(id) ||
|
|
491
|
+
facets.helper.getLiveOfferPayments().has(id) ||
|
|
373
492
|
offerToInvitationMakers.has(id) ||
|
|
374
493
|
offerToPublicSubscriberPaths.has(id) ||
|
|
375
494
|
offerToUsedInvitation.has(id);
|
|
376
495
|
!used || Fail`cannot re-use offer id ${id}`;
|
|
377
496
|
},
|
|
378
497
|
/**
|
|
379
|
-
* @param {
|
|
498
|
+
* @param {Purse} purse
|
|
380
499
|
* @param {Amount<any>} balance
|
|
381
500
|
*/
|
|
382
501
|
updateBalance(purse, balance) {
|
|
@@ -415,44 +534,40 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
415
534
|
});
|
|
416
535
|
},
|
|
417
536
|
|
|
418
|
-
/** @type {(purse: ERef<
|
|
537
|
+
/** @type {(purse: ERef<Purse>) => Promise<void>} */
|
|
419
538
|
async watchPurse(purseRef) {
|
|
420
|
-
const {
|
|
539
|
+
const { helper } = this.facets;
|
|
540
|
+
|
|
541
|
+
// This would seem to fit the observeNotifier() pattern,
|
|
542
|
+
// but purse notifiers are not necessarily durable.
|
|
543
|
+
// If there is an error due to upgrade, retry watchPurse().
|
|
421
544
|
|
|
422
545
|
const purse = await purseRef; // promises don't fit in durable storage
|
|
546
|
+
const handler = makeAmountWatcher(purse, helper);
|
|
423
547
|
|
|
424
|
-
const { helper } = this.facets;
|
|
425
548
|
// publish purse's balance and changes
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
);
|
|
436
|
-
void observeNotifier(E(purse).getCurrentAmountNotifier(), {
|
|
437
|
-
updateState(balance) {
|
|
438
|
-
helper.updateBalance(purse, balance);
|
|
439
|
-
},
|
|
440
|
-
fail(reason) {
|
|
441
|
-
console.error(address, `failed updateState observer`, reason);
|
|
442
|
-
},
|
|
443
|
-
});
|
|
549
|
+
const notifier = await E(purse).getCurrentAmountNotifier();
|
|
550
|
+
const startP = E(notifier).getUpdateSince(undefined);
|
|
551
|
+
// @ts-expect-error import watchPromise's type is unknown
|
|
552
|
+
watchPromise(startP, handler, notifier);
|
|
553
|
+
},
|
|
554
|
+
|
|
555
|
+
watchNextBalance(handler, notifier, updateCount) {
|
|
556
|
+
const nextP = E(notifier).getUpdateSince(updateCount);
|
|
557
|
+
// @ts-expect-error import watchPromise's type is unknown
|
|
558
|
+
watchPromise(nextP, handler, notifier);
|
|
444
559
|
},
|
|
445
560
|
|
|
446
561
|
/**
|
|
447
562
|
* Provide a purse given a NameHub of issuers and their
|
|
448
563
|
* brands.
|
|
449
564
|
*
|
|
450
|
-
* We
|
|
565
|
+
* We currently support only one NameHub, agoricNames, and
|
|
451
566
|
* hence one purse per brand. But we store an array of them
|
|
452
567
|
* to facilitate a transition to decentralized introductions.
|
|
453
568
|
*
|
|
454
569
|
* @param {Brand} brand
|
|
455
|
-
* @param {ERef<NameHub>} known - namehub with brand, issuer branches
|
|
570
|
+
* @param {ERef<import('@agoric/vats').NameHub>} known - namehub with brand, issuer branches
|
|
456
571
|
* @returns {Promise<Purse | undefined>} undefined if brand is not known
|
|
457
572
|
*/
|
|
458
573
|
async getPurseIfKnownBrand(brand, known) {
|
|
@@ -499,6 +614,199 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
499
614
|
void helper.watchPurse(purse);
|
|
500
615
|
return purse;
|
|
501
616
|
},
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* see https://github.com/Agoric/agoric-sdk/issues/8445 and
|
|
620
|
+
* https://github.com/Agoric/agoric-sdk/issues/8286. As originally
|
|
621
|
+
* released, the smartWallet didn't durably monitor the promises for the
|
|
622
|
+
* outcomes of offers, and would have dropped them on upgrade of Zoe or
|
|
623
|
+
* the smartWallet itself. Using watchedPromises, (see offerWatcher.js)
|
|
624
|
+
* we've addressed the problem for new offers. This function will
|
|
625
|
+
* backfill the solution for offers that were outstanding before the
|
|
626
|
+
* transition to incarnation 2 of the smartWallet.
|
|
627
|
+
*/
|
|
628
|
+
async repairUnwatchedSeats() {
|
|
629
|
+
const { state, facets } = this;
|
|
630
|
+
const { address, invitationPurse, liveOffers, liveOfferSeats } =
|
|
631
|
+
state;
|
|
632
|
+
const { zoe, agoricNames, invitationBrand, invitationIssuer } =
|
|
633
|
+
shared;
|
|
634
|
+
|
|
635
|
+
const invitationFromSpec = makeInvitationsHelper(
|
|
636
|
+
zoe,
|
|
637
|
+
agoricNames,
|
|
638
|
+
invitationBrand,
|
|
639
|
+
invitationPurse,
|
|
640
|
+
state.offerToInvitationMakers.get,
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
const watcherPromises = [];
|
|
644
|
+
for (const seatId of liveOfferSeats.keys()) {
|
|
645
|
+
facets.helper.logWalletInfo(`repairing ${seatId}`);
|
|
646
|
+
const offerSpec = liveOffers.get(seatId);
|
|
647
|
+
const seat = liveOfferSeats.get(seatId);
|
|
648
|
+
|
|
649
|
+
const watchOutcome = (async () => {
|
|
650
|
+
await null;
|
|
651
|
+
let invitationAmount = state.offerToUsedInvitation.has(
|
|
652
|
+
// @ts-expect-error older type allowed number
|
|
653
|
+
offerSpec.id,
|
|
654
|
+
)
|
|
655
|
+
? state.offerToUsedInvitation.get(
|
|
656
|
+
// @ts-expect-error older type allowed number
|
|
657
|
+
offerSpec.id,
|
|
658
|
+
)
|
|
659
|
+
: undefined;
|
|
660
|
+
if (invitationAmount) {
|
|
661
|
+
facets.helper.logWalletInfo(
|
|
662
|
+
'recovered invitation amount for offer',
|
|
663
|
+
offerSpec.id,
|
|
664
|
+
);
|
|
665
|
+
} else {
|
|
666
|
+
facets.helper.logWalletInfo(
|
|
667
|
+
'inferring invitation amount for offer',
|
|
668
|
+
offerSpec.id,
|
|
669
|
+
);
|
|
670
|
+
const tempInvitation = invitationFromSpec(
|
|
671
|
+
offerSpec.invitationSpec,
|
|
672
|
+
);
|
|
673
|
+
invitationAmount = await E(invitationIssuer).getAmountOf(
|
|
674
|
+
tempInvitation,
|
|
675
|
+
);
|
|
676
|
+
void E(invitationIssuer).burn(tempInvitation);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const watcher = makeOfferWatcher(
|
|
680
|
+
facets.helper,
|
|
681
|
+
facets.deposit,
|
|
682
|
+
offerSpec,
|
|
683
|
+
address,
|
|
684
|
+
invitationAmount,
|
|
685
|
+
seat,
|
|
686
|
+
);
|
|
687
|
+
return watchOfferOutcomes(watcher, seat);
|
|
688
|
+
})();
|
|
689
|
+
trace(`Repaired seat ${seatId} for wallet ${address}`);
|
|
690
|
+
watcherPromises.push(watchOutcome);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
await Promise.all(watcherPromises);
|
|
694
|
+
},
|
|
695
|
+
async repairUnwatchedPurses() {
|
|
696
|
+
const { state, facets } = this;
|
|
697
|
+
const { helper, self } = facets;
|
|
698
|
+
const { invitationPurse, address } = state;
|
|
699
|
+
|
|
700
|
+
const brandToPurses = getBrandToPurses(walletPurses, self);
|
|
701
|
+
trace(`Found ${brandToPurses.values()} purse(s) for ${address}`);
|
|
702
|
+
for (const purses of brandToPurses.values()) {
|
|
703
|
+
for (const record of purses) {
|
|
704
|
+
void helper.watchPurse(record.purse);
|
|
705
|
+
trace(`Repaired purse ${record.petname} of ${address}`);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
void helper.watchPurse(invitationPurse);
|
|
710
|
+
},
|
|
711
|
+
|
|
712
|
+
/** @param {import('./offers.js').OfferStatus} offerStatus */
|
|
713
|
+
updateStatus(offerStatus) {
|
|
714
|
+
const { state, facets } = this;
|
|
715
|
+
facets.helper.logWalletInfo('offerStatus', offerStatus);
|
|
716
|
+
|
|
717
|
+
void state.updateRecorderKit.recorder.write({
|
|
718
|
+
updated: 'offerStatus',
|
|
719
|
+
status: offerStatus,
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
if ('numWantsSatisfied' in offerStatus) {
|
|
723
|
+
if (state.liveOfferSeats.has(offerStatus.id)) {
|
|
724
|
+
state.liveOfferSeats.delete(offerStatus.id);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (facets.helper.getLiveOfferPayments().has(offerStatus.id)) {
|
|
728
|
+
facets.helper.getLiveOfferPayments().delete(offerStatus.id);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
if (state.liveOffers.has(offerStatus.id)) {
|
|
732
|
+
state.liveOffers.delete(offerStatus.id);
|
|
733
|
+
// This might get skipped in subsequent passes, since we .delete()
|
|
734
|
+
// the first time through
|
|
735
|
+
facets.helper.publishCurrentState();
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
},
|
|
739
|
+
async addContinuingOffer(
|
|
740
|
+
offerId,
|
|
741
|
+
invitationAmount,
|
|
742
|
+
invitationMakers,
|
|
743
|
+
publicSubscribers,
|
|
744
|
+
) {
|
|
745
|
+
const { state, facets } = this;
|
|
746
|
+
|
|
747
|
+
state.offerToUsedInvitation.init(offerId, invitationAmount);
|
|
748
|
+
state.offerToInvitationMakers.init(offerId, invitationMakers);
|
|
749
|
+
const pathMap = await objectMapStoragePath(publicSubscribers);
|
|
750
|
+
if (pathMap) {
|
|
751
|
+
facets.helper.logWalletInfo('recording pathMap', pathMap);
|
|
752
|
+
state.offerToPublicSubscriberPaths.init(offerId, pathMap);
|
|
753
|
+
}
|
|
754
|
+
facets.helper.publishCurrentState();
|
|
755
|
+
},
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* @param {Brand} brand
|
|
759
|
+
* @returns {Promise<Purse>}
|
|
760
|
+
*/
|
|
761
|
+
async purseForBrand(brand) {
|
|
762
|
+
const { state, facets } = this;
|
|
763
|
+
const { registry, invitationBrand } = shared;
|
|
764
|
+
|
|
765
|
+
if (registry.has(brand)) {
|
|
766
|
+
// @ts-expect-error virtual purse
|
|
767
|
+
return E(state.bank).getPurse(brand);
|
|
768
|
+
} else if (invitationBrand === brand) {
|
|
769
|
+
return state.invitationPurse;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const purse = await facets.helper.getPurseIfKnownBrand(
|
|
773
|
+
brand,
|
|
774
|
+
shared.agoricNames,
|
|
775
|
+
);
|
|
776
|
+
if (purse) {
|
|
777
|
+
return purse;
|
|
778
|
+
}
|
|
779
|
+
throw Fail`cannot find/make purse for ${brand}`;
|
|
780
|
+
},
|
|
781
|
+
logWalletInfo(...args) {
|
|
782
|
+
const { state } = this;
|
|
783
|
+
console.info('wallet', state.address, ...args);
|
|
784
|
+
},
|
|
785
|
+
logWalletError(...args) {
|
|
786
|
+
const { state } = this;
|
|
787
|
+
console.error('wallet', state.address, ...args);
|
|
788
|
+
},
|
|
789
|
+
// In new SmartWallets, this is part of state, but we can't add fields
|
|
790
|
+
// to instance state for older SmartWallets, so put it in baggage.
|
|
791
|
+
getLiveOfferPayments() {
|
|
792
|
+
const { state } = this;
|
|
793
|
+
|
|
794
|
+
if (state.liveOfferPayments) {
|
|
795
|
+
return state.liveOfferPayments;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// This will only happen for legacy wallets, before WF incarnation 2
|
|
799
|
+
if (!baggage.has(state.address)) {
|
|
800
|
+
trace(`getLiveOfferPayments adding store for ${state.address}`);
|
|
801
|
+
baggage.init(
|
|
802
|
+
state.address,
|
|
803
|
+
makeScalarBigMapStore('live offer payments', {
|
|
804
|
+
durable: true,
|
|
805
|
+
}),
|
|
806
|
+
);
|
|
807
|
+
}
|
|
808
|
+
return baggage.get(state.address);
|
|
809
|
+
},
|
|
502
810
|
},
|
|
503
811
|
/**
|
|
504
812
|
* Similar to {DepositFacet} but async because it has to look up the purse.
|
|
@@ -514,9 +822,13 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
514
822
|
* @throws if there's not yet a purse, though the payment is held to try again when there is
|
|
515
823
|
*/
|
|
516
824
|
async receive(payment) {
|
|
517
|
-
const {
|
|
518
|
-
|
|
825
|
+
const {
|
|
826
|
+
state,
|
|
827
|
+
facets: { helper },
|
|
828
|
+
} = this;
|
|
829
|
+
const { paymentQueues: queues, bank, invitationPurse } = state;
|
|
519
830
|
const { registry, invitationBrand } = shared;
|
|
831
|
+
|
|
520
832
|
const brand = await E(payment).getAllegedBrand();
|
|
521
833
|
|
|
522
834
|
// When there is a purse deposit into it
|
|
@@ -542,119 +854,195 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
542
854
|
throw Fail`cannot deposit payment with brand ${brand}: no purse`;
|
|
543
855
|
},
|
|
544
856
|
},
|
|
857
|
+
|
|
858
|
+
payments: {
|
|
859
|
+
/**
|
|
860
|
+
* Withdraw the offered amount from the appropriate purse of this wallet.
|
|
861
|
+
*
|
|
862
|
+
* Save its amount in liveOfferPayments in case we need to reclaim the payment.
|
|
863
|
+
*
|
|
864
|
+
* @param {AmountKeywordRecord} give
|
|
865
|
+
* @param {OfferId} offerId
|
|
866
|
+
* @returns {PaymentPKeywordRecord}
|
|
867
|
+
*/
|
|
868
|
+
withdrawGive(give, offerId) {
|
|
869
|
+
const { facets } = this;
|
|
870
|
+
|
|
871
|
+
/** @type {MapStore<Brand, Payment>} */
|
|
872
|
+
const brandPaymentRecord = makeScalarBigMapStore('paymentToBrand', {
|
|
873
|
+
durable: true,
|
|
874
|
+
});
|
|
875
|
+
facets.helper
|
|
876
|
+
.getLiveOfferPayments()
|
|
877
|
+
.init(offerId, brandPaymentRecord);
|
|
878
|
+
|
|
879
|
+
// Add each payment amount to brandPaymentRecord as it is withdrawn. If
|
|
880
|
+
// there's an error later, we can use it to redeposit the correct amount.
|
|
881
|
+
return objectMap(give, amount => {
|
|
882
|
+
/** @type {Promise<Purse>} */
|
|
883
|
+
const purseP = facets.helper.purseForBrand(amount.brand);
|
|
884
|
+
const paymentP = E(purseP).withdraw(amount);
|
|
885
|
+
void E.when(
|
|
886
|
+
paymentP,
|
|
887
|
+
payment => brandPaymentRecord.init(amount.brand, payment),
|
|
888
|
+
e => {
|
|
889
|
+
// recovery will be handled by tryReclaimingWithdrawnPayments()
|
|
890
|
+
facets.helper.logWalletInfo(
|
|
891
|
+
`⚠️ Payment withdrawal failed.`,
|
|
892
|
+
offerId,
|
|
893
|
+
e,
|
|
894
|
+
);
|
|
895
|
+
},
|
|
896
|
+
);
|
|
897
|
+
return paymentP;
|
|
898
|
+
});
|
|
899
|
+
},
|
|
900
|
+
|
|
901
|
+
/**
|
|
902
|
+
* Find the live payments for the offer and deposit them back in the appropriate purses.
|
|
903
|
+
*
|
|
904
|
+
* @param {OfferId} offerId
|
|
905
|
+
* @returns {Promise<void>}
|
|
906
|
+
*/
|
|
907
|
+
async tryReclaimingWithdrawnPayments(offerId) {
|
|
908
|
+
const { facets } = this;
|
|
909
|
+
|
|
910
|
+
await null;
|
|
911
|
+
|
|
912
|
+
const liveOfferPayments = facets.helper.getLiveOfferPayments();
|
|
913
|
+
if (liveOfferPayments.has(offerId)) {
|
|
914
|
+
const brandPaymentRecord = liveOfferPayments.get(offerId);
|
|
915
|
+
if (!brandPaymentRecord) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
// Use allSettled to ensure we attempt all the deposits, regardless of
|
|
919
|
+
// individual rejections.
|
|
920
|
+
await Promise.allSettled(
|
|
921
|
+
Array.from(brandPaymentRecord.entries()).map(([b, p]) => {
|
|
922
|
+
// Wait for the withdrawal to complete. This protects against a
|
|
923
|
+
// race when updating paymentToPurse.
|
|
924
|
+
const purseP = facets.helper.purseForBrand(b);
|
|
925
|
+
|
|
926
|
+
// Now send it back to the purse.
|
|
927
|
+
return E(purseP).deposit(p);
|
|
928
|
+
}),
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
|
|
545
934
|
offers: {
|
|
546
935
|
/**
|
|
547
936
|
* Take an offer description provided in capData, augment it with payments and call zoe.offer()
|
|
548
937
|
*
|
|
549
|
-
* @param {
|
|
938
|
+
* @param {OfferSpec} offerSpec
|
|
550
939
|
* @returns {Promise<void>} after the offer has been both seated and exited by Zoe.
|
|
551
940
|
* @throws if any parts of the offer can be determined synchronously to be invalid
|
|
552
941
|
*/
|
|
553
942
|
async executeOffer(offerSpec) {
|
|
554
943
|
const { facets, state } = this;
|
|
555
|
-
const {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
invitationPurse,
|
|
559
|
-
offerToInvitationMakers,
|
|
560
|
-
offerToUsedInvitation,
|
|
561
|
-
offerToPublicSubscriberPaths,
|
|
562
|
-
updateRecorderKit,
|
|
563
|
-
} = this.state;
|
|
564
|
-
const { invitationBrand, zoe, invitationIssuer, registry } = shared;
|
|
944
|
+
const { address, invitationPurse } = state;
|
|
945
|
+
const { zoe, agoricNames } = shared;
|
|
946
|
+
const { invitationBrand, invitationIssuer } = shared;
|
|
565
947
|
|
|
566
948
|
facets.helper.assertUniqueOfferId(String(offerSpec.id));
|
|
567
949
|
|
|
568
|
-
|
|
569
|
-
info: (...args) => console.info('wallet', address, ...args),
|
|
570
|
-
error: (...args) => console.error('wallet', address, ...args),
|
|
571
|
-
};
|
|
950
|
+
await null;
|
|
572
951
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
offerToInvitationMakers.get,
|
|
584
|
-
),
|
|
585
|
-
/**
|
|
586
|
-
* @param {Brand} brand
|
|
587
|
-
* @returns {Promise<RemotePurse>}
|
|
588
|
-
*/
|
|
589
|
-
purseForBrand: async brand => {
|
|
590
|
-
const { helper } = facets;
|
|
591
|
-
if (registry.has(brand)) {
|
|
592
|
-
// @ts-expect-error RemotePurse cast
|
|
593
|
-
return E(bank).getPurse(brand);
|
|
594
|
-
} else if (invitationBrand === brand) {
|
|
595
|
-
// @ts-expect-error RemotePurse cast
|
|
596
|
-
return invitationPurse;
|
|
597
|
-
}
|
|
952
|
+
let seatRef;
|
|
953
|
+
let watcher;
|
|
954
|
+
try {
|
|
955
|
+
const invitationFromSpec = makeInvitationsHelper(
|
|
956
|
+
zoe,
|
|
957
|
+
agoricNames,
|
|
958
|
+
invitationBrand,
|
|
959
|
+
invitationPurse,
|
|
960
|
+
state.offerToInvitationMakers.get,
|
|
961
|
+
);
|
|
598
962
|
|
|
599
|
-
|
|
600
|
-
brand,
|
|
601
|
-
shared.agoricNames,
|
|
602
|
-
);
|
|
603
|
-
if (purse) {
|
|
604
|
-
return purse;
|
|
605
|
-
}
|
|
606
|
-
throw Fail`cannot find/make purse for ${brand}`;
|
|
607
|
-
},
|
|
608
|
-
logger,
|
|
609
|
-
},
|
|
610
|
-
onStatusChange: offerStatus => {
|
|
611
|
-
logger.info('offerStatus', offerStatus);
|
|
963
|
+
facets.helper.logWalletInfo('starting executeOffer', offerSpec.id);
|
|
612
964
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
status: offerStatus,
|
|
616
|
-
});
|
|
965
|
+
// 1. Prepare values and validate synchronously.
|
|
966
|
+
const { proposal } = offerSpec;
|
|
617
967
|
|
|
618
|
-
|
|
619
|
-
if (isSeatExited) {
|
|
620
|
-
if (state.liveOfferSeats.has(offerStatus.id)) {
|
|
621
|
-
state.liveOfferSeats.delete(offerStatus.id);
|
|
622
|
-
}
|
|
968
|
+
const invitation = invitationFromSpec(offerSpec.invitationSpec);
|
|
623
969
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
970
|
+
const [paymentKeywordRecord, invitationAmount] = await Promise.all([
|
|
971
|
+
proposal?.give &&
|
|
972
|
+
deeplyFulfilledObject(
|
|
973
|
+
facets.payments.withdrawGive(proposal.give, offerSpec.id),
|
|
974
|
+
),
|
|
975
|
+
E(invitationIssuer).getAmountOf(invitation),
|
|
976
|
+
]);
|
|
977
|
+
|
|
978
|
+
// 2. Begin executing offer
|
|
979
|
+
// No explicit signal to user that we reached here but if anything above
|
|
980
|
+
// failed they'd get an 'error' status update.
|
|
981
|
+
|
|
982
|
+
/** @type {UserSeat} */
|
|
983
|
+
seatRef = await E(zoe).offer(
|
|
984
|
+
invitation,
|
|
985
|
+
proposal,
|
|
986
|
+
paymentKeywordRecord,
|
|
987
|
+
offerSpec.offerArgs,
|
|
988
|
+
);
|
|
989
|
+
facets.helper.logWalletInfo(offerSpec.id, 'seated');
|
|
990
|
+
|
|
991
|
+
watcher = makeOfferWatcher(
|
|
992
|
+
facets.helper,
|
|
993
|
+
facets.deposit,
|
|
994
|
+
offerSpec,
|
|
995
|
+
address,
|
|
633
996
|
invitationAmount,
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
) => {
|
|
637
|
-
offerToUsedInvitation.init(offerId, invitationAmount);
|
|
638
|
-
offerToInvitationMakers.init(offerId, invitationMakers);
|
|
639
|
-
const pathMap = await objectMapStoragePath(publicSubscribers);
|
|
640
|
-
if (pathMap) {
|
|
641
|
-
logger.info('recording pathMap', pathMap);
|
|
642
|
-
offerToPublicSubscriberPaths.init(offerId, pathMap);
|
|
643
|
-
}
|
|
644
|
-
facets.helper.publishCurrentState();
|
|
645
|
-
},
|
|
646
|
-
});
|
|
997
|
+
seatRef,
|
|
998
|
+
);
|
|
647
999
|
|
|
648
|
-
return executor.executeOffer(offerSpec, seatRef => {
|
|
649
1000
|
state.liveOffers.init(offerSpec.id, offerSpec);
|
|
650
|
-
facets.helper.publishCurrentState();
|
|
651
1001
|
state.liveOfferSeats.init(offerSpec.id, seatRef);
|
|
652
|
-
|
|
1002
|
+
|
|
1003
|
+
// publish the live offers
|
|
1004
|
+
facets.helper.publishCurrentState();
|
|
1005
|
+
|
|
1006
|
+
// await so that any errors are caught and handled below
|
|
1007
|
+
await watchOfferOutcomes(watcher, seatRef);
|
|
1008
|
+
} catch (err) {
|
|
1009
|
+
// This block only runs if the block above fails during one vat incarnation.
|
|
1010
|
+
facets.helper.logWalletError('IMMEDIATE OFFER ERROR:', err);
|
|
1011
|
+
|
|
1012
|
+
// Update status to observers
|
|
1013
|
+
if (err.upgradeMessage === 'vat upgraded') {
|
|
1014
|
+
// The offer watchers will reconnect. Don't reclaim or exit
|
|
1015
|
+
return;
|
|
1016
|
+
} else if (watcher) {
|
|
1017
|
+
// The watcher's onRejected will updateStatus()
|
|
1018
|
+
} else {
|
|
1019
|
+
facets.helper.updateStatus({
|
|
1020
|
+
error: err.toString(),
|
|
1021
|
+
...offerSpec,
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// Backstop recovery, in case something very basic fails.
|
|
1026
|
+
if (offerSpec?.proposal?.give) {
|
|
1027
|
+
facets.payments
|
|
1028
|
+
.tryReclaimingWithdrawnPayments(offerSpec.id)
|
|
1029
|
+
.catch(e =>
|
|
1030
|
+
facets.helper.logWalletError(
|
|
1031
|
+
'recovery failed reclaiming payments',
|
|
1032
|
+
e,
|
|
1033
|
+
),
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// XXX tests rely on throwing immediate errors, not covering the
|
|
1038
|
+
// error handling in the event the failure is after an upgrade
|
|
1039
|
+
throw err;
|
|
1040
|
+
}
|
|
653
1041
|
},
|
|
654
1042
|
/**
|
|
655
1043
|
* Take an offer's id, look up its seat, try to exit.
|
|
656
1044
|
*
|
|
657
|
-
* @param {
|
|
1045
|
+
* @param {OfferId} offerId
|
|
658
1046
|
* @returns {Promise<void>}
|
|
659
1047
|
* @throws if the seat can't be found or E(seatRef).tryExit() fails.
|
|
660
1048
|
*/
|
|
@@ -672,14 +1060,14 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
672
1060
|
* @returns {Promise<void>}
|
|
673
1061
|
*/
|
|
674
1062
|
handleBridgeAction(actionCapData, canSpend = false) {
|
|
1063
|
+
const { facets } = this;
|
|
1064
|
+
const { offers } = facets;
|
|
675
1065
|
const { publicMarshaller } = shared;
|
|
676
1066
|
|
|
677
|
-
const { offers } = this.facets;
|
|
678
|
-
|
|
679
1067
|
/** @param {Error} err */
|
|
680
1068
|
const recordError = err => {
|
|
681
|
-
const {
|
|
682
|
-
|
|
1069
|
+
const { updateRecorderKit } = this.state;
|
|
1070
|
+
facets.helper.logWalletError('handleBridgeAction error:', err);
|
|
683
1071
|
void updateRecorderKit.recorder.write({
|
|
684
1072
|
updated: 'walletAction',
|
|
685
1073
|
status: { error: err.message },
|
|
@@ -724,14 +1112,18 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
724
1112
|
},
|
|
725
1113
|
/** @deprecated use getPublicTopics */
|
|
726
1114
|
getCurrentSubscriber() {
|
|
727
|
-
|
|
1115
|
+
const { state } = this;
|
|
1116
|
+
return state.currentRecorderKit.subscriber;
|
|
728
1117
|
},
|
|
729
1118
|
/** @deprecated use getPublicTopics */
|
|
730
1119
|
getUpdatesSubscriber() {
|
|
731
|
-
|
|
1120
|
+
const { state } = this;
|
|
1121
|
+
return state.updateRecorderKit.subscriber;
|
|
732
1122
|
},
|
|
733
1123
|
getPublicTopics() {
|
|
734
|
-
const {
|
|
1124
|
+
const { state } = this;
|
|
1125
|
+
const { currentRecorderKit, updateRecorderKit } = state;
|
|
1126
|
+
|
|
735
1127
|
return harden({
|
|
736
1128
|
current: {
|
|
737
1129
|
description: 'Current state of wallet',
|
|
@@ -745,6 +1137,27 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
745
1137
|
},
|
|
746
1138
|
});
|
|
747
1139
|
},
|
|
1140
|
+
// TODO remove this and repairUnwatchedSeats once the repair has taken place.
|
|
1141
|
+
/**
|
|
1142
|
+
* To be called once ever per wallet.
|
|
1143
|
+
*
|
|
1144
|
+
* @param {object} key
|
|
1145
|
+
*/
|
|
1146
|
+
repairWalletForIncarnation2(key) {
|
|
1147
|
+
const { state, facets } = this;
|
|
1148
|
+
|
|
1149
|
+
if (key !== shared.secretWalletFactoryKey) {
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
facets.helper.repairUnwatchedSeats().catch(e => {
|
|
1154
|
+
console.error('repairUnwatchedSeats rejection', e);
|
|
1155
|
+
});
|
|
1156
|
+
facets.helper.repairUnwatchedPurses().catch(e => {
|
|
1157
|
+
console.error('repairUnwatchedPurses rejection', e);
|
|
1158
|
+
});
|
|
1159
|
+
trace(`repaired wallet ${state.address}`);
|
|
1160
|
+
},
|
|
748
1161
|
},
|
|
749
1162
|
},
|
|
750
1163
|
{
|
|
@@ -752,7 +1165,6 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
752
1165
|
const { invitationPurse } = state;
|
|
753
1166
|
const { helper } = facets;
|
|
754
1167
|
|
|
755
|
-
// @ts-expect-error RemotePurse cast
|
|
756
1168
|
void helper.watchPurse(invitationPurse);
|
|
757
1169
|
},
|
|
758
1170
|
},
|