Skip to content

Commit

Permalink
Merge branch 'master' into ta/workspace-wildcard
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Feb 17, 2022
2 parents fa6235c + bca743a commit 8bf051e
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 120 deletions.
160 changes: 144 additions & 16 deletions packages/ERTP/src/amountMath.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,31 @@ const helpers = {
copyBag: copyBagMathHelpers,
};

/** @type {(value: AmountValue) => AssetKind} */
/**
* @template {AmountValue} V
* @type {(value: V) =>
* V extends NatValue ? 'nat' :
* V extends SetValue ? 'set' :
* V extends CopySetValue ? 'copySet' :
* V extends CopyBagValue ? 'copyBag' :
* never}
*/
const assertValueGetAssetKind = value => {
const passStyle = passStyleOf(value);
if (passStyle === 'bigint') {
// @ts-expect-error cast
return 'nat';
}
if (passStyle === 'copyArray') {
// @ts-expect-error cast
return 'set';
}
if (matches(value, M.set())) {
// @ts-expect-error cast
return 'copySet';
}
if (matches(value, M.bag())) {
// @ts-expect-error cast
return 'copyBag';
}
assert.fail(
Expand All @@ -115,10 +127,12 @@ const assertValueGetAssetKind = value => {
*
* Made available only for testing, but it is harmless for other uses.
*
* @param {AmountValue} value
* @returns {MathHelpers<*>}
* @template {AmountValue} V
* @param {V} value
* @returns {MathHelpers<V>}
*/
export const assertValueGetHelpers = value =>
// @ts-expect-error cast
helpers[assertValueGetAssetKind(value)];

/** @type {(allegedBrand: Brand, brand?: Brand) => void} */
Expand All @@ -134,8 +148,9 @@ const optionalBrandCheck = (allegedBrand, brand) => {
};

/**
* @param {Amount} leftAmount
* @param {Amount} rightAmount
* @template {AmountValue} [V=AmountValue]
* @param {Amount<V>} leftAmount
* @param {Amount<V>} rightAmount
* @param {Brand | undefined} brand
* @returns {MathHelpers<*>}
*/
Expand Down Expand Up @@ -164,25 +179,52 @@ const checkLRAndGetHelpers = (leftAmount, rightAmount, brand = undefined) => {
};

/**
* @template {AmountValue} V
* @param {MathHelpers<AmountValue>} h
* @param {Amount} leftAmount
* @param {Amount} rightAmount
* @returns {[AmountValue, AmountValue]}
* @param {Amount<V>} leftAmount
* @param {Amount<V>} rightAmount
* @returns {[V, V]}
*/
const coerceLR = (h, leftAmount, rightAmount) => {
// @ts-ignore cast (ignore b/c erroring in CI but not my IDE)
return [h.doCoerce(leftAmount.value), h.doCoerce(rightAmount.value)];
};

/** @type {AmountMath} */
/**
* Logic for manipulating amounts.
*
* Amounts are the canonical description of tradable goods. They are manipulated
* by issuers and mints, and represent the goods and currency carried by purses
* and
* payments. They can be used to represent things like currency, stock, and the
* abstract right to participate in a particular exchange.
*/
const AmountMath = {
/**
* Make an amount from a value by adding the brand.
*
* @template {AmountValue} [V=AmountValue]
* @param {Brand} brand
* @param {V extends NatValue ? NatValue : V extends SetValue ? SetValue : V extends CopySetValue ? CopySetValue : V extends CopyBagValue ? CopyBagValue : never} allegedValue
* @returns {Amount<V>}
*/
// allegedValue has a conditional expression for type widening, to prevent V being bound to a a literal like 1n
make: (brand, allegedValue) => {
assertRemotable(brand, 'brand');
const h = assertValueGetHelpers(allegedValue);
// @ts-ignore Needs better typing to express AmountValue to Helpers
// relationship
const value = h.doCoerce(allegedValue);
// @ts-ignore cast (ignore b/c erroring in CI but not my IDE)
return harden({ brand, value });
},
/**
* Make sure this amount is valid enough, and return a corresponding
* valid amount if so.
*
* @template {AmountValue} [V=AmountValue]
* @param {Brand} brand
* @param {Amount<V>} allegedAmount
* @returns {Amount<V>}
*/
coerce: (brand, allegedAmount) => {
assertRemotable(brand, 'brand');
assertRecord(allegedAmount, 'amount');
Expand All @@ -192,58 +234,144 @@ const AmountMath = {
X`The brand in the allegedAmount ${allegedAmount} in 'coerce' didn't match the specified brand ${brand}.`,
);
// Will throw on inappropriate value
// @ts-ignore cast (ignore b/c erroring in CI but not my IDE)
return AmountMath.make(brand, allegedValue);
},
/**
* Extract and return the value.
*
* @template {AmountValue} [V=AmountValue]
* @param {Brand} brand
* @param {Amount<V>} amount
* @returns {V}
*/
getValue: (brand, amount) => AmountMath.coerce(brand, amount).value,
/**
* Return the amount representing an empty amount. This is the
* identity element for MathHelpers.add and MatHelpers.subtract.
*
* @template {AssetKind} K
* @param {Brand} brand
* @param {AssetKind=} assetKind
* @returns {Amount<K extends 'nat' ? NatValue: K extends 'set' ? SetValue: K extends 'copySet' ? CopySetValue: K extends 'copyBag' ? CopyBagValue : never>}
*/
makeEmpty: (brand, assetKind = AssetKind.NAT) => {
assertRemotable(brand, 'brand');
assertAssetKind(assetKind);
const value = helpers[assetKind].doMakeEmpty();
// @ts-ignore cast (ignore b/c erroring in CI but not my IDE)
return harden({ brand, value });
},
/**
* Return the amount representing an empty amount, using another
* amount as the template for the brand and assetKind.
*
* @template {AmountValue} V
* @param {Amount<V>} amount
* @returns {Amount<V>}
*/
makeEmptyFromAmount: amount => {
assertRecord(amount, 'amount');
const { brand, value } = amount;
// @ts-expect-error cast
const assetKind = assertValueGetAssetKind(value);
// @ts-ignore cast (ignore b/c erroring in CI but not my IDE)
return AmountMath.makeEmpty(brand, assetKind);
},
/**
* Return true if the Amount is empty. Otherwise false.
*
* @param {Amount} amount
* @param {Brand=} brand
* @returns {boolean}
*/
isEmpty: (amount, brand = undefined) => {
assertRecord(amount, 'amount');
const { brand: allegedBrand, value } = amount;
assertRemotable(allegedBrand, 'brand');
optionalBrandCheck(allegedBrand, brand);
const h = assertValueGetHelpers(value);
// @ts-ignore Needs better typing to express AmountValue to Helpers relationship
return h.doIsEmpty(h.doCoerce(value));
},
/**
* Returns true if the leftAmount is greater than or equal to the
* rightAmount. For non-scalars, "greater than or equal to" depends
* on the kind of amount, as defined by the MathHelpers. For example,
* whether rectangle A is greater than rectangle B depends on whether rectangle
* A includes rectangle B as defined by the logic in MathHelpers.
*
* @template {AmountValue} [V=AmountValue]
* @param {Amount<V>} leftAmount
* @param {Amount<V>} rightAmount
* @param {Brand=} brand
* @returns {boolean}
*/
isGTE: (leftAmount, rightAmount, brand = undefined) => {
const h = checkLRAndGetHelpers(leftAmount, rightAmount, brand);
// @ts-ignore Needs better typing to express AmountValue to Helpers relationship
return h.doIsGTE(...coerceLR(h, leftAmount, rightAmount));
},
/**
* Returns true if the leftAmount equals the rightAmount. We assume
* that if isGTE is true in both directions, isEqual is also true
*
* @template {AmountValue} [V=AmountValue]
* @param {Amount<V>} leftAmount
* @param {Amount<V>} rightAmount
* @param {Brand=} brand
* @returns {boolean}
*/
isEqual: (leftAmount, rightAmount, brand = undefined) => {
const h = checkLRAndGetHelpers(leftAmount, rightAmount, brand);
// @ts-ignore Needs better typing to express AmountValue to Helpers relationship
return h.doIsEqual(...coerceLR(h, leftAmount, rightAmount));
},
/**
* Returns a new amount that is the union of both leftAmount and rightAmount.
*
* For fungible amount this means adding the values. For other kinds of
* amount, it usually means including all of the elements from both
* left and right.
*
* @template {AmountValue} [V=AmountValue]
* @param {Amount<V>} leftAmount
* @param {Amount<V>} rightAmount
* @param {Brand=} brand
* @returns {Amount<V>}
*/
add: (leftAmount, rightAmount, brand = undefined) => {
const h = checkLRAndGetHelpers(leftAmount, rightAmount, brand);
// @ts-ignore Needs better typing to express AmountValue to Helpers relationship
const value = h.doAdd(...coerceLR(h, leftAmount, rightAmount));
return harden({ brand: leftAmount.brand, value });
},
/**
* Returns a new amount that is the leftAmount minus the rightAmount
* (i.e. everything in the leftAmount that is not in the
* rightAmount). If leftAmount doesn't include rightAmount
* (subtraction results in a negative), throw an error. Because the
* left amount must include the right amount, this is NOT equivalent
* to set subtraction.
*
* @template {AmountValue} [V=AmountValue]
* @param {Amount<V>} leftAmount
* @param {Amount<V>} rightAmount
* @param {Brand=} brand
* @returns {Amount<V>}
*/
subtract: (leftAmount, rightAmount, brand = undefined) => {
const h = checkLRAndGetHelpers(leftAmount, rightAmount, brand);
// @ts-ignore Needs better typing to express AmountValue to Helpers relationship
const value = h.doSubtract(...coerceLR(h, leftAmount, rightAmount));
return harden({ brand: leftAmount.brand, value });
},
};
harden(AmountMath);

/**
*
* @param {Amount} amount
*/
const getAssetKind = amount => {
assertRecord(amount, 'amount');
const { value } = amount;
// @ts-ignore cast (ignore b/c erroring in CI but not my IDE)
return assertValueGetAssetKind(value);
};
harden(getAssetKind);
Expand Down
Loading

0 comments on commit 8bf051e

Please sign in to comment.