@agoric/wallet-backend 0.13.5 → 0.13.6-dev-800c2bb.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/bundles/bundle-wallet-js-meta.json +582 -0
- package/bundles/bundle-wallet.js +1 -1
- package/deploy.js +2 -2
- package/jsconfig.json +1 -15
- package/package.json +22 -22
- package/scripts/build-bundles.js +1 -1
- package/src/findOrMakeInvitation.js +10 -13
- package/src/internal-types.js +6 -4
- package/src/issuerTable.js +5 -5
- package/src/lib-dehydrate.js +24 -31
- package/src/lib-wallet.js +85 -210
- package/src/types.js +2 -2
- package/src/wallet.js +4 -8
- package/test/test-getPursesNotifier.js +15 -16
- package/test/test-lib-dehydrate.js +10 -10
- package/test/test-lib-wallet.js +38 -247
- package/test/test-middleware.js +7 -15
- package/bundles/bundle-wallet-meta.js +0 -490
- package/src/marshal-contexts.js +0 -387
- package/test/attestationExample.js +0 -65
- package/test/test-wallet-marshal.js +0 -184
package/src/marshal-contexts.js
DELETED
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
import { makeScalarMap } from '@agoric/store';
|
|
3
|
-
import { Far, makeMarshal, Remotable } from '@endo/marshal';
|
|
4
|
-
import { HandledPromise } from '@endo/eventual-send'; // TODO: convince tsc this isn't needed
|
|
5
|
-
|
|
6
|
-
const { details: X, quote: q } = assert;
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* For a value with a known id in the board, we can use
|
|
10
|
-
* that board id as a slot to preserve identity when marshaling.
|
|
11
|
-
*
|
|
12
|
-
* @typedef {`board${Digits}`} BoardId
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @param {unknown} specimen
|
|
17
|
-
* @returns {specimen is BoardId}
|
|
18
|
-
*/
|
|
19
|
-
const isBoardId = specimen => {
|
|
20
|
-
return typeof specimen === 'string' && !!specimen.match(/^board[^:]/);
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* When marshaling a purse, payment, etc. we partition the slots
|
|
25
|
-
* using prefixes.
|
|
26
|
-
*
|
|
27
|
-
* @template {Record<string, IdTable<*,*>>} T
|
|
28
|
-
* @typedef {`${string & keyof T}:${Digits}`} WalletSlot<T>
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* @template {string} K
|
|
32
|
-
* @typedef {`${K}:${Digits}`} KindSlot<K>
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @template {Record<string, IdTable<*,*>>} T
|
|
37
|
-
* @param {T} _tables
|
|
38
|
-
* @param {string & keyof T} kind
|
|
39
|
-
* @param {number} id
|
|
40
|
-
* @returns {WalletSlot<T>}
|
|
41
|
-
*/
|
|
42
|
-
const makeWalletSlot = (_tables, kind, id) => {
|
|
43
|
-
const digits = /** @type {Digits} */ (`${id}`);
|
|
44
|
-
return `${kind}:${digits}`;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @template {Record<string, IdTable<*,*>>} T
|
|
49
|
-
* @param {T} record
|
|
50
|
-
* @param {(value: string, index: number, obj: string[]) => boolean} predicate
|
|
51
|
-
* @returns {string & keyof T | undefined}
|
|
52
|
-
*/
|
|
53
|
-
const findKey = (record, predicate) => {
|
|
54
|
-
const key = Object.keys(record).find(predicate);
|
|
55
|
-
return key;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* @template {Record<string, IdTable<*,*>>} T
|
|
60
|
-
* @param {T} tables
|
|
61
|
-
* @param {string} slot
|
|
62
|
-
* @returns {{ kind: undefined | string & keyof T, id: number }}
|
|
63
|
-
*/
|
|
64
|
-
const parseWalletSlot = (tables, slot) => {
|
|
65
|
-
const kind = findKey(tables, k => slot.startsWith(`${k}:`));
|
|
66
|
-
const id = kind ? Number(slot.slice(kind.length + 1)) : NaN;
|
|
67
|
-
return { kind, id };
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Since KindSlots always include a colon and BoardIds never do,
|
|
72
|
-
* we an mix them without confusion.
|
|
73
|
-
*
|
|
74
|
-
* @template {Record<string, IdTable<*,*>>} T
|
|
75
|
-
* @typedef {WalletSlot<T> | BoardId} MixedSlot<T>
|
|
76
|
-
*/
|
|
77
|
-
/**
|
|
78
|
-
* @typedef {`1` | `12` | `123`} Digits - 1 or more digits.
|
|
79
|
-
* NOTE: the typescript definition here is more restrictive than
|
|
80
|
-
* actual usage.
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* @template Slot
|
|
85
|
-
* @template Val
|
|
86
|
-
*
|
|
87
|
-
* @typedef {{
|
|
88
|
-
* bySlot: MapStore<Slot, Val>,
|
|
89
|
-
* byVal: MapStore<Val, Slot>,
|
|
90
|
-
* }} IdTable<Value>
|
|
91
|
-
*/
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* @template Slot
|
|
95
|
-
* @template Val
|
|
96
|
-
* @param {IdTable<Slot, Val>} table
|
|
97
|
-
* @param {Slot} slot
|
|
98
|
-
* @param {Val} val
|
|
99
|
-
*/
|
|
100
|
-
const initSlotVal = (table, slot, val) => {
|
|
101
|
-
table.bySlot.init(slot, val);
|
|
102
|
-
table.byVal.init(val, slot);
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Make context for exporting wallet data where brands etc. can be recognized by boardId.
|
|
107
|
-
* Export for use outside the smart wallet.
|
|
108
|
-
*
|
|
109
|
-
* When serializing wallet state for, there's a tension between
|
|
110
|
-
*
|
|
111
|
-
* - keeping purses etc. closely held
|
|
112
|
-
* - recognizing identity of brands also referenced in the state of contracts such as the AMM
|
|
113
|
-
*
|
|
114
|
-
* `makeMarshal()` is parameterized by the type of slots. Here we use a disjoint union of
|
|
115
|
-
* - board ids for widely shared objects
|
|
116
|
-
* - kind:seq ids for closely held objects; for example purse:123
|
|
117
|
-
*/
|
|
118
|
-
export const makeExportContext = () => {
|
|
119
|
-
const walletObjects = {
|
|
120
|
-
/** @type {IdTable<number, Purse>} */
|
|
121
|
-
purse: {
|
|
122
|
-
bySlot: makeScalarMap(),
|
|
123
|
-
byVal: makeScalarMap(),
|
|
124
|
-
},
|
|
125
|
-
/** @type {IdTable<number, Payment>} */
|
|
126
|
-
payment: {
|
|
127
|
-
bySlot: makeScalarMap(),
|
|
128
|
-
byVal: makeScalarMap(),
|
|
129
|
-
},
|
|
130
|
-
// TODO: offer, contact, dapp
|
|
131
|
-
/** @type {IdTable<number, unknown>} */
|
|
132
|
-
unknown: {
|
|
133
|
-
bySlot: makeScalarMap(),
|
|
134
|
-
byVal: makeScalarMap(),
|
|
135
|
-
},
|
|
136
|
-
};
|
|
137
|
-
/** @type {IdTable<BoardId, unknown>} */
|
|
138
|
-
const boardObjects = {
|
|
139
|
-
bySlot: makeScalarMap(),
|
|
140
|
-
byVal: makeScalarMap(),
|
|
141
|
-
};
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Look up the slot in mappings from published data
|
|
145
|
-
* else try walletObjects that we have seen.
|
|
146
|
-
*
|
|
147
|
-
* @throws if not found (a slotToVal function typically
|
|
148
|
-
* conjures a new identity)
|
|
149
|
-
*
|
|
150
|
-
* @param {MixedSlot<typeof walletObjects>} slot
|
|
151
|
-
* @param {string} _iface
|
|
152
|
-
*/
|
|
153
|
-
const slotToVal = (slot, _iface) => {
|
|
154
|
-
if (isBoardId(slot) && boardObjects.bySlot.has(slot)) {
|
|
155
|
-
return boardObjects.bySlot.get(slot);
|
|
156
|
-
}
|
|
157
|
-
const { kind, id } = parseWalletSlot(walletObjects, slot);
|
|
158
|
-
assert(kind, X`bad slot kind: ${slot}`);
|
|
159
|
-
const val = walletObjects[kind].bySlot.get(id); // or throw
|
|
160
|
-
return val;
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
let unknownNonce = 0;
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* @param {unknown} val
|
|
167
|
-
* @returns {MixedSlot<typeof walletObjects>}
|
|
168
|
-
*/
|
|
169
|
-
const valToSlot = val => {
|
|
170
|
-
if (boardObjects.byVal.has(val)) {
|
|
171
|
-
return boardObjects.byVal.get(val);
|
|
172
|
-
}
|
|
173
|
-
const kind = findKey(walletObjects, k => walletObjects[k].byVal.has(val));
|
|
174
|
-
if (kind) {
|
|
175
|
-
// @ts-expect-error has(val) above ensures val has the right type
|
|
176
|
-
const id = walletObjects[kind].byVal.get(val);
|
|
177
|
-
return makeWalletSlot(walletObjects, kind, id);
|
|
178
|
-
}
|
|
179
|
-
unknownNonce += 1;
|
|
180
|
-
const slot = makeWalletSlot(walletObjects, 'unknown', unknownNonce);
|
|
181
|
-
initSlotVal(walletObjects.unknown, unknownNonce, val);
|
|
182
|
-
return slot;
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* @template V
|
|
187
|
-
* @param {string & keyof typeof walletObjects} kind
|
|
188
|
-
* @param {IdTable<number, V>} table
|
|
189
|
-
*/
|
|
190
|
-
const makeSaver = (kind, table) => {
|
|
191
|
-
let nonce = 0;
|
|
192
|
-
/** @param {V} val */
|
|
193
|
-
const saver = val => {
|
|
194
|
-
nonce += 1;
|
|
195
|
-
initSlotVal(table, nonce, val);
|
|
196
|
-
};
|
|
197
|
-
return saver;
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
return harden({
|
|
201
|
-
savePurseActions: makeSaver('purse', walletObjects.purse),
|
|
202
|
-
savePaymentActions: makeSaver('payment', walletObjects.payment),
|
|
203
|
-
/**
|
|
204
|
-
* @param {number} id
|
|
205
|
-
* @param {Purse} purse
|
|
206
|
-
*/
|
|
207
|
-
initPurseId: (id, purse) => {
|
|
208
|
-
initSlotVal(walletObjects.purse, id, purse);
|
|
209
|
-
},
|
|
210
|
-
purseEntries: walletObjects.purse.bySlot.entries,
|
|
211
|
-
/**
|
|
212
|
-
* @param {BoardId} id
|
|
213
|
-
* @param {unknown} val
|
|
214
|
-
*/
|
|
215
|
-
initBoardId: (id, val) => {
|
|
216
|
-
initSlotVal(boardObjects, id, val);
|
|
217
|
-
},
|
|
218
|
-
/**
|
|
219
|
-
* @param {BoardId} id
|
|
220
|
-
* @param {unknown} val
|
|
221
|
-
*/
|
|
222
|
-
ensureBoardId: (id, val) => {
|
|
223
|
-
if (boardObjects.byVal.has(val)) {
|
|
224
|
-
assert.equal(boardObjects.byVal.get(val), id);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
initSlotVal(boardObjects, id, val);
|
|
228
|
-
},
|
|
229
|
-
...makeMarshal(valToSlot, slotToVal),
|
|
230
|
-
});
|
|
231
|
-
};
|
|
232
|
-
|
|
233
|
-
const defaultMakePresence = iface => {
|
|
234
|
-
const severed = `SEVERED: ${iface.replace(/^Alleged: /, '')}`;
|
|
235
|
-
return Far(severed, {});
|
|
236
|
-
};
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* Make context for marshalling wallet or board data.
|
|
240
|
-
* To be imported into the client, which never exports objects.
|
|
241
|
-
*
|
|
242
|
-
* @param {(iface: string) => unknown} [makePresence]
|
|
243
|
-
*/
|
|
244
|
-
export const makeImportContext = (makePresence = defaultMakePresence) => {
|
|
245
|
-
const walletObjects = {
|
|
246
|
-
/** @type {IdTable<number, unknown>} */
|
|
247
|
-
purse: {
|
|
248
|
-
bySlot: makeScalarMap(),
|
|
249
|
-
byVal: makeScalarMap(),
|
|
250
|
-
},
|
|
251
|
-
/** @type {IdTable<number, unknown>} */
|
|
252
|
-
payment: {
|
|
253
|
-
bySlot: makeScalarMap(),
|
|
254
|
-
byVal: makeScalarMap(),
|
|
255
|
-
},
|
|
256
|
-
/** @type {IdTable<number, unknown>} */
|
|
257
|
-
unknown: {
|
|
258
|
-
bySlot: makeScalarMap(),
|
|
259
|
-
byVal: makeScalarMap(),
|
|
260
|
-
},
|
|
261
|
-
};
|
|
262
|
-
/** @type {IdTable<BoardId, unknown>} */
|
|
263
|
-
const boardObjects = {
|
|
264
|
-
bySlot: makeScalarMap(),
|
|
265
|
-
byVal: makeScalarMap(),
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* @template Slot
|
|
270
|
-
* @template Val
|
|
271
|
-
* @param {IdTable<Slot, Val>} table
|
|
272
|
-
* @param {Slot} slot
|
|
273
|
-
* @param {string} iface
|
|
274
|
-
*/
|
|
275
|
-
const provideVal = (table, slot, iface) => {
|
|
276
|
-
if (table.bySlot.has(slot)) {
|
|
277
|
-
return table.bySlot.get(slot);
|
|
278
|
-
}
|
|
279
|
-
const val = makePresence(iface);
|
|
280
|
-
initSlotVal(table, slot, val);
|
|
281
|
-
return val;
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
const slotToVal = {
|
|
285
|
-
/**
|
|
286
|
-
* @param {string} slot
|
|
287
|
-
* @param {string} iface
|
|
288
|
-
*/
|
|
289
|
-
fromBoard: (slot, iface) => {
|
|
290
|
-
assert(isBoardId(slot), X`bad board slot ${q(slot)}`);
|
|
291
|
-
return provideVal(boardObjects, slot, iface);
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
/**
|
|
295
|
-
* @param {string} slot
|
|
296
|
-
* @param {string} iface
|
|
297
|
-
*/
|
|
298
|
-
fromMyWallet: (slot, iface) => {
|
|
299
|
-
if (!slot) {
|
|
300
|
-
// Empty or null slots are neither in the wallet nor the board.
|
|
301
|
-
return makePresence(`${slot}`);
|
|
302
|
-
}
|
|
303
|
-
const { kind, id } = parseWalletSlot(walletObjects, slot);
|
|
304
|
-
return kind
|
|
305
|
-
? provideVal(walletObjects[kind], id, iface)
|
|
306
|
-
: slotToVal.fromBoard(slot, iface);
|
|
307
|
-
},
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const valToSlot = {
|
|
311
|
-
fromBoard: val => boardObjects.byVal.get(val),
|
|
312
|
-
/** @param {unknown} val */
|
|
313
|
-
fromMyWallet: val => {
|
|
314
|
-
const kind = findKey(walletObjects, k => walletObjects[k].byVal.has(val));
|
|
315
|
-
assert(kind, X`cannot serialize unregistered ${val}`);
|
|
316
|
-
|
|
317
|
-
const id = walletObjects[kind].byVal.get(val);
|
|
318
|
-
return makeWalletSlot(walletObjects, kind, id);
|
|
319
|
-
},
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
const marshal = {
|
|
323
|
-
fromBoard: makeMarshal(valToSlot.fromBoard, slotToVal.fromBoard, {
|
|
324
|
-
marshalName: 'fromBoard',
|
|
325
|
-
}),
|
|
326
|
-
fromMyWallet: makeMarshal(valToSlot.fromMyWallet, slotToVal.fromMyWallet, {
|
|
327
|
-
marshalName: 'fromMyWallet',
|
|
328
|
-
}),
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
return harden({
|
|
332
|
-
/**
|
|
333
|
-
* @param {BoardId} id
|
|
334
|
-
* @param {unknown} val
|
|
335
|
-
*/
|
|
336
|
-
initBoardId: (id, val) => {
|
|
337
|
-
initSlotVal(boardObjects, id, val);
|
|
338
|
-
},
|
|
339
|
-
/**
|
|
340
|
-
* @param {BoardId} id
|
|
341
|
-
* @param {unknown} val
|
|
342
|
-
*/
|
|
343
|
-
ensureBoardId: (id, val) => {
|
|
344
|
-
if (boardObjects.byVal.has(val)) {
|
|
345
|
-
assert.equal(boardObjects.byVal.get(val), id);
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
initSlotVal(boardObjects, id, val);
|
|
349
|
-
},
|
|
350
|
-
fromMyWallet: Far('wallet marshaller', { ...marshal.fromMyWallet }),
|
|
351
|
-
fromBoard: Far('board marshaller', { ...marshal.fromBoard }),
|
|
352
|
-
});
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* @param {string} iface
|
|
357
|
-
* @param {{
|
|
358
|
-
* applyMethod: (target: unknown, method: string | symbol, args: unknown[]) => void,
|
|
359
|
-
* applyFunction: (target: unknown, args: unknown[]) => void,
|
|
360
|
-
* }} handler
|
|
361
|
-
*/
|
|
362
|
-
const makePresence = (iface, handler) => {
|
|
363
|
-
let obj;
|
|
364
|
-
// eslint-disable-next-line no-new
|
|
365
|
-
new HandledPromise((resolve, reject, resolveWithPresence) => {
|
|
366
|
-
obj = resolveWithPresence(handler);
|
|
367
|
-
});
|
|
368
|
-
assert(obj);
|
|
369
|
-
return Remotable(iface, undefined, obj);
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* @param {string} iface
|
|
374
|
-
* @param {(parts: unknown[]) => void} log
|
|
375
|
-
*/
|
|
376
|
-
export const makeLoggingPresence = (iface, log) => {
|
|
377
|
-
/** @type {any} */ // TODO: solve types puzzle
|
|
378
|
-
const it = makePresence(iface, {
|
|
379
|
-
applyMethod: (target, method, args) => {
|
|
380
|
-
log(harden(['applyMethod', target, method, args]));
|
|
381
|
-
},
|
|
382
|
-
applyFunction: (target, args) => {
|
|
383
|
-
log(harden(['applyFunction', target, args]));
|
|
384
|
-
},
|
|
385
|
-
});
|
|
386
|
-
return it;
|
|
387
|
-
};
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
import '@agoric/zoe/exported.js';
|
|
3
|
-
import { AmountMath } from '@agoric/ertp';
|
|
4
|
-
import { E } from '@endo/eventual-send';
|
|
5
|
-
import { Far } from '@endo/marshal';
|
|
6
|
-
|
|
7
|
-
/** @param {ZCF} zcf */
|
|
8
|
-
export const start = async zcf => {
|
|
9
|
-
const mint = await zcf.makeZCFMint('Attestation');
|
|
10
|
-
const { brand } = await mint.getIssuerRecord();
|
|
11
|
-
const callHistory = {
|
|
12
|
-
/** @type Array<string> */
|
|
13
|
-
wantProposals: [],
|
|
14
|
-
/** @type Array<string> */
|
|
15
|
-
wantAllocations: [],
|
|
16
|
-
/** @type Array<string> */
|
|
17
|
-
returnProposals: [],
|
|
18
|
-
/** @type Array<string> */
|
|
19
|
-
returnAllocations: [],
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const mintAttestation = ({ value }) => {
|
|
23
|
-
const { userSeat, zcfSeat } = zcf.makeEmptySeatKit();
|
|
24
|
-
mint.mintGains({ Attestation: AmountMath.make(brand, value) }, zcfSeat);
|
|
25
|
-
zcfSeat.exit();
|
|
26
|
-
return E(userSeat).getPayout('Attestation');
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const handleWantAtt = seat => {
|
|
30
|
-
const proposal = seat.getProposal();
|
|
31
|
-
callHistory.wantProposals.push(proposal);
|
|
32
|
-
callHistory.wantAllocations.push(seat.getCurrentAllocation());
|
|
33
|
-
mint.mintGains(
|
|
34
|
-
{ Attestation: AmountMath.make(brand, proposal.want.Attestation.value) },
|
|
35
|
-
seat,
|
|
36
|
-
);
|
|
37
|
-
seat.exit();
|
|
38
|
-
return 'good job';
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const handleReturnAtt = seat => {
|
|
42
|
-
const proposal = seat.getProposal();
|
|
43
|
-
callHistory.returnProposals.push(proposal);
|
|
44
|
-
callHistory.returnAllocations.push(seat.getCurrentAllocation());
|
|
45
|
-
seat.exit();
|
|
46
|
-
return 'thank you for that';
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const makeWantAttInvitation = () =>
|
|
50
|
-
zcf.makeInvitation(handleWantAtt, 'WantAtt');
|
|
51
|
-
|
|
52
|
-
const makeReturnAttInvitation = () =>
|
|
53
|
-
zcf.makeInvitation(handleReturnAtt, 'ReturnAtt');
|
|
54
|
-
|
|
55
|
-
const getCallHistory = () => callHistory;
|
|
56
|
-
|
|
57
|
-
const publicFacet = Far('attestation public facet', {
|
|
58
|
-
mintAttestation,
|
|
59
|
-
makeWantAttInvitation,
|
|
60
|
-
makeReturnAttInvitation,
|
|
61
|
-
getCallHistory,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return harden({ publicFacet });
|
|
65
|
-
};
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js';
|
|
3
|
-
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
4
|
-
import { Far } from '@endo/far';
|
|
5
|
-
import { makeBoard } from '@agoric/vats/src/lib-board.js';
|
|
6
|
-
import { makeHandle } from '@agoric/zoe/src/makeHandle.js';
|
|
7
|
-
import {
|
|
8
|
-
makeExportContext,
|
|
9
|
-
makeImportContext,
|
|
10
|
-
} from '../src/marshal-contexts.js';
|
|
11
|
-
|
|
12
|
-
/** @param {ReturnType<typeof makeBoard>} board */
|
|
13
|
-
const makeAMM = board => {
|
|
14
|
-
const atom = Far('ATOM brand', {});
|
|
15
|
-
const pub = board.getPublishingMarshaller();
|
|
16
|
-
return harden({ getMetrics: () => pub.serialize(harden([atom])) });
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/** @param {ReturnType<typeof makeBoard>} board */
|
|
20
|
-
const makeOnChainWallet = board => {
|
|
21
|
-
const context = makeExportContext();
|
|
22
|
-
let brand;
|
|
23
|
-
return harden({
|
|
24
|
-
suggestIssuer: (name, boardId) => {
|
|
25
|
-
brand = board.getValue(boardId);
|
|
26
|
-
const purse = Far(`${name} purse`, {
|
|
27
|
-
getCurrentAmount: () => harden({ brand, value: 100 }),
|
|
28
|
-
});
|
|
29
|
-
// only for private brands
|
|
30
|
-
// context.initBrandId(boardId, brand);
|
|
31
|
-
context.initBoardId(boardId, brand);
|
|
32
|
-
// @ts-expect-error mock purse
|
|
33
|
-
context.initPurseId(name, purse); // TODO: strong id rather than name?
|
|
34
|
-
},
|
|
35
|
-
publish: () =>
|
|
36
|
-
context.serialize(
|
|
37
|
-
harden(
|
|
38
|
-
[...context.purseEntries()].map(([name, purse], id) => ({
|
|
39
|
-
meta: { id },
|
|
40
|
-
// displayInfo
|
|
41
|
-
// brandBoardId,
|
|
42
|
-
purse,
|
|
43
|
-
brand,
|
|
44
|
-
currentAmount: purse.getCurrentAmount(),
|
|
45
|
-
// actions
|
|
46
|
-
brandPetname: name,
|
|
47
|
-
pursePetname: `${name} purse`,
|
|
48
|
-
})),
|
|
49
|
-
),
|
|
50
|
-
),
|
|
51
|
-
});
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
test('makeImportContext preserves identity across AMM and wallet', t => {
|
|
55
|
-
const context = makeImportContext();
|
|
56
|
-
|
|
57
|
-
const board = makeBoard(0, { prefix: 'board' });
|
|
58
|
-
const amm = makeAMM(board);
|
|
59
|
-
const ammMetricsCapData = amm.getMetrics();
|
|
60
|
-
|
|
61
|
-
t.deepEqual(ammMetricsCapData, {
|
|
62
|
-
body: JSON.stringify([
|
|
63
|
-
{ '@qclass': 'slot', iface: 'Alleged: ATOM brand', index: 0 },
|
|
64
|
-
]),
|
|
65
|
-
slots: ['board011'],
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
/** @type {Brand[]} */
|
|
69
|
-
const [b1] = context.fromBoard.unserialize(ammMetricsCapData);
|
|
70
|
-
/** @type {Brand[]} */
|
|
71
|
-
const [b2] = context.fromBoard.unserialize(amm.getMetrics());
|
|
72
|
-
t.is(b1, b2, 'unserialization twice from same source');
|
|
73
|
-
|
|
74
|
-
const myWallet = makeOnChainWallet(board);
|
|
75
|
-
myWallet.suggestIssuer('ATOM', 'board011');
|
|
76
|
-
const walletCapData = myWallet.publish();
|
|
77
|
-
t.deepEqual(walletCapData, {
|
|
78
|
-
body: JSON.stringify([
|
|
79
|
-
{
|
|
80
|
-
brand: { '@qclass': 'slot', iface: 'Alleged: ATOM brand', index: 0 },
|
|
81
|
-
brandPetname: 'ATOM',
|
|
82
|
-
currentAmount: { brand: { '@qclass': 'slot', index: 0 }, value: 100 },
|
|
83
|
-
meta: { id: 0 },
|
|
84
|
-
purse: { '@qclass': 'slot', iface: 'Alleged: ATOM purse', index: 1 },
|
|
85
|
-
pursePetname: 'ATOM purse',
|
|
86
|
-
},
|
|
87
|
-
]),
|
|
88
|
-
slots: ['board011', 'purse:ATOM'],
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const walletState = context.fromMyWallet.unserialize(walletCapData);
|
|
92
|
-
t.is(walletState[0].brand, b1, 'unserialization across sources');
|
|
93
|
-
|
|
94
|
-
t.throws(
|
|
95
|
-
() => context.fromBoard.unserialize(walletCapData),
|
|
96
|
-
{ message: /bad board slot/ },
|
|
97
|
-
'AMM cannot refer to purses',
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
t.throws(
|
|
101
|
-
() => context.fromMyWallet.serialize(Far('widget', {})),
|
|
102
|
-
{
|
|
103
|
-
message: /unregistered/,
|
|
104
|
-
},
|
|
105
|
-
'import context never exports unregistered objects',
|
|
106
|
-
);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('ensureBoardId allows re-registration; initBoardId does not', t => {
|
|
110
|
-
const brandM = Far('Moola brand', {});
|
|
111
|
-
const brandS = Far('Semolean brand', {});
|
|
112
|
-
|
|
113
|
-
const context = makeExportContext();
|
|
114
|
-
context.initBoardId('board1', brandM);
|
|
115
|
-
t.throws(() => context.initBoardId('board1', brandM));
|
|
116
|
-
context.ensureBoardId('board1', brandM);
|
|
117
|
-
t.throws(() => context.ensureBoardId('board12', brandM));
|
|
118
|
-
context.ensureBoardId('board12', brandS);
|
|
119
|
-
t.throws(() => context.initBoardId('board12', brandM));
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test('makeExportContext.serialize handles unregistered identites', t => {
|
|
123
|
-
const brand = Far('Zoe invitation brand', {});
|
|
124
|
-
const instance = Far('amm instance', {});
|
|
125
|
-
const invitationAmount = harden({ brand, value: [{ instance }] });
|
|
126
|
-
|
|
127
|
-
const context = makeExportContext();
|
|
128
|
-
context.initBoardId('board1', brand);
|
|
129
|
-
const actual = context.serialize(invitationAmount);
|
|
130
|
-
|
|
131
|
-
t.deepEqual(actual, {
|
|
132
|
-
body: JSON.stringify({
|
|
133
|
-
brand: {
|
|
134
|
-
'@qclass': 'slot',
|
|
135
|
-
iface: 'Alleged: Zoe invitation brand',
|
|
136
|
-
index: 0,
|
|
137
|
-
},
|
|
138
|
-
value: [
|
|
139
|
-
{
|
|
140
|
-
instance: {
|
|
141
|
-
'@qclass': 'slot',
|
|
142
|
-
iface: 'Alleged: amm instance',
|
|
143
|
-
index: 1,
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
],
|
|
147
|
-
}),
|
|
148
|
-
slots: ['board1', 'unknown:1'],
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
t.deepEqual(context.unserialize(actual), invitationAmount);
|
|
152
|
-
|
|
153
|
-
const myPayment = /** @type {Payment} */ (
|
|
154
|
-
Far('payment', { getAllegedBrand: () => assert.fail('no impl') })
|
|
155
|
-
);
|
|
156
|
-
context.savePaymentActions(myPayment);
|
|
157
|
-
const cap2 = context.serialize(myPayment);
|
|
158
|
-
t.deepEqual(cap2, {
|
|
159
|
-
body: JSON.stringify({
|
|
160
|
-
'@qclass': 'slot',
|
|
161
|
-
iface: 'Alleged: payment',
|
|
162
|
-
index: 0,
|
|
163
|
-
}),
|
|
164
|
-
slots: ['payment:1'],
|
|
165
|
-
});
|
|
166
|
-
t.deepEqual(context.unserialize(cap2), myPayment);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test('fromBoard.serialize requires board ids', t => {
|
|
170
|
-
const context = makeImportContext();
|
|
171
|
-
|
|
172
|
-
const unpassable = harden({
|
|
173
|
-
instance: makeHandle('Instance'),
|
|
174
|
-
});
|
|
175
|
-
t.throws(() => context.fromBoard.serialize(unpassable), {
|
|
176
|
-
message: '"key" not found: "[Alleged: InstanceHandle]"',
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
context.ensureBoardId('board123', unpassable.instance);
|
|
180
|
-
t.deepEqual(context.fromBoard.serialize(unpassable), {
|
|
181
|
-
body: '{"instance":{"@qclass":"slot","iface":"Alleged: InstanceHandle","index":0}}',
|
|
182
|
-
slots: ['board123'],
|
|
183
|
-
});
|
|
184
|
-
});
|