-
Notifications
You must be signed in to change notification settings - Fork 190
/
types.js
420 lines (387 loc) · 17.5 KB
/
types.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
// @jessie-check
// Ensure this is a module.
export {};
/// <reference types="ses" />
/**
* @import {Passable, RemotableObject} from '@endo/pass-style')
* @import {CopyBag, CopySet, Key} from '@endo/patterns')
* @import {LatestTopic, NotifierRecord} from '@agoric/notifier');
*/
/** @typedef {{ brand: Brand<'nat'>; value: bigint }} NatAmount */
/**
* @template {Key} K
* @typedef {{ brand: Brand<'set'>; value: K[] }} SetAmount
*/
/**
* @template {Key} K
* @typedef {{ brand: Brand<'copySet'>; value: CopySet<K> }} CopySetAmount
*/
/**
* @template {Key} K
* @typedef {{ brand: Brand<'copyBag'>; value: CopyBag<K> }} CopyBagAmount
*/
/** @typedef {{ brand: Brand<any>; value: any }} AnyAmount */
/**
* @template {AssetKind} [K=AssetKind]
* @template {Key} [M=Key]
* @typedef {K extends 'nat'
* ? NatAmount
* : K extends 'set'
* ? SetAmount<M>
* : K extends 'copySet'
* ? CopySetAmount<M>
* : K extends 'copyBag'
* ? CopyBagAmount<M>
* : AnyAmount} Amount
* Amounts are descriptions of digital assets, answering the questions "how
* much" and "of what kind". Amounts are values labeled with a brand.
* AmountMath executes the logic of how amounts are changed when digital
* assets are merged, separated, or otherwise manipulated. For example, a
* deposit of 2 bucks into a purse that already has 3 bucks gives a new purse
* balance of 5 bucks. An empty purse has 0 bucks. AmountMath relies heavily
* on polymorphic MathHelpers, which manipulate the unbranded portion.
*/
/**
* @typedef {NatValue | SetValue | CopySet | import('@endo/patterns').CopyBag} AmountValue
* An `AmountValue` describes a set or quantity of assets that can be owned or
* shared.
*
* A fungible `AmountValue` uses a non-negative bigint to represent a quantity
* of that many assets.
*
* A non-fungible `AmountValue` uses an array or CopySet of `Key`s to represent
* a set of whatever asset each key represents. A `Key` is a passable value
* that can be used as an element in a set (SetStore or CopySet) or as the key
* in a map (MapStore or CopyMap).
*
* `SetValue` is for the deprecated set representation, using an array directly
* to represent the array of its elements. `CopySet` is the proper
* representation using a CopySet.
*
* A semi-fungible `CopyBag` is represented as a `CopyBag` of `Key` objects.
* "Bag" is synonymous with MultiSet, where an element of a bag can be present
* once or more times, i.e., some positive bigint number of times,
* representing that quantity of the asset represented by that key.
*/
/**
* @typedef {'nat' | 'set' | 'copySet' | 'copyBag'} AssetKind See doc-comment
* for `AmountValue`.
*/
/**
* @template {AssetKind} K
* @template {Key} [M=Key] member kind, for Amounts that have member values
* @typedef {K extends 'nat'
* ? NatValue
* : K extends 'set'
* ? SetValue<M>
* : K extends 'copySet'
* ? CopySet<M>
* : K extends 'copyBag'
* ? CopyBag<M>
* : never} AssetValueForKind
*/
/**
* @template {AmountValue} V
* @typedef {V extends NatValue
* ? 'nat'
* : V extends SetValue
* ? 'set'
* : V extends CopySet
* ? 'copySet'
* : V extends import('@endo/patterns').CopyBag
* ? 'copyBag'
* : never} AssetKindForValue
*/
/**
* @template {AssetKind} [K=AssetKind]
* @typedef {object} DisplayInfo
* @property {number} [decimalPlaces] Tells the display software how many
* decimal places to move the decimal over to the left, or in other words,
* which position corresponds to whole numbers. We require fungible digital
* assets to be represented in integers, in the smallest unit (i.e. USD might
* be represented in mill, a thousandth of a dollar. In that case,
* `decimalPlaces` would be 3.) This property is optional, and for
* non-fungible digital assets, should not be specified. The decimalPlaces
* property should be used for _display purposes only_. Any other use is an
* anti-pattern.
* @property {K} assetKind - the kind of asset, either AssetKind.NAT (fungible)
* or AssetKind.SET or AssetKind.COPY_SET (non-fungible)
*/
// XXX hack around JSDoc union handling
/**
* @template {AssetKind} K
* @typedef {object} BrandMethods
* @property {(allegedIssuer: ERef<Issuer<K>>) => Promise<boolean>} isMyIssuer
* Should be used with `issuer.getBrand` to ensure an issuer and brand match.
* @property {() => string} getAllegedName
* @property {() => DisplayInfo<K>} getDisplayInfo Give information to UI on how
* to display the amount.
* @property {() => Pattern} getAmountShape
*/
/**
* @template {AssetKind} [K=AssetKind]
* @typedef {RemotableObject & BrandMethods<K>} Brand The brand identifies the
* kind of issuer, and has a function to get the alleged name for the kind of
* asset described. The alleged name (such as 'BTC' or 'moola') is provided by
* the maker of the issuer and should not be trusted as accurate.
*
* Every amount created by a particular issuer will share the same brand, but
* recipients cannot rely on the brand to verify that a purported amount
* represents the issuer they intended, since the same brand can be reused by
* a misbehaving issuer.
*/
// /////////////////////////// Issuer //////////////////////////////////////////
/**
* @callback IssuerIsLive Return true if the payment continues to exist.
*
* If the payment is a promise, the operation will proceed upon fulfillment.
* @param {ERef<Payment>} payment
* @returns {Promise<boolean>}
*/
/**
* @template {AssetKind} K
* @template {Key} [M=Key] member kind, for Amounts that have member values
* @callback IssuerGetAmountOf Get the amount of digital assets in the payment.
* Because the payment is not trusted, we cannot call a method on it directly,
* and must use the issuer instead.
*
* If the payment is a promise, the operation will proceed upon fulfillment.
* @param {ERef<Payment<K, M>>} payment
* @returns {Promise<Amount<K, M>>}
*/
/**
* @callback IssuerBurn Burn all of the digital assets in the payment.
* `optAmountShape` is optional. If the `optAmountShape` pattern is present,
* the amount of the digital assets in the payment must match
* `optAmountShape`, to prevent sending the wrong payment and other
* confusion.
*
* If the payment is a promise, the operation will proceed upon fulfillment.
*
* As always with optional `Pattern` arguments, keep in mind that technically
* the value `undefined` itself is a valid `Key` and therefore a valid
* `Pattern`. But in optional pattern position, a top level `undefined` will
* be interpreted as absence. If you want to express a `Pattern` that will
* match only `undefined`, use `M.undefined()` instead.
* @param {ERef<Payment>} payment
* @param {Pattern} [optAmountShape]
* @returns {Promise<Amount>}
*/
/**
* @template {AssetKind} K
* @template {Key} M
* @typedef {object} IssuerMethods Work around JSDoc union handling
* @property {() => Brand<K>} getBrand Get the Brand for this Issuer. The Brand
* indicates the type of digital asset and is shared by the mint, the issuer,
* and any purses and payments of this particular kind. The brand is not
* closely held, so this function should not be trusted to identify an issuer
* alone. Fake digital assets and amount can use another issuer's brand.
* @property {() => string} getAllegedName Get the allegedName for this
* mint/issuer
* @property {() => K} getAssetKind Get the kind of MathHelpers used by this
* Issuer.
* @property {() => DisplayInfo<K>} getDisplayInfo Give information to UI on how
* to display amounts for this issuer.
* @property {() => Purse<K, M>} makeEmptyPurse Make an empty purse of this
* brand.
* @property {IssuerIsLive} isLive
* @property {IssuerGetAmountOf<K, M>} getAmountOf
* @property {IssuerBurn} burn
*/
/**
* @template {AssetKind} [K=AssetKind]
* @template {Key} [M=Key] member kind, for Amounts that have member values
* @typedef {RemotableObject & IssuerMethods<K, M>} Issuer The issuer cannot
* mint a new amount, but it can create empty purses and payments. The issuer
* can also transform payments (splitting payments, combining payments,
* burning payments, and claiming payments exclusively). The issuer should be
* gotten from a trusted source and then relied upon as the decider of whether
* an untrusted payment is valid.
*/
/**
* @template {AssetKind} [K=AssetKind]
* @typedef {object} PaymentLedger
* @property {Mint<K>} mint
* @property {Purse<K>} mintRecoveryPurse Externally useful only if this issuer
* uses recovery sets. Can be used to get the recovery set associated with
* minted payments that are still live.
* @property {Issuer<K>} issuer
* @property {Brand<K>} brand
*/
/**
* @template {AssetKind} [K=AssetKind]
* @template {Key} [M=Key] member kind, for Amounts that have member values
* @typedef {object} IssuerKit
* @property {Mint<K, M>} mint
* @property {Purse<K, M>} mintRecoveryPurse Externally useful only if this
* issuer uses recovery sets. Can be used to get the recovery set associated
* with minted payments that are still live.
* @property {Issuer<K, M>} issuer
* @property {Brand<K>} brand
* @property {DisplayInfo} displayInfo
*/
/**
* @typedef {object} AdditionalDisplayInfo
* @property {number} [decimalPlaces] Tells the display software how many
* decimal places to move the decimal over to the left, or in other words,
* which position corresponds to whole numbers. We require fungible digital
* assets to be represented in integers, in the smallest unit (i.e. USD might
* be represented in mill, a thousandth of a dollar. In that case,
* `decimalPlaces` would be 3.) This property is optional, and for
* non-fungible digital assets, should not be specified. The decimalPlaces
* property should be used for _display purposes only_. Any other use is an
* anti-pattern.
* @property {AssetKind} [assetKind]
*/
/**
* @template {AssetKind} [K=AssetKind]
* @template {Key} [M=Key] member kind, for Amounts that have member values
* @typedef {object} Mint Holding a Mint carries the right to issue new digital
* assets. These assets all have the same kind, which is called a Brand.
* @property {() => Issuer<K, M>} getIssuer Gets the Issuer for this mint.
* @property {(newAmount: Amount<K>) => Payment<K, M>} mintPayment Creates a new
* Payment containing newly minted amount.
*/
// /////////////////////////// Purse / Payment /////////////////////////////////
/**
* Issuers first became durable with mandatory recovery sets. Later they were
* made optional, but there is no support for converting from one state to the
* other. Thus, absence of a `RecoverySetsOption` state is equivalent to
* `'hasRecoverySets'`. In the absence of a `recoverySetsOption` parameter,
* upgradeIssuerKit defaults to the predecessor's `RecoverySetsOption` state, or
* `'hasRecoverySets'` if none.
*
* At this time, issuers started in one of the states (`'noRecoverySets'`, or
* `'hasRecoverySets'`) cannot be converted to the other on upgrade. If this
* transition is needed, it can likely be supported in a future upgrade. File an
* issue on github and explain what you need and why.
*
* @typedef {'hasRecoverySets' | 'noRecoverySets'} RecoverySetsOption
*/
// /////////////////////////// Purse / Payment /////////////////////////////////
/**
* @callback DepositFacetReceive
* @param {Payment} payment
* @param {Pattern} [optAmountShape]
* @returns {Amount}
*/
/**
* @typedef {object} DepositFacet
* @property {DepositFacetReceive} receive Deposit all the contents of payment
* into the purse that made this facet, returning the amount. If the optional
* argument `optAmount` does not equal the amount of digital assets in the
* payment, throw an error.
*
* If payment is a promise, throw an error.
*/
/**
* @template {AssetKind} [K=AssetKind]
* @template {Key} [M=Key] member kind, for Amounts that have member values
* @typedef {RemotableObject & PurseMethods<K, M>} Purse Purses hold amount of
* digital assets of the same brand, but unlike Payments, they are not meant
* to be sent to others. To transfer digital assets, a Payment should be
* withdrawn from a Purse. The amount of digital assets in a purse can change
* through the action of deposit() and withdraw().
*/
/**
* @template {AssetKind} [K=AssetKind]
* @template {Key} [M=Key] member kind, for Amounts that have member values
* @typedef {object} PurseMethods The primary use for Purses and Payments is for
* currency-like and goods-like digital assets, but they can also be used to
* represent other kinds of rights, such as the right to participate in a
* particular contract.
* @property {() => Brand<K>} getAllegedBrand Get the alleged Brand for this
* Purse
* @property {() => Amount<K, M>} getCurrentAmount Get the amount contained in
* this purse.
* @property {() => LatestTopic<Amount<K, M>>} getCurrentAmountNotifier Get a
* lossy notifier for changes to this purse's balance.
* @property {<P extends Payment<K, M>>(
* payment: P,
* optAmountShape?: Pattern,
* ) => P extends Payment<K, M> ? Amount<K, M> : never} deposit
* Deposit all the contents of payment into this purse, returning the amount. If
* the optional argument `optAmount` does not equal the amount of digital
* assets in the payment, throw an error.
*
* If payment is a promise, throw an error.
* @property {() => DepositFacet} getDepositFacet Return an object whose
* `receive` method deposits to the current Purse.
* @property {(amount: Amount<K, M>) => Payment<K, M>} withdraw Withdraw amount
* from this purse into a new Payment.
* @property {() => CopySet<Payment<K, M>>} getRecoverySet The set of payments
* withdrawn from this purse that are still live. These are the payments that
* can still be recovered in emergencies by, for example, depositing into this
* purse. Such a deposit action is like canceling an outstanding check because
* you're tired of waiting for it. Once your cancellation is acknowledged, you
* can spend the assets at stake on other things. Afterwards, if the recipient
* of the original check finally gets around to depositing it, their deposit
* fails.
*
* Returns an empty set if this issuer does not support recovery sets.
* @property {() => Amount<K, M>} recoverAll For use in emergencies, such as
* coming back from a traumatic crash and upgrade. This deposits all the
* payments in this purse's recovery set into the purse itself, returning the
* total amount of assets recovered.
*
* Returns an empty amount if this issuer does not support recovery sets.
*/
/**
* @template {AssetKind} [K=AssetKind]
* @template {Key} [M=Key] member kind, for Amounts that have member values
* @typedef {RemotableObject & PaymentMethods<K>} Payment Payments hold amount
* of digital assets of the same brand in transit. Payments can be deposited
* in purses, split into multiple payments, combined, and claimed (getting an
* exclusive payment). Payments are linear, meaning that either a payment has
* the same amount of digital assets it started with, or it is used up
* entirely. It is impossible to partially use a payment.
*
* Payments are often received from other actors and therefore should not be
* trusted themselves. To get the amount of digital assets in a payment, use
* the trusted issuer: issuer.getAmountOf(payment),
*
* Payments can be converted to Purses by getting a trusted issuer and calling
* `issuer.makeEmptyPurse()` to create a purse, then
* `purse.deposit(payment)`.
*/
/**
* @template {AssetKind} [K=AssetKind]
* @typedef {object} PaymentMethods
* @property {() => Brand<K>} getAllegedBrand Get the allegedBrand, indicating
* the type of digital asset this payment purports to be, and which issuer to
* use. Because payments are not trusted, any method calls on payments should
* be treated with suspicion and verified elsewhere.
*/
// /////////////////////////// MathHelpers /////////////////////////////////////
/**
* @template {AmountValue} V
* @typedef {object} MathHelpers All of the difference in how digital asset
* amount are manipulated can be reduced to the behavior of the math on
* values. We extract this custom logic into mathHelpers. MathHelpers are
* about value arithmetic, whereas AmountMath is about amounts, which are the
* values labeled with a brand. AmountMath use mathHelpers to do their value
* arithmetic, and then brand the results, making a new amount.
*
* The MathHelpers are designed to be called only from AmountMath, and so all
* methods but coerce can assume their inputs are valid. They only need to do
* output validation, and only when there is a possibility of invalid output.
* @property {(allegedValue: V) => V} doCoerce Check the kind of this value and
* throw if it is not the expected kind.
* @property {() => V} doMakeEmpty Get the representation for the identity
* element (often 0 or an empty array)
* @property {(value: V) => boolean} doIsEmpty Is the value the identity
* element?
* @property {(left: V, right: V) => boolean} doIsGTE Is the left greater than
* or equal to the right?
* @property {(left: V, right: V) => boolean} doIsEqual Does left equal right?
* @property {(left: V, right: V) => V} doAdd Return the left combined with the
* right.
* @property {(left: V, right: V) => V} doSubtract Return what remains after
* removing the right from the left. If something in the right was not in the
* left, we throw an error.
*/
/** @typedef {bigint} NatValue */
/**
* @template {Key} [K=Key]
* @typedef {K[]} SetValue
*/