From e1d3664effbce02598aed96a62f0d5b55647e105 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Fri, 1 Jan 2021 22:23:17 -0800 Subject: [PATCH] fix: backported from #2159 --- packages/ERTP/src/types.js | 10 +- .../zoe/src/contractSupport/zoeHelpers.js | 112 +++++++++++++++--- packages/zoe/src/objArrayConversion.js | 2 +- .../unitTests/contractSupport/test-offerTo.js | 3 +- .../contractSupport/test-withdrawFrom.js | 3 +- .../unitTests/contracts/test-coveredCall.js | 4 +- .../contracts/test-multipoolAutoswap.js | 3 +- .../test/unitTests/zcf/test-zoeHelpersWZcf.js | 3 +- 8 files changed, 111 insertions(+), 29 deletions(-) diff --git a/packages/ERTP/src/types.js b/packages/ERTP/src/types.js index 94afa7d4d859..775fed8de8a2 100644 --- a/packages/ERTP/src/types.js +++ b/packages/ERTP/src/types.js @@ -12,7 +12,10 @@ */ /** - * @typedef {Ground} Amount + * TODO Want to say typedef {Object & Ground} Amount, but then Amount seems to + * be typed as "any" + * + * @typedef {Object} 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 @@ -37,7 +40,10 @@ */ /** - * @typedef {Amount & Pattern} AmountPattern + * TODO Want to say typedef {Object & Pattern} AmountPattern, but then + * AmountPattern seems to be typed as "any" + * + * @typedef {Object} AmountPattern * TODO explain * * @property {Brand} brand diff --git a/packages/zoe/src/contractSupport/zoeHelpers.js b/packages/zoe/src/contractSupport/zoeHelpers.js index cd50a2cbfdae..14281f362ff0 100644 --- a/packages/zoe/src/contractSupport/zoeHelpers.js +++ b/packages/zoe/src/contractSupport/zoeHelpers.js @@ -1,8 +1,8 @@ // @ts-check import '../../exported'; -import { assert, details } from '@agoric/assert'; -import { sameStructure } from '@agoric/same-structure'; +import { assert, details, quote as q } from '@agoric/assert'; +import { sameStructure, isGround } from '@agoric/same-structure'; import { E } from '@agoric/eventual-send'; import { makePromiseKit } from '@agoric/promise-kit'; @@ -15,6 +15,12 @@ export const defaultAcceptanceMsg = `The offer has been accepted. Once the contr const getKeysSorted = obj => harden(Object.getOwnPropertyNames(obj || {}).sort()); +/** + * @typedef FromToAllocations + * @property {Allocation} from + * @property {Allocation} to + */ + /** * Given toGains (an AmountKeywordRecord), and allocations (a pair, * 'to' and 'from', of Allocations), all the entries in @@ -32,10 +38,6 @@ const getKeysSorted = obj => * toGains. Note that the total amounts should always be equal; it * is the keywords that might be different. * @returns {FromToAllocations} allocations - new allocations - * - * @typedef FromToAllocations - * @property {Allocation} from - * @property {Allocation} to */ const calcNewAllocations = ( zcf, @@ -152,11 +154,12 @@ export const trade = ( left.losses, )); } catch (err) { - const newErr = new Error( - `The trade between left ${left} and right ${right} failed.`, + // TODO consider revising once we can use assert.error + throw assert.fail( + details`The trade between left ${q(left)} and right ${q( + right, + )} failed due to ${err}.`, ); - assert.note(newErr, details`due to ${err}`); - throw newErr; } // Check whether reallocate would error before calling. If @@ -178,8 +181,10 @@ export const trade = ( if (!offerSafeForRight) { console.log(`offer not safe for right`); } - throw new Error( - `The trade between left ${left} and right ${right} failed offer safety. Please check the log for more information`, + assert.fail( + details`The trade between left ${q(left)} and right ${q( + right, + )} failed offer safety. Please check the log for more information`, ); } @@ -195,6 +200,57 @@ export const trade = ( } }; +/** + * @param {ContractFacet} zcf + * @param {ZCFSeat} fromSeat + * @param {ZCFSeat} toSeat + * @param {string} fromHasExitedMsg + * @param {string} toHasExitedMsg + * @returns {AmountKeywordRecord} + */ +const findFromOtherSeat = ( + zcf, + fromSeat, + toSeat, + fromHasExitedMsg, + toHasExitedMsg, +) => { + assert(!fromSeat.hasExited(), fromHasExitedMsg); + assert(!toSeat.hasExited(), toHasExitedMsg); + /** @type {AmountPatternKeywordRecord} */ + const amountPatternKeywordRecord = toSeat.getProposal().want; + /** @type {AmountKeywordRecord} */ + const fromSeatAmountKeywordRecord = fromSeat.getCurrentAllocation(); + + /** + * @param {[Keyword, AmountPattern]} pair + * @returns {[Keyword, Amount]} + */ + const findAmountPair = pair => { + const [keyword, amountPattern] = pair; + const fromSeatAmount = fromSeatAmountKeywordRecord[keyword]; + if (fromSeatAmount === undefined) { + assert( + isGround(amountPattern), + details`Unmatched wants must be ground ${amountPattern}`, + ); + // A ground AmountPattern is a valid Amount + return [keyword, amountPattern]; + } + const amountMath = zcf.getAmountMath(amountPattern.brand); + const split = amountMath.frugalSplit(amountPattern, fromSeatAmount); + // If we are trying to transfer an amount but can't find what + // should be transferred, we should throw + assert( + split !== undefined, + details`The trade between fromSeat ${fromSeat} and toSeat ${toSeat} failed because ${amountPattern} was not found.`, + ); + return [keyword, split.matched]; + }; + + return objectMap(amountPatternKeywordRecord, findAmountPair); +}; + /** @type {Swap} */ export const swap = ( zcf, @@ -208,11 +264,23 @@ export const swap = ( zcf, { seat: leftSeat, - gains: leftSeat.getProposal().want, // TODO + gains: findFromOtherSeat( + zcf, + rightSeat, + leftSeat, + rightHasExitedMsg, + leftHasExitedMsg, + ), }, { seat: rightSeat, - gains: rightSeat.getProposal().want, // TODO + gains: findFromOtherSeat( + zcf, + leftSeat, + rightSeat, + leftHasExitedMsg, + rightHasExitedMsg, + ), }, leftHasExitedMsg, rightHasExitedMsg, @@ -247,12 +315,24 @@ export const swapExact = ( zcf, { seat: leftSeat, - gains: leftSeat.getProposal().want, // TODO + gains: findFromOtherSeat( + zcf, + rightSeat, + leftSeat, + rightHasExitedMsg, + leftHasExitedMsg, + ), losses: leftSeat.getProposal().give, }, { seat: rightSeat, - gains: rightSeat.getProposal().want, // TODO + gains: findFromOtherSeat( + zcf, + leftSeat, + rightSeat, + leftHasExitedMsg, + rightHasExitedMsg, + ), losses: rightSeat.getProposal().give, }, leftHasExitedMsg, diff --git a/packages/zoe/src/objArrayConversion.js b/packages/zoe/src/objArrayConversion.js index 8315d6121da9..e3c03634d418 100644 --- a/packages/zoe/src/objArrayConversion.js +++ b/packages/zoe/src/objArrayConversion.js @@ -43,7 +43,7 @@ export const assertSubset = (whole, part) => { /** * @template T, U - * @template {keyof T} K + * @template {string} K * @param {Record} original * @param {(pair: [K, T]) => [K, U]} mapPairFn * @returns {Record} diff --git a/packages/zoe/test/unitTests/contractSupport/test-offerTo.js b/packages/zoe/test/unitTests/contractSupport/test-offerTo.js index 84cf17b8c6ea..f1b5da9df29a 100644 --- a/packages/zoe/test/unitTests/contractSupport/test-offerTo.js +++ b/packages/zoe/test/unitTests/contractSupport/test-offerTo.js @@ -233,8 +233,7 @@ test(`offerTo - violates offer safety of fromSeat`, async t => { toSeatContractA, ), { - message: - 'The trade between left [object Object] and right [object Object] failed offer safety. Please check the log for more information', + message: /The trade between left .* and right .* failed offer safety. Please check the log for more information/, }, ); diff --git a/packages/zoe/test/unitTests/contractSupport/test-withdrawFrom.js b/packages/zoe/test/unitTests/contractSupport/test-withdrawFrom.js index 4a627bd38d01..247120bff614 100644 --- a/packages/zoe/test/unitTests/contractSupport/test-withdrawFrom.js +++ b/packages/zoe/test/unitTests/contractSupport/test-withdrawFrom.js @@ -88,8 +88,7 @@ test(`withdrawFromSeat - violates offerSafety`, async t => { await t.throwsAsync( withdrawFromSeat(zcf, zcfSeat, { B: bucks(4) }), { - message: - 'The trade between left [object Object] and right [object Object] failed offer safety. Please check the log for more information', + message: /The trade between left .* and right .* failed offer safety. Please check the log for more information/, }, `withdrawFrom can't violate offerSafety`, ); diff --git a/packages/zoe/test/unitTests/contracts/test-coveredCall.js b/packages/zoe/test/unitTests/contracts/test-coveredCall.js index 03e440ea1f08..a1aca0e7fb25 100644 --- a/packages/zoe/test/unitTests/contracts/test-coveredCall.js +++ b/packages/zoe/test/unitTests/contracts/test-coveredCall.js @@ -514,8 +514,8 @@ test('zoe - coveredCall with swap for invitation', async t => { // TODO BUG The commented out line with optionAmountPattern is the // one we want, but atomicSwap calls swap with the want pattern // as the gains, which are assumed to be amounts, not amount patterns. - // want: { Asset: optionAmountPattern }, - want: { Asset: optionAmount }, + want: { Asset: optionAmountPattern }, + // want: { Asset: optionAmount }, give: { Price: bucks(1) }, }); diff --git a/packages/zoe/test/unitTests/contracts/test-multipoolAutoswap.js b/packages/zoe/test/unitTests/contracts/test-multipoolAutoswap.js index c6c73fccd6fb..9f7362dff44a 100644 --- a/packages/zoe/test/unitTests/contracts/test-multipoolAutoswap.js +++ b/packages/zoe/test/unitTests/contracts/test-multipoolAutoswap.js @@ -1193,8 +1193,7 @@ test('multipoolAutoSwap jig - removeLiquidity ask for too much', async t => { payment, ); await t.throwsAsync(() => seat.getOfferResult(), { - message: - 'The trade between left [object Object] and right [object Object] failed offer safety. Please check the log for more information', + message: /The trade between left .* and right .* failed offer safety. Please check the log for more information/, }); }); diff --git a/packages/zoe/test/unitTests/zcf/test-zoeHelpersWZcf.js b/packages/zoe/test/unitTests/zcf/test-zoeHelpersWZcf.js index 1c17ee42b72f..f5881a980b24 100644 --- a/packages/zoe/test/unitTests/zcf/test-zoeHelpersWZcf.js +++ b/packages/zoe/test/unitTests/zcf/test-zoeHelpersWZcf.js @@ -91,8 +91,7 @@ test(`zoeHelper with zcf - swap no match`, async t => { t.throws( () => swap(zcf, aZcfSeat, bZcfSeat), { - message: - 'The trade between left [object Object] and right [object Object] failed.', + message: /The trade between fromSeat .* and toSeat .* failed because .* was not found/, }, 'mismatched offers', );