diff --git a/packages/zoe/src/cleanProposal.js b/packages/zoe/src/cleanProposal.js index 69de51d51c2..94b5ffc4052 100644 --- a/packages/zoe/src/cleanProposal.js +++ b/packages/zoe/src/cleanProposal.js @@ -1,9 +1,9 @@ import { assert, q, Fail } from '@agoric/assert'; import { AmountMath, getAssetKind } from '@agoric/ertp'; +import { objectMap } from '@agoric/internal'; import { assertRecord } from '@endo/marshal'; import { assertKey, assertPattern, mustMatch, isKey } from '@agoric/store'; import { FullProposalShape } from './typeGuards.js'; -import { arrayToObj } from './objArrayConversion.js'; import './internal-types.js'; @@ -56,12 +56,10 @@ export const coerceAmountPatternKeywordRecord = ( allegedAmountKeywordRecord, getAssetKindByBrand, ) => { - const keywords = cleanKeywords(allegedAmountKeywordRecord); - - const amounts = Object.values(allegedAmountKeywordRecord); - // Check that each value can be coerced using the AmountMath - // indicated by brand. `AmountMath.coerce` throws if coercion fails. - const coercedAmounts = amounts.map(amount => { + cleanKeywords(allegedAmountKeywordRecord); + return objectMap(allegedAmountKeywordRecord, amount => { + // Check that each value can be coerced using the AmountMath + // indicated by brand. `AmountMath.coerce` throws if coercion fails. if (isKey(amount)) { const brandAssetKind = getAssetKindByBrand(amount.brand); const assetKind = getAssetKind(amount); @@ -75,9 +73,6 @@ export const coerceAmountPatternKeywordRecord = ( return amount; } }); - - // Recreate the amountKeywordRecord with coercedAmounts. - return harden(arrayToObj(coercedAmounts, keywords)); }; export const coerceAmountKeywordRecord = ( diff --git a/packages/zoe/src/issuerStorage.js b/packages/zoe/src/issuerStorage.js index af14c0b30b2..b49bbc03555 100644 --- a/packages/zoe/src/issuerStorage.js +++ b/packages/zoe/src/issuerStorage.js @@ -1,7 +1,7 @@ +import { deeplyFulfilledObject, objectMap } from '@agoric/internal'; import { provideDurableWeakMapStore } from '@agoric/vat-data'; import { E } from '@endo/eventual-send'; -import { arrayToObj } from './objArrayConversion.js'; import { cleanKeywords } from './cleanProposal.js'; import { makeIssuerRecord } from './issuerRecord.js'; @@ -124,11 +124,6 @@ export const provideIssuerStorage = zcfBaggage => { return getByBrand(brand); }; - const storeIssuers = issuers => { - assertInstantiated(); - return Promise.all(issuers.map(storeIssuer)); - }; - /** @type {GetAssetKindByBrand} */ const getAssetKindByBrand = brand => { assertInstantiated(); @@ -143,28 +138,14 @@ export const provideIssuerStorage = zcfBaggage => { */ const storeIssuerKeywordRecord = async uncleanIssuerKeywordRecord => { assertInstantiated(); - const keywords = cleanKeywords(uncleanIssuerKeywordRecord); - const issuerPs = keywords.map( - keyword => uncleanIssuerKeywordRecord[keyword], + cleanKeywords(uncleanIssuerKeywordRecord); + const issuerRecordPs = objectMap(uncleanIssuerKeywordRecord, issuerP => + storeIssuer(issuerP), ); - // The issuers may not have been seen before, so we must wait for the - // issuer records to be available synchronously - const issuerRecords = await storeIssuers(issuerPs); - // AWAIT /// - - const issuers = arrayToObj( - issuerRecords.map(record => record.issuer), - keywords, - ); - const brands = arrayToObj( - issuerRecords.map(record => record.brand), - keywords, - ); - - return harden({ - issuers, - brands, - }); + const issuerRecords = await deeplyFulfilledObject(issuerRecordPs); + const issuers = objectMap(issuerRecords, ({ issuer }) => issuer); + const brands = objectMap(issuerRecords, ({ brand }) => brand); + return harden({ issuers, brands }); }; /** diff --git a/packages/zoe/src/objArrayConversion.js b/packages/zoe/src/objArrayConversion.js deleted file mode 100644 index ce2860d3978..00000000000 --- a/packages/zoe/src/objArrayConversion.js +++ /dev/null @@ -1,35 +0,0 @@ -import { assert, Fail, q } from '@agoric/assert'; - -/** - * @typedef {bigint|boolean|null|number|string|symbol|undefined} Primitive - * @typedef {string|number|symbol} PropertyName - */ - -/** - * @template T - * @template {PropertyName} U - * @param {T[]} array - * @param {U[]} keys - */ -export const arrayToObj = (array, keys) => { - array.length === keys.length || Fail`array and keys must be of equal length`; - const obj = - /** @type {Record} */ - ({}); - keys.forEach((key, i) => (obj[key] = array[i])); - return harden(obj); -}; - -/** - * Assert all values from `part` appear in `whole`. - * - * @param {string[]} whole - * @param {string[]} part - */ -export const assertSubset = (whole, part) => { - part.forEach(key => { - assert.typeof(key, 'string'); - whole.includes(key) || - Fail`key ${q(key)} was not one of the expected keys ${q(whole)}`; - }); -}; diff --git a/packages/zoe/src/zoeService/escrowStorage.js b/packages/zoe/src/zoeService/escrowStorage.js index fa717d74fef..357ca9b880f 100644 --- a/packages/zoe/src/zoeService/escrowStorage.js +++ b/packages/zoe/src/zoeService/escrowStorage.js @@ -1,14 +1,13 @@ import { AmountMath } from '@agoric/ertp'; import { E } from '@endo/eventual-send'; import { q, Fail } from '@agoric/assert'; -import { objectMap } from '@agoric/internal'; +import { deeplyFulfilledObject, objectMap } from '@agoric/internal'; import { provideDurableWeakMapStore } from '@agoric/vat-data'; /// import './internal-types.js'; import { cleanKeywords } from '../cleanProposal.js'; -import { arrayToObj } from '../objArrayConversion.js'; /** * Store the pool purses whose purpose is to escrow assets, with one @@ -76,7 +75,6 @@ export const provideEscrowStorage = baggage => { const depositPayments = async (proposal, payments) => { const { give, want } = proposal; const giveKeywords = Object.keys(give); - const wantKeywords = Object.keys(want); const paymentKeywords = cleanKeywords(payments); // Assert that all of the payment keywords are present in the give @@ -91,35 +89,30 @@ export const provideEscrowStorage = baggage => { )}`; }); - const proposalKeywords = harden([...giveKeywords, ...wantKeywords]); - - // If any of these deposits hang or fail, then depositPayments + // If any of these deposits hang or fail, then this `await` also // hangs or fails, the offer does not succeed, and any funds that // were deposited into the pool purses are lost. We have a ticket // for giving the user a refund of what was already deposited, and // offer safety and payout liveness are still meaningful as long // as issuers are well-behaved. For more, see // https://github.com/Agoric/agoric-sdk/issues/1271 - const amountsDeposited = await Promise.all( - giveKeywords.map(keyword => { - payments[keyword] !== undefined || - Fail`The ${q( - keyword, - )} keyword in proposal.give did not have an associated payment in the paymentKeywordRecord, which had keywords: ${q( - paymentKeywords, - )}`; - return doDepositPayment(payments[keyword], give[keyword]); - }), - ); - - const emptyAmountsForWantKeywords = wantKeywords.map(keyword => - AmountMath.makeEmptyFromAmount(want[keyword]), - ); + const depositPs = objectMap(give, (amount, keyword) => { + payments[keyword] !== undefined || + Fail`The ${q( + keyword, + )} keyword in proposal.give did not have an associated payment in the paymentKeywordRecord, which had keywords: ${q( + paymentKeywords, + )}`; + return doDepositPayment(payments[keyword], amount); + }); + const deposits = await deeplyFulfilledObject(depositPs); - const initialAllocation = arrayToObj( - [...amountsDeposited, ...emptyAmountsForWantKeywords], - proposalKeywords, - ); + const initialAllocation = harden({ + ...objectMap(want, amount => AmountMath.makeEmptyFromAmount(amount)), + // Deposits should win in case of overlapping give/want keywords + // (which are not allowed as of 2024-01). + ...deposits, + }); return initialAllocation; }; diff --git a/packages/zoe/test/unitTests/test-objArrayConversion.js b/packages/zoe/test/unitTests/test-objArrayConversion.js deleted file mode 100644 index 68b8edaa374..00000000000 --- a/packages/zoe/test/unitTests/test-objArrayConversion.js +++ /dev/null @@ -1,24 +0,0 @@ -import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; - -import { arrayToObj } from '../../src/objArrayConversion.js'; - -test('arrayToObj', t => { - t.plan(3); - const keywords = ['X', 'Y']; - const array = [1, 2]; - t.deepEqual(arrayToObj(array, keywords), { X: 1, Y: 2 }); - - const keywords2 = ['X', 'Y', 'Z']; - t.throws( - () => arrayToObj(array, keywords2), - { message: 'array and keys must be of equal length' }, - `unequal length should throw`, - ); - - const array2 = [4, 5, 2]; - t.throws( - () => arrayToObj(array2, keywords), - { message: 'array and keys must be of equal length' }, - `unequal length should throw`, - ); -});