UNPKG

@agoric/zoe

Version:

Zoe: the Smart Contract Framework for Offer Enforcement

98 lines (85 loc) 3.14 kB
import { Fail } from '@endo/errors'; import { makeScalarMapStore } from '@agoric/vat-data'; import { assertRightsConserved } from './rightsConservation.js'; import { addToAllocation, subtractFromAllocation } from './allocationMath.js'; /** * @import {MapStore} from '@agoric/swingset-liveslots'; */ /** @typedef {Array<AmountKeywordRecord>} TransactionList */ /** * Convert from a list of transfer descriptions ([fromSeat, toSeat, fromAmount, * toAmount], with many parts optional) to a list of resulting allocations for * each of the seats mentioned. * * @param {Array<TransferPart>} transfers * @returns {[ZCFSeat, AmountKeywordRecord][]} */ export const makeAllocationMap = transfers => { /** @type {MapStore<ZCFSeat, [TransactionList, TransactionList]>} */ const allocations = makeScalarMapStore(); const getAllocations = seat => { if (allocations.has(seat)) { return allocations.get(seat); } /** @type {[TransactionList, TransactionList]} */ const pair = [[], []]; allocations.init(seat, pair); return pair; }; const decrementAllocation = (seat, decrement) => { const [incr, decr] = getAllocations(seat); const newDecr = [...decr, decrement]; allocations.set(seat, [incr, newDecr]); }; const incrementAllocation = (seat, increment) => { const [incr, decr] = getAllocations(seat); const newIncr = [...incr, increment]; allocations.set(seat, [newIncr, decr]); }; for (const [fromSeat, toSeat, fromAmounts, toAmounts] of transfers) { if (fromSeat) { if (!fromAmounts) { throw Fail`Transfer from ${fromSeat} must say how much`; } decrementAllocation(fromSeat, fromAmounts); if (toSeat) { // Conserved transfer between seats if (toAmounts) { // distinct amounts, so we check conservation. assertRightsConserved( Object.values(fromAmounts), Object.values(toAmounts), ); incrementAllocation(toSeat, toAmounts); } else { // fromAmounts will be used for toAmounts as well incrementAllocation(toSeat, fromAmounts); } } else { // Transfer only from fromSeat !toAmounts || Fail`Transfer without toSeat cannot have toAmounts ${toAmounts}`; } } else { toSeat || Fail`Transfer must have at least one of fromSeat or toSeat`; // Transfer only to toSeat !fromAmounts || Fail`Transfer without fromSeat cannot have fromAmounts ${fromAmounts}`; toAmounts || Fail`Transfer to ${toSeat} must say how much`; incrementAllocation(toSeat, toAmounts); } } /** @type {[ZCFSeat,AmountKeywordRecord][]} */ const resultingAllocations = []; for (const [seat, [incrList, decrList]] of allocations.entries()) { let newAlloc = seat.getCurrentAllocation(); for (const incr of incrList) { newAlloc = addToAllocation(newAlloc, incr); } for (const decr of decrList) { newAlloc = subtractFromAllocation(newAlloc, decr); } resultingAllocations.push([seat, newAlloc]); } return resultingAllocations; };