@agoric/smart-wallet 0.5.4-other-dev-3eb1a1d.0 → 0.5.4-other-dev-d15096d.0.d15096d
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 +35 -30
- package/src/index.js +4 -0
- package/src/invitations.d.ts +6 -2
- package/src/invitations.d.ts.map +1 -1
- package/src/invitations.js +10 -3
- package/src/marshal-contexts.d.ts +2 -1
- package/src/marshal-contexts.d.ts.map +1 -1
- package/src/marshal-contexts.js +1 -0
- package/src/offerWatcher.d.ts +8 -4
- package/src/offerWatcher.d.ts.map +1 -1
- package/src/offerWatcher.js +23 -9
- package/src/offers.d.ts +59 -3
- package/src/offers.d.ts.map +1 -1
- package/src/offers.js +33 -2
- package/src/proposals/upgrade-wallet-factory2-proposal.js +3 -2
- package/src/proposals/upgrade-walletFactory-proposal.js +7 -2
- package/src/smartWallet.d.ts +63 -24
- package/src/smartWallet.d.ts.map +1 -1
- package/src/smartWallet.js +229 -51
- package/src/typeGuards.js +29 -1
- package/src/types.d.ts +3 -2
- package/src/types.d.ts.map +1 -1
- package/src/types.ts +4 -4
- package/src/utils.d.ts +18 -8
- package/src/utils.d.ts.map +1 -1
- package/src/utils.js +12 -8
- package/src/walletFactory.d.ts +28 -20
- package/src/walletFactory.d.ts.map +1 -1
- package/src/walletFactory.js +37 -31
- package/src/proposals/upgrade-wallet-factory2-proposal.d.ts +0 -23
- package/src/proposals/upgrade-wallet-factory2-proposal.d.ts.map +0 -1
- package/src/proposals/upgrade-walletFactory-proposal.d.ts +0 -17
- package/src/proposals/upgrade-walletFactory-proposal.d.ts.map +0 -1
package/src/smartWallet.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fail, q } from '@endo/errors';
|
|
2
|
-
import { E } from '@endo/far';
|
|
2
|
+
import { E, passStyleOf } from '@endo/far';
|
|
3
3
|
import {
|
|
4
4
|
AmountShape,
|
|
5
5
|
BrandShape,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
StorageNodeShape,
|
|
16
16
|
} from '@agoric/internal';
|
|
17
17
|
import { isUpgradeDisconnection } from '@agoric/internal/src/upgrade-api.js';
|
|
18
|
-
import { M, mustMatch } from '@agoric/store';
|
|
18
|
+
import { M, matches, mustMatch } from '@agoric/store';
|
|
19
19
|
import {
|
|
20
20
|
appendToStoredArray,
|
|
21
21
|
provideLazy,
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
import {
|
|
37
37
|
AmountKeywordRecordShape,
|
|
38
38
|
PaymentPKeywordRecordShape,
|
|
39
|
+
InvitationElementShape,
|
|
39
40
|
} from '@agoric/zoe/src/typeGuards.js';
|
|
40
41
|
import { prepareVowTools } from '@agoric/vow';
|
|
41
42
|
import { makeDurableZone } from '@agoric/zone/durable.js';
|
|
@@ -45,7 +46,25 @@ import { shape } from './typeGuards.js';
|
|
|
45
46
|
import { objectMapStoragePath } from './utils.js';
|
|
46
47
|
import { prepareOfferWatcher, makeWatchOfferOutcomes } from './offerWatcher.js';
|
|
47
48
|
|
|
48
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* @import {ERemote, Remote} from '@agoric/internal';
|
|
51
|
+
* @import {EMarshaller} from '@agoric/internal/src/marshal/wrap-marshaller.js';
|
|
52
|
+
* @import {Amount, Brand, Issuer, Payment, Purse} from '@agoric/ertp';
|
|
53
|
+
* @import {WeakMapStore, MapStore} from '@agoric/store'
|
|
54
|
+
* @import {InvitationAmount, InvitationDetails, PaymentPKeywordRecord, Proposal, UserSeat} from '@agoric/zoe';
|
|
55
|
+
* @import {CopyRecord} from '@endo/pass-style';
|
|
56
|
+
* @import {EReturn} from '@endo/far';
|
|
57
|
+
* @import {OfferId, OfferStatus, OfferSpec, InvokeEntryMessage, ResultPlan} from './offers.js';
|
|
58
|
+
* @import {MakeOfferWatcher} from './offerWatcher.js';
|
|
59
|
+
* @import {Petname} from './types.js';
|
|
60
|
+
* @import {Bank} from '@agoric/vats/src/vat-bank.js';
|
|
61
|
+
* @import {NameHub} from '@agoric/vats';
|
|
62
|
+
* @import {InvitationMakers} from './types.js';
|
|
63
|
+
* @import {RecorderKit} from '@agoric/zoe/src/contractSupport/recorder.js';
|
|
64
|
+
* @import {Baggage} from '@agoric/vat-data';
|
|
65
|
+
* @import {PublicSubscribers} from './types.js';
|
|
66
|
+
* @import {CapData} from '@endo/marshal';
|
|
67
|
+
*/
|
|
49
68
|
|
|
50
69
|
const trace = makeTracer('SmrtWlt');
|
|
51
70
|
|
|
@@ -54,24 +73,13 @@ const trace = makeTracer('SmrtWlt');
|
|
|
54
73
|
* @see {@link ../README.md} }
|
|
55
74
|
*/
|
|
56
75
|
|
|
57
|
-
/** @typedef {number | string} OfferId */
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @typedef {{
|
|
61
|
-
* id: OfferId;
|
|
62
|
-
* invitationSpec: import('./invitations').InvitationSpec;
|
|
63
|
-
* proposal: Proposal;
|
|
64
|
-
* offerArgs?: any;
|
|
65
|
-
* }} OfferSpec
|
|
66
|
-
*/
|
|
67
|
-
|
|
68
76
|
/**
|
|
69
77
|
* @typedef {{
|
|
70
78
|
* logger: {
|
|
71
79
|
* info: (...args: any[]) => void;
|
|
72
80
|
* error: (...args: any[]) => void;
|
|
73
81
|
* };
|
|
74
|
-
* makeOfferWatcher:
|
|
82
|
+
* makeOfferWatcher: MakeOfferWatcher;
|
|
75
83
|
* invitationFromSpec: ERef<Invitation>;
|
|
76
84
|
* }} ExecutorPowers
|
|
77
85
|
*/
|
|
@@ -90,10 +98,16 @@ const trace = makeTracer('SmrtWlt');
|
|
|
90
98
|
* }} TryExitOfferAction
|
|
91
99
|
*/
|
|
92
100
|
|
|
101
|
+
/**
|
|
102
|
+
* @typedef {object} InvokeStoreEntryAction
|
|
103
|
+
* @property {'invokeEntry'} method BridgeAction discriminator
|
|
104
|
+
* @property {InvokeEntryMessage} message object method call to make
|
|
105
|
+
*/
|
|
106
|
+
|
|
93
107
|
// Discriminated union. Possible future messages types:
|
|
94
108
|
// maybe suggestIssuer for https://github.com/Agoric/agoric-sdk/issues/6132
|
|
95
109
|
// setting petnames and adding brands for https://github.com/Agoric/agoric-sdk/issues/6126
|
|
96
|
-
/** @typedef {ExecuteOfferAction | TryExitOfferAction} BridgeAction */
|
|
110
|
+
/** @typedef {ExecuteOfferAction | TryExitOfferAction | InvokeStoreEntryAction} BridgeAction */
|
|
97
111
|
|
|
98
112
|
/**
|
|
99
113
|
* Purses is an array to support a future requirement of multiple purses per
|
|
@@ -132,7 +146,13 @@ const trace = makeTracer('SmrtWlt');
|
|
|
132
146
|
/**
|
|
133
147
|
* @typedef {{ updated: 'offerStatus'; status: OfferStatus }
|
|
134
148
|
* | { updated: 'balance'; currentAmount: Amount }
|
|
135
|
-
* | { updated: 'walletAction'; status: { error: string } }
|
|
149
|
+
* | { updated: 'walletAction'; status: { error: string } }
|
|
150
|
+
* | {
|
|
151
|
+
* updated: 'invocation';
|
|
152
|
+
* id: string | number;
|
|
153
|
+
* error?: string;
|
|
154
|
+
* result?: { name?: string; passStyle: string };
|
|
155
|
+
* }} UpdateRecord
|
|
136
156
|
* Record of an update to the state of this wallet.
|
|
137
157
|
*
|
|
138
158
|
* Client is responsible for coalescing updates into a current state. See
|
|
@@ -151,7 +171,7 @@ const trace = makeTracer('SmrtWlt');
|
|
|
151
171
|
* brand: Brand;
|
|
152
172
|
* displayInfo: DisplayInfo;
|
|
153
173
|
* issuer: Issuer;
|
|
154
|
-
* petname:
|
|
174
|
+
* petname: Petname;
|
|
155
175
|
* }} BrandDescriptor
|
|
156
176
|
* For use by clients to describe brands to users. Includes `displayInfo` to
|
|
157
177
|
* save a remote call.
|
|
@@ -160,10 +180,10 @@ const trace = makeTracer('SmrtWlt');
|
|
|
160
180
|
/**
|
|
161
181
|
* @typedef {{
|
|
162
182
|
* address: string;
|
|
163
|
-
* bank: ERef<
|
|
164
|
-
* currentStorageNode: StorageNode
|
|
183
|
+
* bank: ERef<Bank>;
|
|
184
|
+
* currentStorageNode: Remote<StorageNode>;
|
|
165
185
|
* invitationPurse: Purse<'set', InvitationDetails>;
|
|
166
|
-
* walletStorageNode: StorageNode
|
|
186
|
+
* walletStorageNode: Remote<StorageNode>;
|
|
167
187
|
* }} UniqueParams
|
|
168
188
|
*
|
|
169
189
|
*
|
|
@@ -171,12 +191,12 @@ const trace = makeTracer('SmrtWlt');
|
|
|
171
191
|
*
|
|
172
192
|
*
|
|
173
193
|
* @typedef {{
|
|
174
|
-
* agoricNames: ERef<
|
|
194
|
+
* agoricNames: ERef<NameHub>;
|
|
175
195
|
* registry: BrandDescriptorRegistry;
|
|
176
196
|
* invitationIssuer: Issuer<'set'>;
|
|
177
197
|
* invitationBrand: Brand<'set'>;
|
|
178
198
|
* invitationDisplayInfo: DisplayInfo;
|
|
179
|
-
* publicMarshaller:
|
|
199
|
+
* publicMarshaller: ERemote<EMarshaller>;
|
|
180
200
|
* zoe: ERef<ZoeService>;
|
|
181
201
|
* }} SharedParams
|
|
182
202
|
*
|
|
@@ -193,18 +213,16 @@ const trace = makeTracer('SmrtWlt');
|
|
|
193
213
|
* @typedef {Readonly<
|
|
194
214
|
* UniqueParams & {
|
|
195
215
|
* paymentQueues: MapStore<Brand, Payment[]>;
|
|
196
|
-
* offerToInvitationMakers: MapStore<
|
|
197
|
-
* string,
|
|
198
|
-
* import('./types.js').InvitationMakers
|
|
199
|
-
* >;
|
|
216
|
+
* offerToInvitationMakers: MapStore<string, InvitationMakers>;
|
|
200
217
|
* offerToPublicSubscriberPaths: MapStore<string, Record<string, string>>;
|
|
201
218
|
* offerToUsedInvitation: MapStore<string, Amount<'set'>>;
|
|
202
219
|
* purseBalances: MapStore<Purse, Amount>;
|
|
203
|
-
* updateRecorderKit:
|
|
204
|
-
* currentRecorderKit:
|
|
220
|
+
* updateRecorderKit: RecorderKit<UpdateRecord>;
|
|
221
|
+
* currentRecorderKit: RecorderKit<CurrentWalletRecord>;
|
|
205
222
|
* liveOffers: MapStore<OfferId, OfferStatus>;
|
|
206
223
|
* liveOfferSeats: MapStore<OfferId, UserSeat<unknown>>;
|
|
207
224
|
* liveOfferPayments: MapStore<OfferId, MapStore<Brand, Payment>>;
|
|
225
|
+
* myStore: MapStore;
|
|
208
226
|
* }
|
|
209
227
|
* >} ImmutableState
|
|
210
228
|
*
|
|
@@ -220,17 +238,17 @@ const trace = makeTracer('SmrtWlt');
|
|
|
220
238
|
* TODO: consider moving to nameHub.js?
|
|
221
239
|
*
|
|
222
240
|
* @param {unknown} target - passable Key
|
|
223
|
-
* @param {ERef<
|
|
241
|
+
* @param {ERef<NameHub>} nameHub
|
|
224
242
|
*/
|
|
225
243
|
const namesOf = async (target, nameHub) => {
|
|
226
244
|
const entries = await E(nameHub).entries();
|
|
227
|
-
const
|
|
245
|
+
const results = [];
|
|
228
246
|
for (const [name, candidate] of entries) {
|
|
229
247
|
if (candidate === target) {
|
|
230
|
-
|
|
248
|
+
results.push(name);
|
|
231
249
|
}
|
|
232
250
|
}
|
|
233
|
-
return harden(
|
|
251
|
+
return harden(results);
|
|
234
252
|
};
|
|
235
253
|
|
|
236
254
|
/**
|
|
@@ -264,7 +282,7 @@ const getBrandToPurses = (walletPurses, key) => {
|
|
|
264
282
|
};
|
|
265
283
|
|
|
266
284
|
/**
|
|
267
|
-
* @param {
|
|
285
|
+
* @param {Baggage} baggage
|
|
268
286
|
* @param {SharedParams} shared
|
|
269
287
|
*/
|
|
270
288
|
export const prepareSmartWallet = (baggage, shared) => {
|
|
@@ -315,7 +333,9 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
315
333
|
amountWatcherGuard,
|
|
316
334
|
/**
|
|
317
335
|
* @param {Purse} purse
|
|
318
|
-
* @param {ReturnType<
|
|
336
|
+
* @param {ReturnType<
|
|
337
|
+
* typeof makeWalletWithResolvedStorageNodes
|
|
338
|
+
* >['helper']} helper
|
|
319
339
|
*/
|
|
320
340
|
(purse, helper) => ({ purse, helper }),
|
|
321
341
|
{
|
|
@@ -350,6 +370,31 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
350
370
|
|
|
351
371
|
const makeAmountWatcher = prepareAmountWatcher();
|
|
352
372
|
|
|
373
|
+
/**
|
|
374
|
+
* @param {Amount} amount
|
|
375
|
+
* @returns {Amount}
|
|
376
|
+
*/
|
|
377
|
+
const cleanAmountForPublish = amount => {
|
|
378
|
+
mustMatch(amount, AmountShape);
|
|
379
|
+
if (
|
|
380
|
+
!matches(amount.brand, shared.invitationBrand) ||
|
|
381
|
+
!matches(amount.value, M.arrayOf(InvitationElementShape))
|
|
382
|
+
) {
|
|
383
|
+
return amount;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const invitationAmount = /** @type {InvitationAmount} */ (amount);
|
|
387
|
+
const filteredInvitationValue = invitationAmount.value.map(
|
|
388
|
+
({ handle: _, ...filteredInvitationDetails }) =>
|
|
389
|
+
filteredInvitationDetails,
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
return harden({
|
|
393
|
+
brand: shared.invitationBrand,
|
|
394
|
+
value: filteredInvitationValue,
|
|
395
|
+
});
|
|
396
|
+
};
|
|
397
|
+
|
|
353
398
|
/**
|
|
354
399
|
* @param {UniqueParams} unique
|
|
355
400
|
* @returns {State}
|
|
@@ -362,6 +407,7 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
362
407
|
address: M.string(),
|
|
363
408
|
bank: M.eref(M.remotable()),
|
|
364
409
|
invitationPurse: PurseShape,
|
|
410
|
+
// Should not be M.eref, makeRecordedKit assumes resolved
|
|
365
411
|
currentStorageNode: M.eref(StorageNodeShape),
|
|
366
412
|
walletStorageNode: M.eref(StorageNodeShape),
|
|
367
413
|
}),
|
|
@@ -394,14 +440,17 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
394
440
|
durable: true,
|
|
395
441
|
},
|
|
396
442
|
),
|
|
443
|
+
// NB: Wallets before this state property was added do not support
|
|
444
|
+
// saving results or invoking the saved items.
|
|
445
|
+
myStore: zone.detached().mapStore('my items'),
|
|
397
446
|
};
|
|
398
447
|
|
|
399
|
-
/** @type {
|
|
448
|
+
/** @type {RecorderKit<UpdateRecord>} */
|
|
400
449
|
const updateRecorderKit = makeRecorderKit(unique.walletStorageNode);
|
|
401
450
|
// NB: state size must not grow monotonically
|
|
402
451
|
// This is the node that UIs subscribe to for everything they need.
|
|
403
452
|
// e.g. agoric follow :published.wallet.agoric1nqxg4pye30n3trct0hf7dclcwfxz8au84hr3ht
|
|
404
|
-
/** @type {
|
|
453
|
+
/** @type {RecorderKit<CurrentWalletRecord>} */
|
|
405
454
|
const currentRecorderKit = makeRecorderKit(unique.currentStorageNode);
|
|
406
455
|
|
|
407
456
|
const nonpreciousState = {
|
|
@@ -426,6 +475,11 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
426
475
|
};
|
|
427
476
|
};
|
|
428
477
|
|
|
478
|
+
const invocationResultShape = M.splitRecord(
|
|
479
|
+
{},
|
|
480
|
+
{ id: M.or(M.string(), M.number()), saveResult: shape.ResultPlan },
|
|
481
|
+
);
|
|
482
|
+
|
|
429
483
|
const behaviorGuards = {
|
|
430
484
|
helper: M.interface('helperFacetI', {
|
|
431
485
|
assertUniqueOfferId: M.call(M.string()).returns(),
|
|
@@ -447,6 +501,8 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
447
501
|
logWalletInfo: M.call().rest(M.arrayOf(M.any())).returns(),
|
|
448
502
|
logWalletError: M.call().rest(M.arrayOf(M.any())).returns(),
|
|
449
503
|
getLiveOfferPayments: M.call().returns(M.remotable('mapStore')),
|
|
504
|
+
saveEntry: M.call(shape.ResultPlan, M.any()).returns(M.string()),
|
|
505
|
+
findUnusedName: M.call(M.string()).returns(M.string()),
|
|
450
506
|
}),
|
|
451
507
|
|
|
452
508
|
deposit: M.interface('depositFacetI', {
|
|
@@ -465,11 +521,19 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
465
521
|
executeOffer: M.call(shape.OfferSpec).returns(M.promise()),
|
|
466
522
|
tryExitOffer: M.call(M.scalar()).returns(M.promise()),
|
|
467
523
|
}),
|
|
524
|
+
invoke: M.interface('invoke', {
|
|
525
|
+
invokeEntry: M.callWhen(shape.InvokeEntryMessage).returns(),
|
|
526
|
+
}),
|
|
527
|
+
resultStepWatcher: M.interface('resultStepWatcher', {
|
|
528
|
+
onFulfilled: M.call(M.any(), invocationResultShape).returns(),
|
|
529
|
+
onRejected: M.call(M.any(), invocationResultShape).returns(),
|
|
530
|
+
}),
|
|
468
531
|
self: M.interface('selfFacetI', {
|
|
469
532
|
handleBridgeAction: M.call(shape.StringCapData, M.boolean()).returns(
|
|
470
533
|
M.promise(),
|
|
471
534
|
),
|
|
472
535
|
getDepositFacet: M.call().returns(M.remotable()),
|
|
536
|
+
getInvokeFacet: M.call().returns(M.remotable()),
|
|
473
537
|
getOffersFacet: M.call().returns(M.remotable()),
|
|
474
538
|
getCurrentSubscriber: M.call().returns(SubscriberShape),
|
|
475
539
|
getUpdatesSubscriber: M.call().returns(SubscriberShape),
|
|
@@ -529,7 +593,7 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
529
593
|
}
|
|
530
594
|
void updateRecorderKit.recorder.write({
|
|
531
595
|
updated: 'balance',
|
|
532
|
-
currentAmount: balance,
|
|
596
|
+
currentAmount: cleanAmountForPublish(balance),
|
|
533
597
|
});
|
|
534
598
|
const { helper } = this.facets;
|
|
535
599
|
helper.publishCurrentState();
|
|
@@ -546,9 +610,11 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
546
610
|
void currentRecorderKit.recorder.write({
|
|
547
611
|
purses: [...purseBalances.values()].map(a => ({
|
|
548
612
|
brand: a.brand,
|
|
549
|
-
balance: a,
|
|
613
|
+
balance: cleanAmountForPublish(a),
|
|
550
614
|
})),
|
|
551
|
-
offerToUsedInvitation: [...offerToUsedInvitation.entries()]
|
|
615
|
+
offerToUsedInvitation: [...offerToUsedInvitation.entries()].map(
|
|
616
|
+
([offerId, a]) => [offerId, cleanAmountForPublish(a)],
|
|
617
|
+
),
|
|
552
618
|
offerToPublicSubscriberPaths: [
|
|
553
619
|
...offerToPublicSubscriberPaths.entries(),
|
|
554
620
|
],
|
|
@@ -586,8 +652,7 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
586
652
|
* transition to decentralized introductions.
|
|
587
653
|
*
|
|
588
654
|
* @param {Brand} brand
|
|
589
|
-
* @param {ERef<
|
|
590
|
-
* brand, issuer branches
|
|
655
|
+
* @param {ERef<NameHub>} known - namehub with brand, issuer branches
|
|
591
656
|
* @returns {Promise<Purse | undefined>} undefined if brand is not known
|
|
592
657
|
*/
|
|
593
658
|
async getPurseIfKnownBrand(brand, known) {
|
|
@@ -666,8 +731,8 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
666
731
|
/**
|
|
667
732
|
* @param {string} offerId
|
|
668
733
|
* @param {Amount<'set'>} invitationAmount
|
|
669
|
-
* @param {
|
|
670
|
-
* @param {
|
|
734
|
+
* @param {InvitationMakers} invitationMakers
|
|
735
|
+
* @param {PublicSubscribers} publicSubscribers
|
|
671
736
|
*/
|
|
672
737
|
async addContinuingOffer(
|
|
673
738
|
offerId,
|
|
@@ -739,6 +804,35 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
739
804
|
}
|
|
740
805
|
return baggage.get(state.address);
|
|
741
806
|
},
|
|
807
|
+
/** @param {string} suggestion */
|
|
808
|
+
findUnusedName(suggestion) {
|
|
809
|
+
const { myStore } = this.state;
|
|
810
|
+
let nonce = 0;
|
|
811
|
+
let name = suggestion;
|
|
812
|
+
while (myStore.has(name)) {
|
|
813
|
+
nonce += myStore.getSize(); // avoid linear work
|
|
814
|
+
name = `${suggestion}.${nonce}`;
|
|
815
|
+
}
|
|
816
|
+
return name;
|
|
817
|
+
},
|
|
818
|
+
/**
|
|
819
|
+
* @param {ResultPlan} plan
|
|
820
|
+
* @param {unknown} value
|
|
821
|
+
*/
|
|
822
|
+
saveEntry(plan, value) {
|
|
823
|
+
const { myStore } = this.state;
|
|
824
|
+
const name = plan.overwrite
|
|
825
|
+
? plan.name
|
|
826
|
+
: this.facets.helper.findUnusedName(plan.name);
|
|
827
|
+
|
|
828
|
+
if (myStore.has(name)) {
|
|
829
|
+
myStore.set(name, value);
|
|
830
|
+
} else {
|
|
831
|
+
myStore.init(name, value);
|
|
832
|
+
}
|
|
833
|
+
trace('set', name, '=', value);
|
|
834
|
+
return name;
|
|
835
|
+
},
|
|
742
836
|
},
|
|
743
837
|
/**
|
|
744
838
|
* Similar to {DepositFacet} but async because it has to look up the
|
|
@@ -1013,19 +1107,90 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
1013
1107
|
await E(seatRef).tryExit();
|
|
1014
1108
|
},
|
|
1015
1109
|
},
|
|
1110
|
+
|
|
1111
|
+
invoke: {
|
|
1112
|
+
/**
|
|
1113
|
+
* @param {InvokeEntryMessage} message
|
|
1114
|
+
*/
|
|
1115
|
+
async invokeEntry(message) {
|
|
1116
|
+
trace('invokeEntry', message);
|
|
1117
|
+
const { myStore } = this.state;
|
|
1118
|
+
const { resultStepWatcher } = this.facets;
|
|
1119
|
+
|
|
1120
|
+
const { targetName: name, method, args, saveResult, id } = message;
|
|
1121
|
+
myStore.has(name) || Fail`cannot invoke ${q(name)}: no such item`;
|
|
1122
|
+
const value = myStore.get(name);
|
|
1123
|
+
trace('entry', name, value);
|
|
1124
|
+
trace('invoke', value, '.', method, '(', args, ')');
|
|
1125
|
+
if (id) {
|
|
1126
|
+
const { updateRecorderKit } = this.state;
|
|
1127
|
+
void updateRecorderKit.recorder.write({
|
|
1128
|
+
updated: 'invocation',
|
|
1129
|
+
id,
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
const callP = E(value)[method](...args);
|
|
1133
|
+
if (id || saveResult) {
|
|
1134
|
+
vowTools.watch(callP, resultStepWatcher, { id, saveResult });
|
|
1135
|
+
} else {
|
|
1136
|
+
void callP;
|
|
1137
|
+
}
|
|
1138
|
+
},
|
|
1139
|
+
},
|
|
1140
|
+
|
|
1141
|
+
resultStepWatcher: {
|
|
1142
|
+
/**
|
|
1143
|
+
* @param {unknown} result
|
|
1144
|
+
* @param {{ id?: string | number; saveResult?: ResultPlan }} opts
|
|
1145
|
+
*/
|
|
1146
|
+
onFulfilled(result, opts) {
|
|
1147
|
+
trace('resultStepWatcher opts', opts);
|
|
1148
|
+
const { id, saveResult } = opts;
|
|
1149
|
+
if (saveResult) {
|
|
1150
|
+
this.facets.helper.saveEntry(saveResult, result);
|
|
1151
|
+
}
|
|
1152
|
+
const passStyle = passStyleOf(result);
|
|
1153
|
+
const { updateRecorderKit } = this.state;
|
|
1154
|
+
if (id) {
|
|
1155
|
+
void updateRecorderKit.recorder.write({
|
|
1156
|
+
updated: 'invocation',
|
|
1157
|
+
id,
|
|
1158
|
+
result: {
|
|
1159
|
+
...(saveResult?.name ? { name: saveResult.name } : {}),
|
|
1160
|
+
passStyle,
|
|
1161
|
+
},
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
1165
|
+
/**
|
|
1166
|
+
* @param {unknown} reason
|
|
1167
|
+
* @param {{ id: string | number; saveResult?: ResultPlan }} opts
|
|
1168
|
+
*/
|
|
1169
|
+
onRejected(reason, opts) {
|
|
1170
|
+
trace('rejected', reason, opts);
|
|
1171
|
+
if (opts.id) {
|
|
1172
|
+
const { updateRecorderKit } = this.state;
|
|
1173
|
+
void updateRecorderKit.recorder.write({
|
|
1174
|
+
updated: 'invocation',
|
|
1175
|
+
id: opts.id,
|
|
1176
|
+
error: String(reason),
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
},
|
|
1181
|
+
|
|
1016
1182
|
self: {
|
|
1017
1183
|
/**
|
|
1018
1184
|
* Umarshals the actionCapData and delegates to the appropriate action
|
|
1019
1185
|
* handler.
|
|
1020
1186
|
*
|
|
1021
|
-
* @param {
|
|
1022
|
-
* of type BridgeAction
|
|
1187
|
+
* @param {CapData<string | null>} actionCapData of type BridgeAction
|
|
1023
1188
|
* @param {boolean} [canSpend]
|
|
1024
1189
|
* @returns {Promise<void>}
|
|
1025
1190
|
*/
|
|
1026
1191
|
handleBridgeAction(actionCapData, canSpend = false) {
|
|
1027
|
-
const { facets } = this;
|
|
1028
|
-
const { offers } = facets;
|
|
1192
|
+
const { facets, state } = this;
|
|
1193
|
+
const { offers, invoke } = facets;
|
|
1029
1194
|
const { publicMarshaller } = shared;
|
|
1030
1195
|
|
|
1031
1196
|
/** @param {Error} err */
|
|
@@ -1038,6 +1203,8 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
1038
1203
|
});
|
|
1039
1204
|
};
|
|
1040
1205
|
|
|
1206
|
+
const walletHasNameHub = 'myStore' in state && state.myStore != null;
|
|
1207
|
+
|
|
1041
1208
|
// use E.when to retain distributed stack trace
|
|
1042
1209
|
return E.when(
|
|
1043
1210
|
E(publicMarshaller).fromCapData(actionCapData),
|
|
@@ -1047,12 +1214,21 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
1047
1214
|
switch (action.method) {
|
|
1048
1215
|
case 'executeOffer': {
|
|
1049
1216
|
canSpend || Fail`executeOffer requires spend authority`;
|
|
1217
|
+
if (action.offer.saveResult != null && !walletHasNameHub) {
|
|
1218
|
+
Fail`executeOffer saveResult requires a new smart wallet with myStore`;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1050
1221
|
return offers.executeOffer(action.offer);
|
|
1051
1222
|
}
|
|
1052
1223
|
case 'tryExitOffer': {
|
|
1053
1224
|
assert(canSpend, 'tryExitOffer requires spend authority');
|
|
1054
1225
|
return offers.tryExitOffer(action.offerId);
|
|
1055
1226
|
}
|
|
1227
|
+
case 'invokeEntry': {
|
|
1228
|
+
walletHasNameHub ||
|
|
1229
|
+
Fail`invokeEntry requires a new smart wallet with myStore`;
|
|
1230
|
+
return invoke.invokeEntry(action.message);
|
|
1231
|
+
}
|
|
1056
1232
|
default: {
|
|
1057
1233
|
throw Fail`invalid handle bridge action ${q(action)}`;
|
|
1058
1234
|
}
|
|
@@ -1071,6 +1247,9 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
1071
1247
|
getDepositFacet() {
|
|
1072
1248
|
return this.facets.deposit;
|
|
1073
1249
|
},
|
|
1250
|
+
getInvokeFacet() {
|
|
1251
|
+
return this.facets.invoke;
|
|
1252
|
+
},
|
|
1074
1253
|
getOffersFacet() {
|
|
1075
1254
|
return this.facets.offers;
|
|
1076
1255
|
},
|
|
@@ -1118,7 +1297,7 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
1118
1297
|
* UniqueParams,
|
|
1119
1298
|
* 'currentStorageNode' | 'walletStorageNode'
|
|
1120
1299
|
* > & {
|
|
1121
|
-
* walletStorageNode:
|
|
1300
|
+
* walletStorageNode: ERemote<StorageNode>;
|
|
1122
1301
|
* }} uniqueWithoutChildNodes
|
|
1123
1302
|
*/
|
|
1124
1303
|
const makeSmartWallet = async uniqueWithoutChildNodes => {
|
|
@@ -1138,5 +1317,4 @@ export const prepareSmartWallet = (baggage, shared) => {
|
|
|
1138
1317
|
return makeSmartWallet;
|
|
1139
1318
|
};
|
|
1140
1319
|
harden(prepareSmartWallet);
|
|
1141
|
-
|
|
1142
|
-
/** @typedef {Awaited<ReturnType<ReturnType<typeof prepareSmartWallet>>>} SmartWallet */
|
|
1320
|
+
/** @typedef {EReturn<EReturn<typeof prepareSmartWallet>>} SmartWallet */
|
package/src/typeGuards.js
CHANGED
|
@@ -4,6 +4,28 @@ import {
|
|
|
4
4
|
ProposalShape,
|
|
5
5
|
} from '@agoric/zoe/src/typeGuards.js';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* @import {TypedPattern} from '@agoric/internal';
|
|
9
|
+
* @import {InvokeEntryMessage, ResultPlan} from './offers.js';
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** @type {TypedPattern<ResultPlan>} */
|
|
13
|
+
const ResultPlanShape = M.splitRecord(
|
|
14
|
+
{ name: M.string() },
|
|
15
|
+
{ overwrite: M.boolean() },
|
|
16
|
+
{},
|
|
17
|
+
);
|
|
18
|
+
/** @type {TypedPattern<InvokeEntryMessage>} */
|
|
19
|
+
const InvokeEntryMessageShape = M.splitRecord(
|
|
20
|
+
{
|
|
21
|
+
targetName: M.string(),
|
|
22
|
+
method: M.string(),
|
|
23
|
+
args: M.array(),
|
|
24
|
+
},
|
|
25
|
+
{ saveResult: ResultPlanShape, id: M.or(M.number(), M.string()) },
|
|
26
|
+
{},
|
|
27
|
+
);
|
|
28
|
+
|
|
7
29
|
export const shape = {
|
|
8
30
|
// smartWallet
|
|
9
31
|
StringCapData: {
|
|
@@ -51,8 +73,14 @@ export const shape = {
|
|
|
51
73
|
invitationSpec: M.any(),
|
|
52
74
|
proposal: ProposalShape,
|
|
53
75
|
},
|
|
54
|
-
{
|
|
76
|
+
{
|
|
77
|
+
offerArgs: M.any(),
|
|
78
|
+
saveResult: ResultPlanShape,
|
|
79
|
+
},
|
|
80
|
+
{},
|
|
55
81
|
),
|
|
82
|
+
ResultPlan: ResultPlanShape,
|
|
83
|
+
InvokeEntryMessage: InvokeEntryMessageShape,
|
|
56
84
|
|
|
57
85
|
// walletFactory
|
|
58
86
|
/**
|
package/src/types.d.ts
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
* Similar to types.js but in TypeScript syntax because some types here need it.
|
|
5
5
|
* Downside is it can't reference any ambient types, which most of agoric-sdk type are presently.
|
|
6
6
|
*/
|
|
7
|
+
import type { Payment } from '@agoric/ertp';
|
|
7
8
|
import type { AgoricNamesRemotes } from '@agoric/vats/tools/board-utils.js';
|
|
9
|
+
import type { InvitationDetails } from '@agoric/zoe';
|
|
8
10
|
import type { PublicTopic } from '@agoric/zoe/src/contractSupport/topics.js';
|
|
9
|
-
import type { Payment } from '@agoric/ertp';
|
|
10
11
|
import type { OfferSpec } from './offers.js';
|
|
11
|
-
type Invitation = Payment<'set'>;
|
|
12
|
+
type Invitation = Payment<'set', InvitationDetails>;
|
|
12
13
|
/**
|
|
13
14
|
* A petname can either be a plain string or a path for which the first element
|
|
14
15
|
* is a petname for the origin, and the rest of the elements are a snapshot of
|
package/src/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAI7C,KAAK,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;AAExC,MAAM,MAAM,gBAAgB,GAAG,MAAM,CACnC,MAAM,EACN,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CACxC,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;AAErE,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,iBAAiB,EAAE,iBAAiB,CAAC;CACtC;AAED,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI;IACpB,GAAG,EAAE,MAAM,CAAC,CAAC;IACb,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC;CACnB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,eAAe,CAAC;IACtB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,qBAAqB,CAAC;IAC5B,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,CACvB,WAAW,EAAE,kBAAkB,EAC/B,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,SAAS,CAAC"}
|
package/src/types.ts
CHANGED
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* Downside is it can't reference any ambient types, which most of agoric-sdk type are presently.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- fails to notice the @see uses
|
|
8
9
|
import type { agoric } from '@agoric/cosmic-proto/agoric/bundle.js';
|
|
10
|
+
import type { Payment } from '@agoric/ertp';
|
|
9
11
|
import type { AgoricNamesRemotes } from '@agoric/vats/tools/board-utils.js';
|
|
12
|
+
import type { InvitationDetails } from '@agoric/zoe';
|
|
10
13
|
import type { PublicTopic } from '@agoric/zoe/src/contractSupport/topics.js';
|
|
11
|
-
import type { Payment } from '@agoric/ertp';
|
|
12
14
|
import type { OfferSpec } from './offers.js';
|
|
13
15
|
|
|
14
|
-
declare const CapDataShape: unique symbol;
|
|
15
|
-
|
|
16
16
|
// Match the type in Zoe, which can't be imported because it's ambient.
|
|
17
17
|
// This omits the parameters that aren't used in this module.
|
|
18
|
-
type Invitation = Payment<'set'>;
|
|
18
|
+
type Invitation = Payment<'set', InvitationDetails>;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* A petname can either be a plain string or a path for which the first element
|
package/src/utils.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* @import {OfferId, OfferStatus} from './offers.js';
|
|
3
|
+
* @import {UpdateRecord} from './smartWallet.js';
|
|
4
|
+
* @import {Follower} from '@agoric/casting';
|
|
5
|
+
* @import {PublicSubscribers} from './types.js';
|
|
6
|
+
* @import {TopicsRecord} from '@agoric/zoe/src/contractSupport/index.js';
|
|
7
|
+
*/
|
|
2
8
|
export const NO_SMART_WALLET_ERROR: "no smart wallet";
|
|
3
|
-
export function makeWalletStateCoalescer(invitationBrand?:
|
|
9
|
+
export function makeWalletStateCoalescer(invitationBrand?: Brand<"set">): {
|
|
4
10
|
state: {
|
|
5
|
-
invitationsReceived: Map<
|
|
11
|
+
invitationsReceived: Map<string, {
|
|
6
12
|
acceptedIn: OfferId;
|
|
7
13
|
description: string;
|
|
8
14
|
instance: Instance;
|
|
@@ -10,10 +16,10 @@ export function makeWalletStateCoalescer(invitationBrand?: globalThis.Brand<"set
|
|
|
10
16
|
offerStatuses: Map<OfferId, OfferStatus>;
|
|
11
17
|
balances: Map<globalThis.Brand, globalThis.Amount>;
|
|
12
18
|
};
|
|
13
|
-
update: (updateRecord:
|
|
19
|
+
update: (updateRecord: UpdateRecord | {}) => void;
|
|
14
20
|
};
|
|
15
|
-
export function coalesceUpdates(updates: ERef<Subscriber<
|
|
16
|
-
invitationsReceived: Map<
|
|
21
|
+
export function coalesceUpdates(updates: ERef<Subscriber<UpdateRecord>>, invitationBrand?: Brand<"set">): {
|
|
22
|
+
invitationsReceived: Map<string, {
|
|
17
23
|
acceptedIn: OfferId;
|
|
18
24
|
description: string;
|
|
19
25
|
instance: Instance;
|
|
@@ -21,9 +27,13 @@ export function coalesceUpdates(updates: ERef<Subscriber<import("./smartWallet.j
|
|
|
21
27
|
offerStatuses: Map<OfferId, OfferStatus>;
|
|
22
28
|
balances: Map<globalThis.Brand, globalThis.Amount>;
|
|
23
29
|
};
|
|
24
|
-
export function assertHasData(follower:
|
|
25
|
-
export function objectMapStoragePath(subscribers?:
|
|
30
|
+
export function assertHasData(follower: Follower<any>): Promise<void>;
|
|
31
|
+
export function objectMapStoragePath(subscribers?: PublicSubscribers | TopicsRecord): ERef<Record<string, string>> | null;
|
|
26
32
|
export type CoalescedWalletState = ReturnType<typeof makeWalletStateCoalescer>["state"];
|
|
27
33
|
import type { OfferId } from './offers.js';
|
|
28
34
|
import type { OfferStatus } from './offers.js';
|
|
35
|
+
import type { UpdateRecord } from './smartWallet.js';
|
|
36
|
+
import type { Follower } from '@agoric/casting';
|
|
37
|
+
import type { PublicSubscribers } from './types.js';
|
|
38
|
+
import type { TopicsRecord } from '@agoric/zoe/src/contractSupport/index.js';
|
|
29
39
|
//# sourceMappingURL=utils.d.ts.map
|