11import { q , Fail } from '@endo/errors' ;
2- import { passStyleOf , assertRemotable , assertRecord } from '@endo/marshal' ;
2+ import { assertRemotable , assertRecord } from '@endo/pass-style' ;
3+ import { kindOf } from '@endo/patterns' ;
34
4- import { M , matches } from '@agoric/store' ;
55import { natMathHelpers } from './mathHelpers/natMathHelpers.js' ;
66import { setMathHelpers } from './mathHelpers/setMathHelpers.js' ;
77import { copySetMathHelpers } from './mathHelpers/copySetMathHelpers.js' ;
88import { copyBagMathHelpers } from './mathHelpers/copyBagMathHelpers.js' ;
99
1010/**
11+ * @import {Passable} from '@endo/pass-style'
1112 * @import {CopyBag, CopySet} from '@endo/patterns';
12- * @import {Amount, AssetValueForKind, Brand, CopyBagAmount, CopySetAmount, MathHelpers, NatAmount, NatValue, SetAmount, SetValue} from './types.js';
13+ * @import {Amount, AmountBound, AmountValueBound, AssetValueForKind, Brand, CopyBagAmount, CopySetAmount, MathHelpers, NatAmount, NatValue, SetAmount, SetValue} from './types.js';
1314 */
1415
1516// NB: AssetKind is both a constant for enumerated values and a type for those values.
@@ -75,43 +76,50 @@ const helpers = {
7576 copyBag : copyBagMathHelpers ,
7677} ;
7778
78- /** @type {(value: unknown) => 'nat' | 'set' | 'copySet' | 'copyBag' } } */
79- const assertValueGetAssetKind = value => {
80- const passStyle = passStyleOf ( value ) ;
81- if ( passStyle === 'bigint' ) {
82- return 'nat' ;
83- }
84- if ( passStyle === 'copyArray' ) {
85- return 'set' ;
86- }
87- if ( matches ( value , M . set ( ) ) ) {
88- return 'copySet' ;
89- }
90- if ( matches ( value , M . bag ( ) ) ) {
91- return 'copyBag' ;
79+ /**
80+ * @template {AmountValueBound} V=AmountValueBound
81+ * @param {V } value
82+ * @param {AssetKind } [defaultKind]
83+ * @returns {AssetKind }
84+ */
85+ const assertValueBoundGetAssetKind = ( value , defaultKind = undefined ) => {
86+ const kind = kindOf ( value ) ;
87+ switch ( kind ) {
88+ case 'bigint' : {
89+ return 'nat' ;
90+ }
91+ case 'copyArray' : {
92+ return 'set' ;
93+ }
94+ case 'copySet' :
95+ case 'copyBag' : {
96+ return kind ;
97+ }
98+ case 'match:has' : {
99+ if ( defaultKind === undefined ) {
100+ throw Fail `defaultKind expected for right has-bound ${ value } ` ;
101+ }
102+ return defaultKind ;
103+ }
104+ default : {
105+ throw Fail `value ${ value } must be an AmountValueBound, not ${ q ( kind ) } ` ;
106+ }
92107 }
93- // TODO This isn't quite the right error message, in case valuePassStyle
94- // is 'tagged'. We would need to distinguish what kind of tagged
95- // object it is.
96- // Also, this kind of manual listing is a maintenance hazard we
97- // (TODO) will encounter when we extend the math helpers further.
98- throw Fail `value ${ value } must be a bigint, copySet, copyBag, or an array, not ${ q (
99- passStyle ,
100- ) } `;
101108} ;
102109
103110/**
104111 * Asserts that value is a valid AmountMath and returns the appropriate helpers.
105112 *
106113 * Made available only for testing, but it is harmless for other uses.
107114 *
108- * @template V
115+ * @template {AmountValueBound} V=AmountValueBound
109116 * @param {V } value
117+ * @param {AssetKind } [defaultKind]
110118 * @returns {MathHelpers<V> }
111119 */
112- export const assertValueGetHelpers = value =>
120+ export const assertValueBoundGetHelpers = ( value , defaultKind = undefined ) =>
113121 // @ts -expect-error cast
114- helpers [ assertValueGetAssetKind ( value ) ] ;
122+ helpers [ assertValueBoundGetAssetKind ( value , defaultKind ) ] ;
115123
116124/** @type {(allegedBrand: Brand, brand?: Brand) => void } */
117125const optionalBrandCheck = ( allegedBrand , brand ) => {
@@ -127,15 +135,19 @@ const optionalBrandCheck = (allegedBrand, brand) => {
127135/**
128136 * @template {AssetKind} K
129137 * @param {Amount<K> } leftAmount
130- * @param {Amount <K> } rightAmount
138+ * @param {AmountBound <K> } rightAmountBound
131139 * @param {Brand<K> | undefined } brand
132140 * @returns {MathHelpers<any> }
133141 */
134- const checkLRAndGetHelpers = ( leftAmount , rightAmount , brand = undefined ) => {
142+ const checkLRAndGetHelpers = (
143+ leftAmount ,
144+ rightAmountBound ,
145+ brand = undefined ,
146+ ) => {
135147 assertRecord ( leftAmount , 'leftAmount' ) ;
136- assertRecord ( rightAmount , 'rightAmount ' ) ;
137- const { value : leftValue , brand : leftBrand } = leftAmount ;
138- const { value : rightValue , brand : rightBrand } = rightAmount ;
148+ assertRecord ( rightAmountBound , 'rightAmountBound ' ) ;
149+ const { brand : leftBrand , value : leftValue } = leftAmount ;
150+ const { brand : rightBrand , value : rightValueBound } = rightAmountBound ;
139151 assertRemotable ( leftBrand , 'leftBrand' ) ;
140152 assertRemotable ( rightBrand , 'rightBrand' ) ;
141153 optionalBrandCheck ( leftBrand , brand ) ;
@@ -144,41 +156,41 @@ const checkLRAndGetHelpers = (leftAmount, rightAmount, brand = undefined) => {
144156 Fail `Brands in left ${ q ( leftBrand ) } and right ${ q (
145157 rightBrand ,
146158 ) } should match but do not`;
147- const leftHelpers = assertValueGetHelpers ( leftValue ) ;
148- const rightHelpers = assertValueGetHelpers ( rightValue ) ;
149- leftHelpers === rightHelpers ||
150- Fail `The left ${ leftAmount } and right amount ${ rightAmount } had different assetKinds` ;
151- return leftHelpers ;
159+ const leftKind = assertValueBoundGetAssetKind ( leftValue ) ;
160+ const rightKind = assertValueBoundGetAssetKind ( rightValueBound , leftKind ) ;
161+ leftKind === rightKind ||
162+ Fail `The left ${ leftAmount } and right amount ${ rightAmountBound } had different assetKinds: ${ q ( leftKind ) } vs ${ q ( rightKind ) } ` ;
163+ return helpers [ leftKind ] ;
152164} ;
153165
154166/**
155167 * @template {AssetKind} K
156168 * @param {MathHelpers<AssetValueForKind<K>> } h
157169 * @param {Amount<K> } leftAmount
158- * @param {Amount <K> } rightAmount
170+ * @param {AmountBound <K> } rightAmountBound
159171 * @returns {[K, K] }
160172 */
161- const coerceLR = ( h , leftAmount , rightAmount ) => {
173+ const coerceLR = ( h , leftAmount , rightAmountBound ) => {
162174 // @ts -expect-error could be arbitrary subtype
163- return [ h . doCoerce ( leftAmount . value ) , h . doCoerce ( rightAmount . value ) ] ;
175+ return [ h . doCoerce ( leftAmount . value ) , h . doCoerce ( rightAmountBound . value ) ] ;
164176} ;
165177
166178/**
167- * Returns true if the leftAmount is greater than or equal to the rightAmount.
168- * The notion of "greater than or equal to" depends on the kind of amount, as
169- * defined by the MathHelpers. For example, whether rectangle A is greater than
170- * rectangle B depends on whether rectangle A includes rectangle B as defined by
171- * the logic in MathHelpers.
179+ * Returns true if the leftAmount is greater than or equal to the
180+ * rightAmountBound. The notion of "greater than or equal to" depends on the
181+ * kind of amount, as defined by the MathHelpers. For example, whether rectangle
182+ * A is greater than rectangle B depends on whether rectangle A includes
183+ * rectangle B as defined by the logic in MathHelpers.
172184 *
173185 * @template {AssetKind} K
174186 * @param {Amount<K> } leftAmount
175- * @param {Amount <K> } rightAmount
187+ * @param {AmountBound <K> } rightAmountBound
176188 * @param {Brand<K> } [brand]
177189 * @returns {boolean }
178190 */
179- const isGTE = ( leftAmount , rightAmount , brand = undefined ) => {
180- const h = checkLRAndGetHelpers ( leftAmount , rightAmount , brand ) ;
181- return h . doIsGTE ( ...coerceLR ( h , leftAmount , rightAmount ) ) ;
191+ const isGTE = ( leftAmount , rightAmountBound , brand = undefined ) => {
192+ const h = checkLRAndGetHelpers ( leftAmount , rightAmountBound , brand ) ;
193+ return h . doIsGTE ( ...coerceLR ( h , leftAmount , rightAmountBound ) ) ;
182194} ;
183195
184196/**
@@ -215,7 +227,7 @@ export const AmountMath = {
215227 */
216228 make : ( brand , allegedValue ) => {
217229 assertRemotable ( brand , 'brand' ) ;
218- const h = assertValueGetHelpers ( allegedValue ) ;
230+ const h = assertValueBoundGetHelpers ( allegedValue ) ;
219231 const value = h . doCoerce ( allegedValue ) ;
220232 // @ts -expect-error cast
221233 return harden ( { brand, value } ) ;
@@ -275,7 +287,7 @@ export const AmountMath = {
275287 makeEmptyFromAmount : amount => {
276288 assertRecord ( amount , 'amount' ) ;
277289 const { brand, value } = amount ;
278- const assetKind = assertValueGetAssetKind ( value ) ;
290+ const assetKind = assertValueBoundGetAssetKind ( value ) ;
279291 // @ts -expect-error different subtype
280292 return AmountMath . makeEmpty ( brand , assetKind ) ;
281293 } ,
@@ -291,7 +303,7 @@ export const AmountMath = {
291303 const { brand : allegedBrand , value } = amount ;
292304 assertRemotable ( allegedBrand , 'brand' ) ;
293305 optionalBrandCheck ( allegedBrand , brand ) ;
294- const h = assertValueGetHelpers ( value ) ;
306+ const h = assertValueBoundGetHelpers ( value ) ;
295307 return h . doIsEmpty ( h . doCoerce ( value ) ) ;
296308 } ,
297309 isGTE,
@@ -387,6 +399,6 @@ harden(AmountMath);
387399export const getAssetKind = amount => {
388400 assertRecord ( amount , 'amount' ) ;
389401 const { value } = amount ;
390- return assertValueGetAssetKind ( value ) ;
402+ return assertValueBoundGetAssetKind ( value ) ;
391403} ;
392404harden ( getAssetKind ) ;
0 commit comments