11import { q , Fail } from '@endo/errors' ;
2- import { passStyleOf , assertRemotable , assertRecord } from '@endo/marshal' ;
2+ import { assertRemotable , assertRecord } from '@endo/pass-style' ;
3+ import { isKey , kindOf , mustMatch } 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' ;
9+ import {
10+ AmountBoundShape ,
11+ AmountShape ,
12+ AmountValueHasBoundShape ,
13+ } from './typeGuards.js' ;
914
1015/**
11- * @import {CopyBag, CopySet} from '@endo/patterns';
12- * @import {Amount, AssetValueForKind, Brand, CopyBagAmount, CopySetAmount, MathHelpers, NatAmount, NatValue, SetAmount, SetValue} from './types.js';
16+ * @import {Passable} from '@endo/pass-style'
17+ * @import {Key, CopyBag, CopySet} from '@endo/patterns';
18+ * @import {Amount, AmountBound, AmountValue, AssetValueForKind, Brand, CopyBagAmount, CopySetAmount, MathHelpers, NatAmount, NatValue, SetAmount, SetValue} from './types.js';
1319 */
1420
1521// NB: AssetKind is both a constant for enumerated values and a type for those values.
@@ -75,39 +81,42 @@ const helpers = {
7581 copyBag : copyBagMathHelpers ,
7682} ;
7783
78- /** @type {(value: unknown) => 'nat' | 'set' | 'copySet' | 'copyBag' } } */
84+ /**
85+ * @template {AssetKind} K=AssetKind
86+ * @template {Key} M=Key
87+ * @template {AssetValueForKind<K, M>} V=AssetValueForKind<K, M>
88+ * @param {V } value
89+ * @returns {AssetKind }
90+ */
7991const 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' ;
92+ const kind = kindOf ( value ) ;
93+ switch ( kind ) {
94+ case 'bigint' : {
95+ return 'nat' ;
96+ }
97+ case 'copyArray' : {
98+ return 'set' ;
99+ }
100+ case 'copySet' :
101+ case 'copyBag' : {
102+ return kind ;
103+ }
104+ default : {
105+ throw Fail `value ${ value } must be an AmountValue, not ${ q ( kind ) } ` ;
106+ }
89107 }
90- if ( matches ( value , M . bag ( ) ) ) {
91- return 'copyBag' ;
92- }
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 {AssetKind} K=AssetKind
116+ * @template {Key} M=Key
117+ * @template {AssetValueForKind<K, M>} V=AssetValueForKind<K, M>
109118 * @param {V } value
110- * @returns {MathHelpers<V> }
119+ * @returns {MathHelpers<K, M, V> }
111120 */
112121export const assertValueGetHelpers = value =>
113122 // @ts -expect-error cast
@@ -125,35 +134,38 @@ const optionalBrandCheck = (allegedBrand, brand) => {
125134} ;
126135
127136/**
128- * @template {AssetKind} K
137+ * @template {AssetKind} K=AssetKind
138+ * @template {Key} M=Key
139+ * @template {AssetValueForKind<K, M>} V=AssetValueForKind<K, M>
129140 * @param {Amount<K> } leftAmount
130141 * @param {Amount<K> } rightAmount
131142 * @param {Brand<K> | undefined } brand
132- * @returns {MathHelpers<any > }
143+ * @returns {MathHelpers<K, M, V > }
133144 */
134145const checkLRAndGetHelpers = ( leftAmount , rightAmount , brand = undefined ) => {
135- assertRecord ( leftAmount , 'leftAmount' ) ;
136- assertRecord ( rightAmount , 'rightAmount' ) ;
137- const { value : leftValue , brand : leftBrand } = leftAmount ;
138- const { value : rightValue , brand : rightBrand } = rightAmount ;
139- assertRemotable ( leftBrand , 'leftBrand' ) ;
140- assertRemotable ( rightBrand , 'rightBrand' ) ;
146+ mustMatch ( leftAmount , AmountShape , 'left amount' ) ;
147+ mustMatch ( rightAmount , AmountShape , 'right amount' ) ;
148+ const { brand : leftBrand , value : leftValue } = leftAmount ;
149+ const { brand : rightBrand , value : rightValue } = rightAmount ;
141150 optionalBrandCheck ( leftBrand , brand ) ;
142151 optionalBrandCheck ( rightBrand , brand ) ;
143152 leftBrand === rightBrand ||
144153 Fail `Brands in left ${ q ( leftBrand ) } and right ${ q (
145154 rightBrand ,
146155 ) } 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 ;
156+ const leftKind = assertValueGetAssetKind ( leftValue ) ;
157+ const rightKind = assertValueGetAssetKind ( rightValue ) ;
158+ leftKind === rightKind ||
159+ Fail `The left ${ leftAmount } and right amounts ${ rightAmount } had different assetKinds: ${ q ( leftKind ) } vs ${ q ( rightKind ) } ` ;
160+ // @ts -expect-error cast
161+ return helpers [ leftKind ] ;
152162} ;
153163
154164/**
155- * @template {AssetKind} K
156- * @param {MathHelpers<AssetValueForKind<K>> } h
165+ * @template {AssetKind} K=AssetKind
166+ * @template {Key} M=Key
167+ * @template {AssetValueForKind<K, M>} V=AssetValueForKind<K, M>
168+ * @param {MathHelpers<K, M, V> } h
157169 * @param {Amount<K> } leftAmount
158170 * @param {Amount<K> } rightAmount
159171 * @returns {[K, K] }
@@ -164,21 +176,42 @@ const coerceLR = (h, leftAmount, rightAmount) => {
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+ if ( isKey ( rightAmountBound ) ) {
193+ const rightAmount = /** @type {Amount<K> } */ ( rightAmountBound ) ;
194+ const h = checkLRAndGetHelpers ( leftAmount , rightAmount , brand ) ;
195+ // @ts -expect-error cast?
196+ return h . doIsGTE ( ...coerceLR ( h , leftAmount , rightAmount ) ) ;
197+ }
198+ mustMatch ( leftAmount , AmountShape , 'left amount' ) ;
199+ mustMatch ( rightAmountBound , AmountBoundShape , 'right amount bound' ) ;
200+ const { brand : leftBrand , value : leftValue } = leftAmount ;
201+ const { brand : rightBrand , value : rightValueHasBound } = rightAmountBound ;
202+ optionalBrandCheck ( leftBrand , brand ) ;
203+ optionalBrandCheck ( rightBrand , brand ) ;
204+ leftBrand === rightBrand ||
205+ Fail `Brands in left ${ q ( leftBrand ) } and right ${ q (
206+ rightBrand ,
207+ ) } should match but do not`;
208+ const leftKind = assertValueGetAssetKind ( leftValue ) ;
209+ // If it were anything else, it would have been a Key and so taken care of
210+ // in the first case above.
211+ mustMatch ( rightValueHasBound , AmountValueHasBoundShape , 'right value bound' ) ;
212+ const h = helpers [ leftKind ] ;
213+ // @ts -expect-error cast?
214+ return h . doIsGTE ( h . doCoerce ( leftValue ) , rightValueHasBound ) ;
182215} ;
183216
184217/**
@@ -307,6 +340,7 @@ export const AmountMath = {
307340 */
308341 isEqual : ( leftAmount , rightAmount , brand = undefined ) => {
309342 const h = checkLRAndGetHelpers ( leftAmount , rightAmount , brand ) ;
343+ // @ts -expect-error cast?
310344 return h . doIsEqual ( ...coerceLR ( h , leftAmount , rightAmount ) ) ;
311345 } ,
312346 /**
@@ -324,6 +358,7 @@ export const AmountMath = {
324358 */
325359 add : ( leftAmount , rightAmount , brand = undefined ) => {
326360 const h = checkLRAndGetHelpers ( leftAmount , rightAmount , brand ) ;
361+ // @ts -expect-error cast?
327362 const value = h . doAdd ( ...coerceLR ( h , leftAmount , rightAmount ) ) ;
328363 // @ts -expect-error different subtype
329364 return harden ( { brand : leftAmount . brand , value } ) ;
@@ -344,6 +379,7 @@ export const AmountMath = {
344379 */
345380 subtract : ( leftAmount , rightAmount , brand = undefined ) => {
346381 const h = checkLRAndGetHelpers ( leftAmount , rightAmount , brand ) ;
382+ // @ts -expect-error cast?
347383 const value = h . doSubtract ( ...coerceLR ( h , leftAmount , rightAmount ) ) ;
348384 // @ts -expect-error different subtype
349385 return harden ( { brand : leftAmount . brand , value } ) ;
0 commit comments