Skip to content

Commit

Permalink
fix(zoe): assert that amountKeywordRecord is a copyRecord
Browse files Browse the repository at this point in the history
BREAKING CHANGE: must harden `amountKeywordRecord` before passing to ZCF objects
  • Loading branch information
katelynsills committed Nov 15, 2021
1 parent 5774824 commit 8b9910e
Show file tree
Hide file tree
Showing 47 changed files with 373 additions and 283 deletions.
9 changes: 5 additions & 4 deletions packages/pegasus/src/pegasus.js
Original file line number Diff line number Diff line change
Expand Up @@ -421,9 +421,10 @@ const makePegasus = (zcf, board, namesByAddress) => {
board,
namesByAddress,
denomUri,
retain: (zcfSeat, amounts) => zcfMint.burnLosses(amounts, zcfSeat),
retain: (zcfSeat, amounts) =>
zcfMint.burnLosses(harden(amounts), zcfSeat),
redeem: (zcfSeat, amounts) => {
zcfMint.mintGains(amounts, zcfSeat);
zcfMint.mintGains(harden(amounts), zcfSeat);
},
});

Expand Down Expand Up @@ -499,8 +500,8 @@ const makePegasus = (zcf, board, namesByAddress) => {
winner,
) => {
// Transfer the amount to our backing seat.
loser.decrementBy({ [loserKeyword]: amount });
winner.incrementBy({ [winnerKeyword]: amount });
loser.decrementBy(harden({ [loserKeyword]: amount }));
winner.incrementBy(harden({ [winnerKeyword]: amount }));
zcf.reallocate(loser, winner);
};

Expand Down
2 changes: 1 addition & 1 deletion packages/treasury/src/burn.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { E } from '@agoric/eventual-send';
*/
export async function paymentFromZCFMint(zcf, zcfMint, amount) {
const { zcfSeat, userSeat } = zcf.makeEmptySeatKit();
zcfMint.mintGains({ Temp: amount }, zcfSeat);
zcfMint.mintGains(harden({ Temp: amount }), zcfSeat);
zcfSeat.exit();
return E(userSeat).getPayout('Temp');
}
12 changes: 8 additions & 4 deletions packages/treasury/src/collectRewardFees.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ export const makeMakeCollectFeesInvitation = (
await E.get(offerTo(zcf, invitation, {}, {}, transferSeat)).deposited;

seat.incrementBy(
feeSeat.decrementBy({ RUN: feeSeat.getAmountAllocated('RUN', runBrand) }),
feeSeat.decrementBy(
harden({ RUN: feeSeat.getAmountAllocated('RUN', runBrand) }),
),
);
seat.incrementBy(
transferSeat.decrementBy({
RUN: transferSeat.getAmountAllocated('RUN', runBrand),
}),
transferSeat.decrementBy(
harden({
RUN: transferSeat.getAmountAllocated('RUN', runBrand),
}),
),
);
const totalTransferred = seat.getStagedAllocation().RUN;

Expand Down
2 changes: 1 addition & 1 deletion packages/treasury/src/liquidation.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function liquidate(

const isUnderwater = !AmountMath.isGTE(runProceedsAmount, runDebt);
const runToBurn = isUnderwater ? runProceedsAmount : runDebt;
burnLosses({ RUN: runToBurn }, liquidationSeat);
burnLosses(harden({ RUN: runToBurn }), liquidationSeat);
vaultKit.liquidated(AmountMath.subtract(runDebt, runToBurn));

// any remaining RUN plus anything else leftover from the sale are refunded
Expand Down
22 changes: 12 additions & 10 deletions packages/treasury/src/stablecoinMachine.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,11 @@ export async function start(zcf, privateArgs) {
*/
function reallocateReward(amount, fromSeat, otherSeat = undefined) {
rewardPoolSeat.incrementBy(
fromSeat.decrementBy({
RUN: amount,
}),
fromSeat.decrementBy(
harden({
RUN: amount,
}),
),
);
if (otherSeat !== undefined) {
zcf.reallocate(rewardPoolSeat, fromSeat, otherSeat);
Expand Down Expand Up @@ -205,13 +207,13 @@ export async function start(zcf, privateArgs) {
// tokens as Liquidity. These governance tokens are held by govSeat
const { zcfSeat: govSeat } = zcf.makeEmptySeatKit();
// TODO this should create the seat for us
govMint.mintGains({ Governance: govAmount }, govSeat);
govMint.mintGains(harden({ Governance: govAmount }), govSeat);

// trade the governance tokens for collateral, putting the
// collateral on Secondary to be positioned for Autoswap
seat.incrementBy(govSeat.decrementBy({ Governance: govAmount }));
seat.decrementBy({ Collateral: collateralIn });
govSeat.incrementBy({ Secondary: collateralIn });
seat.incrementBy(govSeat.decrementBy(harden({ Governance: govAmount })));
seat.decrementBy(harden({ Collateral: collateralIn }));
govSeat.incrementBy(harden({ Secondary: collateralIn }));

zcf.reallocate(govSeat, seat);
// the collateral is now on the temporary seat
Expand All @@ -221,7 +223,7 @@ export async function start(zcf, privateArgs) {

// mint the new RUN to the Central position on the govSeat
// so we can setup the autoswap pool
runMint.mintGains({ Central: runAmount }, govSeat);
runMint.mintGains(harden({ Central: runAmount }), govSeat);

// TODO: check for existing pool, use its price instead of the
// user-provided 'rate'. Or throw an error if it already exists.
Expand Down Expand Up @@ -356,9 +358,9 @@ export async function start(zcf, privateArgs) {
} = zcf.makeEmptySeatKit();
const bootstrapAmount = AmountMath.make(runBrand, bootstrapPaymentValue);
runMint.mintGains(
{
harden({
Bootstrap: bootstrapAmount,
},
}),
bootstrapZCFSeat,
);
bootstrapZCFSeat.exit();
Expand Down
30 changes: 18 additions & 12 deletions packages/treasury/src/vault.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,14 @@ export function makeVaultKit(
// Return any overpayment

const { zcfSeat: burnSeat } = zcf.makeEmptySeatKit();
burnSeat.incrementBy(seat.decrementBy({ RUN: runDebt }));
burnSeat.incrementBy(seat.decrementBy(harden({ RUN: runDebt })));
seat.incrementBy(
vaultSeat.decrementBy({ Collateral: getCollateralAllocated(vaultSeat) }),
vaultSeat.decrementBy(
harden({ Collateral: getCollateralAllocated(vaultSeat) }),
),
);
zcf.reallocate(seat, vaultSeat, burnSeat);
runMint.burnLosses({ RUN: runDebt }, burnSeat);
runMint.burnLosses(harden({ RUN: runDebt }), burnSeat);
seat.exit();
burnSeat.exit();
vaultState = VaultState.CLOSED;
Expand Down Expand Up @@ -276,11 +278,11 @@ export function makeVaultKit(
const proposal = seat.getProposal();
if (proposal.want.Collateral) {
seat.incrementBy(
vaultSeat.decrementBy({ Collateral: proposal.want.Collateral }),
vaultSeat.decrementBy(harden({ Collateral: proposal.want.Collateral })),
);
} else if (proposal.give.Collateral) {
vaultSeat.incrementBy(
seat.decrementBy({ Collateral: proposal.give.Collateral }),
seat.decrementBy(harden({ Collateral: proposal.give.Collateral })),
);
}
}
Expand Down Expand Up @@ -321,14 +323,16 @@ export function makeVaultKit(
function transferRun(seat) {
const proposal = seat.getProposal();
if (proposal.want.RUN) {
seat.incrementBy(vaultSeat.decrementBy({ RUN: proposal.want.RUN }));
seat.incrementBy(
vaultSeat.decrementBy(harden({ RUN: proposal.want.RUN })),
);
} else if (proposal.give.RUN) {
// We don't allow runDebt to be negative, so we'll refund overpayments
const acceptedRun = AmountMath.isGTE(proposal.give.RUN, runDebt)
? runDebt
: proposal.give.RUN;

vaultSeat.incrementBy(seat.decrementBy({ RUN: acceptedRun }));
vaultSeat.incrementBy(seat.decrementBy(harden({ RUN: acceptedRun })));
}
}

Expand Down Expand Up @@ -402,13 +406,13 @@ export function makeVaultKit(

// mint to vaultSeat, then reallocate to reward and client, then burn from
// vaultSeat. Would using a separate seat clarify the accounting?
runMint.mintGains({ RUN: toMint }, vaultSeat);
runMint.mintGains(harden({ RUN: toMint }), vaultSeat);
transferCollateral(clientSeat);
transferRun(clientSeat);
manager.reallocateReward(fee, vaultSeat, clientSeat);

runDebt = newDebt;
runMint.burnLosses({ RUN: runAfter.vault }, vaultSeat);
runMint.burnLosses(harden({ RUN: runAfter.vault }), vaultSeat);

assertVaultHoldsNoRun();

Expand Down Expand Up @@ -445,10 +449,12 @@ export function makeVaultKit(
runDebt = AmountMath.add(wantedRun, fee);
await assertSufficientCollateral(collateralAmount, runDebt);

runMint.mintGains({ RUN: runDebt }, vaultSeat);
runMint.mintGains(harden({ RUN: runDebt }), vaultSeat);

seat.incrementBy(vaultSeat.decrementBy({ RUN: wantedRun }));
vaultSeat.incrementBy(seat.decrementBy({ Collateral: collateralAmount }));
seat.incrementBy(vaultSeat.decrementBy(harden({ RUN: wantedRun })));
vaultSeat.incrementBy(
seat.decrementBy(harden({ Collateral: collateralAmount })),
);
manager.reallocateReward(fee, vaultSeat, seat);

updateUiState();
Expand Down
2 changes: 1 addition & 1 deletion packages/treasury/src/vaultManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ export function makeVaultManager(
);
sortedVaultKits.updateAllDebts();
reschedulePriceCheck();
runMint.mintGains({ RUN: poolIncrement }, poolIncrementSeat);
runMint.mintGains(harden({ RUN: poolIncrement }), poolIncrementSeat);
reallocateReward(poolIncrement, poolIncrementSeat);
}

Expand Down
8 changes: 5 additions & 3 deletions packages/treasury/test/vault-contract-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ export async function start(zcf, privateArgs) {

function reallocateReward(amount, fromSeat, otherSeat) {
stableCoinSeat.incrementBy(
fromSeat.decrementBy({
RUN: amount,
}),
fromSeat.decrementBy(
harden({
RUN: amount,
}),
),
);
if (otherSeat !== undefined) {
zcf.reallocate(stableCoinSeat, fromSeat, otherSeat);
Expand Down
6 changes: 4 additions & 2 deletions packages/zoe/src/cleanProposal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { assert, details as X, q } from '@agoric/assert';
import { mustBeComparable } from '@agoric/same-structure';
import { isNat } from '@agoric/nat';
import { AmountMath, getAssetKind } from '@agoric/ertp';
import { assertRecord } from '@agoric/marshal';
import {
isOnDemandExitRule,
isWaivedExitRule,
Expand Down Expand Up @@ -61,10 +62,11 @@ const cleanKeys = (allowedKeys, record) => {
export const getKeywords = keywordRecord =>
harden(Object.getOwnPropertyNames(keywordRecord));

const coerceAmountKeywordRecord = (
export const coerceAmountKeywordRecord = (
allegedAmountKeywordRecord,
getAssetKindByBrand,
) => {
assertRecord(allegedAmountKeywordRecord, 'amountKeywordRecord');
const keywords = getKeywords(allegedAmountKeywordRecord);
keywords.forEach(assertKeywordName);

Expand All @@ -84,7 +86,7 @@ const coerceAmountKeywordRecord = (
});

// Recreate the amountKeywordRecord with coercedAmounts.
return arrayToObj(coercedAmounts, keywords);
return harden(arrayToObj(coercedAmounts, keywords));
};

export const cleanKeywords = keywordRecord => {
Expand Down
9 changes: 9 additions & 0 deletions packages/zoe/src/contractFacet/zcfSeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Far } from '@agoric/marshal';
import { isOfferSafe } from './offerSafety.js';
import { assertRightsConserved } from './rightsConservation.js';
import { addToAllocation, subtractFromAllocation } from './allocationMath.js';
import { coerceAmountKeywordRecord } from '../cleanProposal.js';

/** @type {CreateSeatManager} */
export const createSeatManager = (
Expand Down Expand Up @@ -303,6 +304,10 @@ export const createSeatManager = (
},
incrementBy: amountKeywordRecord => {
assertActive(zcfSeat);
amountKeywordRecord = coerceAmountKeywordRecord(
amountKeywordRecord,
getAssetKindByBrand,
);
setStagedAllocation(
zcfSeat,
addToAllocation(getStagedAllocation(zcfSeat), amountKeywordRecord),
Expand All @@ -311,6 +316,10 @@ export const createSeatManager = (
},
decrementBy: amountKeywordRecord => {
assertActive(zcfSeat);
amountKeywordRecord = coerceAmountKeywordRecord(
amountKeywordRecord,
getAssetKindByBrand,
);
setStagedAllocation(
zcfSeat,
subtractFromAllocation(
Expand Down
15 changes: 3 additions & 12 deletions packages/zoe/src/contractFacet/zcfZygote.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { AssetKind, AmountMath } from '@agoric/ertp';
import { makeNotifierKit, observeNotifier } from '@agoric/notifier';
import { makePromiseKit } from '@agoric/promise-kit';

import { cleanProposal } from '../cleanProposal.js';
import { cleanProposal, coerceAmountKeywordRecord } from '../cleanProposal.js';
import { evalContractBundle } from './evalContractCode.js';
import { makeExitObj } from './exit.js';
import { makeHandle } from '../makeHandle.js';
Expand Down Expand Up @@ -113,15 +113,6 @@ export const makeZCFZygote = (
return { zcfSeat, userSeat: userSeatPromiseKit.promise };
};

const assertAmountKeywordRecord = (amountKeywordRecord, name) => {
assert.typeof(
amountKeywordRecord,
'object',
X`${name} ${amountKeywordRecord} must be an amountKeywordRecord`,
);
assert(amountKeywordRecord !== null, X`${name} cannot be null`);
};

// A helper for the code shared between MakeZCFMint and RegisterZCFMint
const doMakeZCFMint = async (keyword, zoeMintP) => {
const {
Expand All @@ -148,7 +139,7 @@ export const makeZCFZygote = (
return mintyIssuerRecord;
},
mintGains: (gains, zcfSeat = undefined) => {
assertAmountKeywordRecord(gains, 'gains');
gains = coerceAmountKeywordRecord(gains, getAssetKindByBrand);
if (zcfSeat === undefined) {
zcfSeat = makeEmptySeatKit().zcfSeat;
}
Expand Down Expand Up @@ -186,7 +177,7 @@ export const makeZCFZygote = (
return zcfSeat;
},
burnLosses: (losses, zcfSeat) => {
assertAmountKeywordRecord(losses, 'losses');
losses = coerceAmountKeywordRecord(losses, getAssetKindByBrand);
const totalToBurn = Object.values(losses).reduce(add, empty);
assert(
!zcfSeat.hasExited(),
Expand Down
24 changes: 12 additions & 12 deletions packages/zoe/src/contractSupport/zoeHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ export const satisfies = (zcf, seat, update) => {
/** @type {Swap} */
export const swap = (zcf, leftSeat, rightSeat) => {
try {
rightSeat.decrementBy(leftSeat.getProposal().want);
leftSeat.incrementBy(leftSeat.getProposal().want);
rightSeat.decrementBy(harden(leftSeat.getProposal().want));
leftSeat.incrementBy(harden(leftSeat.getProposal().want));

leftSeat.decrementBy(rightSeat.getProposal().want);
rightSeat.incrementBy(rightSeat.getProposal().want);
leftSeat.decrementBy(harden(rightSeat.getProposal().want));
rightSeat.incrementBy(harden(rightSeat.getProposal().want));

zcf.reallocate(leftSeat, rightSeat);
} catch (err) {
Expand All @@ -75,11 +75,11 @@ export const swap = (zcf, leftSeat, rightSeat) => {
/** @type {SwapExact} */
export const swapExact = (zcf, leftSeat, rightSeat) => {
try {
rightSeat.decrementBy(rightSeat.getProposal().give);
leftSeat.incrementBy(leftSeat.getProposal().want);
rightSeat.decrementBy(harden(rightSeat.getProposal().give));
leftSeat.incrementBy(harden(leftSeat.getProposal().want));

leftSeat.decrementBy(leftSeat.getProposal().give);
rightSeat.incrementBy(rightSeat.getProposal().want);
leftSeat.decrementBy(harden(leftSeat.getProposal().give));
rightSeat.incrementBy(harden(rightSeat.getProposal().want));

zcf.reallocate(leftSeat, rightSeat);
} catch (err) {
Expand Down Expand Up @@ -178,8 +178,8 @@ export async function depositToSeat(zcf, recipientSeat, amounts, payments) {
// exit the temporary seat. Note that the offerResult is the return value of this
// function, so this synchronous trade must happen before the
// offerResult resolves.
tempSeat.decrementBy(amounts);
recipientSeat.incrementBy(amounts);
tempSeat.decrementBy(harden(amounts));
recipientSeat.incrementBy(harden(amounts));
zcf.reallocate(tempSeat, recipientSeat);
tempSeat.exit();
return depositToSeatSuccessMsg;
Expand Down Expand Up @@ -213,8 +213,8 @@ export async function depositToSeat(zcf, recipientSeat, amounts, payments) {
export async function withdrawFromSeat(zcf, seat, amounts) {
assert(!seat.hasExited(), 'The seat cannot have exited.');
const { zcfSeat: tempSeat, userSeat: tempUserSeatP } = zcf.makeEmptySeatKit();
seat.decrementBy(amounts);
tempSeat.incrementBy(amounts);
seat.decrementBy(harden(amounts));
tempSeat.incrementBy(harden(amounts));
zcf.reallocate(tempSeat, seat);
tempSeat.exit();
return E(tempUserSeatP).getPayouts();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ const extendExpiration = (

// commit point within updateLienedAmount
valueToMint.forEach(updateLienedAmount);
zcfMint.burnLosses(seat.getCurrentAllocation(), seat);
zcfMint.mintGains({ Attestation: amountToMint }, seat);
zcfMint.burnLosses(harden(seat.getCurrentAllocation()), seat);
zcfMint.mintGains(harden({ Attestation: amountToMint }), seat);
seat.exit();
};
harden(extendExpiration);
Expand Down
Loading

0 comments on commit 8b9910e

Please sign in to comment.