From ce1cfafb6d375453865062e1bd66ade66fb80686 Mon Sep 17 00:00:00 2001 From: Turadg Aleahmad Date: Mon, 16 May 2022 16:39:19 -0700 Subject: [PATCH] feat(vault): liquidation penalty handled by liquidation contracts (#5343) * handleFooOffer style per https://github.com/Agoric/agoric-sdk/pull/5179 * chore(zoe): more typing offerTo and Invitation * plan * factored penalties into liquidation contracts * rm obsolete partitionProceeds * lint and comments * named parameters for makeGovernedTerms() * wire reservePublicFacet into vaultFactory terms * rm obsolete penaltyPoolSeat * fixup! wire reservePublicFacet into vaultFactory terms * fixup! rm obsolete penaltyPoolSeat * consume reserve instance instead of facet * typecheck liquidator,vaultFactory tests * fix test typo tsc caught * fix invitation param and typecheck that would have caught it * update comment docs * work around type resolution shortcomings * better comments Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../src/contractGovernance/paramManager.js | 6 +- .../src/proposals/core-proposal.js | 6 +- .../src/proposals/econ-behaviors.js | 38 ++++++----- .../vaultFactory/liquidateIncrementally.js | 45 +++++++++++-- .../src/vaultFactory/liquidateMinimum.js | 43 +++++++++--- .../src/vaultFactory/liquidation.js | 66 +++++-------------- .../run-protocol/src/vaultFactory/params.js | 47 +++++++------ .../run-protocol/src/vaultFactory/types.js | 17 +++-- .../src/vaultFactory/vaultDirector.js | 21 +++--- .../src/vaultFactory/vaultFactory.js | 6 +- .../src/vaultFactory/vaultManager.js | 18 ++--- packages/run-protocol/test/reserve/setup.js | 2 +- .../test/swingsetTests/mockAmm.js | 1 + .../run-protocol/test/swingsetTests/setup.js | 31 +++++---- .../vaultFactory/test-liquidation-helpers.js | 39 ----------- .../test/vaultFactory/test-liquidator.js | 41 +++++++----- .../test/vaultFactory/test-vaultFactory.js | 49 +++++++++----- .../zoe/src/contractSupport/zoeHelpers.js | 9 ++- packages/zoe/src/contracts/otcDesk.js | 1 + packages/zoe/src/types.js | 1 + 20 files changed, 267 insertions(+), 220 deletions(-) delete mode 100644 packages/run-protocol/test/vaultFactory/test-liquidation-helpers.js diff --git a/packages/governance/src/contractGovernance/paramManager.js b/packages/governance/src/contractGovernance/paramManager.js index fa294758c96..1eea5de7329 100644 --- a/packages/governance/src/contractGovernance/paramManager.js +++ b/packages/governance/src/contractGovernance/paramManager.js @@ -17,7 +17,7 @@ import { } from './assertions.js'; import { CONTRACT_ELECTORATE } from './governParam.js'; -const { details: X } = assert; +const { details: X, quote: q } = assert; /** * @param {ParamManagerBase} paramManager @@ -29,6 +29,10 @@ const assertElectorateMatches = (paramManager, governedParams) => { const { [CONTRACT_ELECTORATE]: { value: paramElectorate }, } = governedParams; + assert( + paramElectorate, + X`Missing ${q(CONTRACT_ELECTORATE)} term in ${q(governedParams)}`, + ); assert( keyEQ(managerElectorate, paramElectorate), X`Electorate in manager (${managerElectorate})} incompatible with terms (${paramElectorate}`, diff --git a/packages/run-protocol/src/proposals/core-proposal.js b/packages/run-protocol/src/proposals/core-proposal.js index 641507e8cf5..2183bdd9820 100644 --- a/packages/run-protocol/src/proposals/core-proposal.js +++ b/packages/run-protocol/src/proposals/core-proposal.js @@ -67,7 +67,11 @@ const SHARED_MAIN_MANIFEST = harden({ }, }, instance: { - consume: { amm: 'amm', economicCommittee: 'economicCommittee' }, + consume: { + amm: 'amm', + economicCommittee: 'economicCommittee', + reserve: 'reserve', + }, produce: { VaultFactory: 'VaultFactory', Treasury: 'VaultFactory', diff --git a/packages/run-protocol/src/proposals/econ-behaviors.js b/packages/run-protocol/src/proposals/econ-behaviors.js index 5e11229ab41..30bcf02cdd6 100644 --- a/packages/run-protocol/src/proposals/econ-behaviors.js +++ b/packages/run-protocol/src/proposals/econ-behaviors.js @@ -247,7 +247,7 @@ export const startVaultFactory = async ( { consume: { chainTimerService, - priceAuthority, + priceAuthority: priceAuthorityP, zoe, feeMintAccess: feeMintAccessP, // ISSUE: why doeszn't Zoe await this? economicCommitteeCreatorFacet: electorateCreatorFacet, @@ -296,27 +296,35 @@ export const startVaultFactory = async ( loanFee: makeRatio(0n, centralBrand, BASIS_POINTS), }; - const [ammInstance, electorateInstance, contractGovernorInstall] = - await Promise.all([ - instance.consume.amm, - instance.consume.economicCommittee, - contractGovernor, - ]); + const [ + ammInstance, + electorateInstance, + contractGovernorInstall, + reserveInstance, + ] = await Promise.all([ + instance.consume.amm, + instance.consume.economicCommittee, + contractGovernor, + instance.consume.reserve, + ]); const ammPublicFacet = await E(zoe).getPublicFacet(ammInstance); const feeMintAccess = await feeMintAccessP; - const pa = await priceAuthority; + const priceAuthority = await priceAuthorityP; + const reservePublicFacet = await E(zoe).getPublicFacet(reserveInstance); const timer = await chainTimerService; - const vaultFactoryTerms = makeGovernedTerms( - pa, - loanParams, - installations.liquidate, + const vaultFactoryTerms = makeGovernedTerms({ + priceAuthority, + reservePublicFacet, + loanTiming: loanParams, + liquidationInstall: installations.liquidate, timer, invitationAmount, vaultManagerParams, ammPublicFacet, - liquidationDetailTerms(centralBrand), - AmountMath.make(centralBrand, minInitialDebt), - ); + liquidationTerms: liquidationDetailTerms(centralBrand), + minInitialDebt: AmountMath.make(centralBrand, minInitialDebt), + bootstrapPaymentValue: 0n, + }); const governorTerms = harden({ timer, diff --git a/packages/run-protocol/src/vaultFactory/liquidateIncrementally.js b/packages/run-protocol/src/vaultFactory/liquidateIncrementally.js index b6256ab7116..ebba37704cb 100644 --- a/packages/run-protocol/src/vaultFactory/liquidateIncrementally.js +++ b/packages/run-protocol/src/vaultFactory/liquidateIncrementally.js @@ -23,6 +23,10 @@ const trace = makeTracer('LiqI', false); * price impact on the AMM of any one sale. Each block it will compute * a tranche of collateral to sell, where the size is a function of * the amount of that collateral in the AMM pool and the desired price impact. + * It presently consults the AMM and Oracle for whether to sell. + * + * The next revision of this will work as follows... + * * It then gets 3 prices for the current tranche: * - AMM quote - compute XYK locally based on the pool sizes * - Reserve quote - based on a low price at which the Reserve will purchase @@ -47,6 +51,7 @@ const trace = makeTracer('LiqI', false); * @typedef {{ * amm: XYKAMMPublicFacet, * priceAuthority: PriceAuthority, + * reservePublicFacet: AssetReservePublicFacet, * timerService: TimerService, * debtBrand: Brand, * MaxImpactBP: NatValue, @@ -59,6 +64,7 @@ const start = async zcf => { const { amm, priceAuthority, + reservePublicFacet, timerService, debtBrand, MaxImpactBP, @@ -74,6 +80,10 @@ const start = async zcf => { const asFloat = (numerator, denominator) => Number(numerator) / Number(denominator); + // TODO(5467)) distribute penalties to the reserve + assert(reservePublicFacet, 'Missing reservePublicFacet'); + const { zcfSeat: penaltyPoolSeat } = zcf.makeEmptySeatKit(); + /** * Compute the tranche size whose sale on the AMM would have * a price impact of MAX_IMPACT_BP. @@ -242,8 +252,16 @@ const start = async zcf => { trace('offerResult', { amounts }); } - const debtorHook = async (debtorSeat, { debt: originalDebt }) => { - trace('LIQ', originalDebt); + /** + * @param {ZCFSeat} debtorSeat + * @param {object} options + * @param {Amount<'nat'>} options.debt Debt before penalties + * @param {Ratio} options.penaltyRate + */ + const handleLiquidateOffer = async ( + debtorSeat, + { debt: originalDebt, penaltyRate }, + ) => { assertProposalShape(debtorSeat, { give: { In: null }, }); @@ -251,7 +269,11 @@ const start = async zcf => { originalDebt.brand === debtBrand, X`Cannot liquidate to ${originalDebt.brand}`, ); - for await (const t of processTranches(debtorSeat, originalDebt)) { + const penalty = ceilMultiplyBy(originalDebt, penaltyRate); + const debtWithPenalty = AmountMath.add(originalDebt, penalty); + trace('LIQ', { originalDebt, debtWithPenalty }); + + for await (const t of processTranches(debtorSeat, debtWithPenalty)) { const { collateral, oracleLimit, ammProceeds, debt } = t; trace(`OFFER TO DEBT: `, { collateral, @@ -276,6 +298,18 @@ const start = async zcf => { }); } } + + // Now we need to know how much was sold so we can pay off the debt. + // We can use this seat because only liquidation adds debt brand to it.. + const debtPaid = debtorSeat.getAmountAllocated('Out', debtBrand); + const penaltyPaid = AmountMath.min(penalty, debtPaid); + + // Allocate penalty portion of proceeds to a seat that will hold it for transfer to reserve + penaltyPoolSeat.incrementBy( + debtorSeat.decrementBy(harden({ Out: penaltyPaid })), + ); + zcf.reallocate(penaltyPoolSeat, debtorSeat); + debtorSeat.exit(); trace('exit seat'); }; @@ -283,8 +317,9 @@ const start = async zcf => { /** * @type {ERef} */ - const creatorFacet = Far('debtorInvitationCreator', { - makeLiquidateInvitation: () => zcf.makeInvitation(debtorHook, 'Liquidate'), + const creatorFacet = Far('debtorInvitationCreator (incrementally)', { + makeLiquidateInvitation: () => + zcf.makeInvitation(handleLiquidateOffer, 'Liquidate'), }); return harden({ creatorFacet }); diff --git a/packages/run-protocol/src/vaultFactory/liquidateMinimum.js b/packages/run-protocol/src/vaultFactory/liquidateMinimum.js index fa22347b583..88c9772f0df 100644 --- a/packages/run-protocol/src/vaultFactory/liquidateMinimum.js +++ b/packages/run-protocol/src/vaultFactory/liquidateMinimum.js @@ -1,7 +1,10 @@ // @ts-check import { E } from '@endo/eventual-send'; -import { offerTo } from '@agoric/zoe/src/contractSupport/index.js'; +import { + ceilMultiplyBy, + offerTo, +} from '@agoric/zoe/src/contractSupport/index.js'; import { AmountMath } from '@agoric/ertp'; import { Far } from '@endo/marshal'; @@ -26,10 +29,19 @@ const start = async zcf => { /** * @param {ZCFSeat} debtorSeat - * @param {{ debt: Amount<'nat'> }} options + * @param {object} options + * @param {Amount<'nat'>} options.debt Debt before penalties + * @param {Ratio} options.penaltyRate */ - const debtorHook = async (debtorSeat, { debt }) => { - const debtBrand = debt.brand; + const handleLiquidationOffer = async ( + debtorSeat, + { debt: originalDebt, penaltyRate }, + ) => { + // XXX does not distribute penalties anywhere + const { zcfSeat: penaltyPoolSeat } = zcf.makeEmptySeatKit(); + const penalty = ceilMultiplyBy(originalDebt, penaltyRate); + const debtWithPenalty = AmountMath.add(originalDebt, penalty); + const debtBrand = originalDebt.brand; const { give: { In: amountIn }, } = debtorSeat.getProposal(); @@ -39,7 +51,7 @@ const start = async zcf => { give: { In: amountIn }, want: { Out: AmountMath.makeEmpty(debtBrand) }, }); - trace(`OFFER TO DEBT: `, debt, amountIn); + trace(`OFFER TO DEBT: `, debtWithPenalty, amountIn); const { deposited } = await offerTo( zcf, swapInvitation, @@ -47,23 +59,36 @@ const start = async zcf => { liqProposal, debtorSeat, debtorSeat, - { stopAfter: debt }, + { stopAfter: debtWithPenalty }, ); const amounts = await deposited; trace(`Liq results`, { - debt, + debtWithPenalty, amountIn, paid: debtorSeat.getCurrentAllocation(), amounts, }); + + // Now we need to know how much was sold so we can pay off the debt. + // We can use this seat because only liquidation adds debt brand to it.. + const debtPaid = debtorSeat.getAmountAllocated('Out', debtBrand); + const penaltyPaid = AmountMath.min(penalty, debtPaid); + + // Allocate penalty portion of proceeds to a seat that will hold it for transfer to reserve + penaltyPoolSeat.incrementBy( + debtorSeat.decrementBy(harden({ Out: penaltyPaid })), + ); + zcf.reallocate(penaltyPoolSeat, debtorSeat); + debtorSeat.exit(); }; /** * @type {ERef} */ - const creatorFacet = Far('debtorInvitationCreator', { - makeLiquidateInvitation: () => zcf.makeInvitation(debtorHook, 'Liquidate'), + const creatorFacet = Far('debtorInvitationCreator (minimum)', { + makeLiquidateInvitation: () => + zcf.makeInvitation(handleLiquidationOffer, 'Liquidate'), }); return harden({ creatorFacet }); diff --git a/packages/run-protocol/src/vaultFactory/liquidation.js b/packages/run-protocol/src/vaultFactory/liquidation.js index 5e74f839b2c..98ed7bb8723 100644 --- a/packages/run-protocol/src/vaultFactory/liquidation.js +++ b/packages/run-protocol/src/vaultFactory/liquidation.js @@ -3,30 +3,11 @@ import { E } from '@endo/eventual-send'; import { AmountMath } from '@agoric/ertp'; -import { - ceilMultiplyBy, - makeRatio, - offerTo, -} from '@agoric/zoe/src/contractSupport/index.js'; +import { makeRatio, offerTo } from '@agoric/zoe/src/contractSupport/index.js'; import { makeTracer } from '../makeTracer.js'; const trace = makeTracer('LIQ'); -/** - * @param {Amount<'nat'>} proceeds - * @param {Amount<'nat'>} debt - after incurring penalty - * @param {Amount<'nat'>} penaltyPortion - */ -const partitionProceeds = (proceeds, debt, penaltyPortion) => { - const debtPaid = AmountMath.min(proceeds, debt); - - // Pay as much of the penalty as possible - const penaltyProceeds = AmountMath.min(penaltyPortion, debtPaid); - const runToBurn = AmountMath.subtract(debtPaid, penaltyProceeds); - - return { debtPaid, penaltyProceeds, runToBurn }; -}; - /** * Liquidates a Vault, using the strategy to parameterize the particular * contract being used. The strategy provides a KeywordMapping and proposal @@ -42,7 +23,6 @@ const partitionProceeds = (proceeds, debt, penaltyPortion) => { * ) => void} burnLosses * @param {Liquidator} liquidator * @param {Brand} collateralBrand - * @param {ZCFSeat} penaltyPoolSeat * @param {Ratio} penaltyRate * @returns {Promise} */ @@ -52,16 +32,12 @@ const liquidate = async ( burnLosses, liquidator, collateralBrand, - penaltyPoolSeat, penaltyRate, ) => { trace('liquidate start', vault); vault.liquidating(); - const debtBeforePenalty = vault.getCurrentDebt(); - const penalty = ceilMultiplyBy(debtBeforePenalty, penaltyRate); - - const debt = AmountMath.add(debtBeforePenalty, penalty); + const debt = vault.getCurrentDebt(); const vaultZcfSeat = vault.getVaultSeat(); @@ -69,7 +45,7 @@ const liquidate = async ( 'Collateral', collateralBrand, ); - trace(`liq prep`, collateralToSell, debtBeforePenalty, debt); + trace(`liq prep`, { collateralToSell, debt, liquidator }); const { deposited, userSeatPromise: liqSeat } = await offerTo( zcf, @@ -81,33 +57,23 @@ const liquidate = async ( }), vaultZcfSeat, vaultZcfSeat, - harden({ debt }), - ); - trace(` offeredTo`, collateralToSell, debt); - - // await deposited, but we don't need the value. - await Promise.all([deposited, E(liqSeat).getOfferResult()]); - - // Now we need to know how much was sold so we can pay off the debt. - // We can use this because only liquidation adds RUN to the vaultSeat. - const { debtPaid, penaltyProceeds, runToBurn } = partitionProceeds( - vaultZcfSeat.getAmountAllocated('RUN', debt.brand), - debt, - penalty, + harden({ debt, penaltyRate }), ); + trace(` offeredTo`, { collateralToSell, debt }); - trace({ debt, debtPaid, penaltyProceeds, runToBurn }); - - // Allocate penalty portion of proceeds to a seat that will be transferred to reserve - penaltyPoolSeat.incrementBy( - vaultZcfSeat.decrementBy(harden({ RUN: penaltyProceeds })), - ); - zcf.reallocate(penaltyPoolSeat, vaultZcfSeat); + // await deposited and offer result, but ignore the latter + const [proceeds] = await Promise.all([ + deposited, + E(liqSeat).getOfferResult(), + ]); + // NB: all the proceeds from AMM sale are on the vault seat instead of a staging seat + const runToBurn = AmountMath.min(proceeds.RUN, debt); + trace('before burn', { debt, proceeds, runToBurn }); burnLosses(runToBurn, vaultZcfSeat); // Accounting complete. Update the vault state. - vault.liquidated(AmountMath.subtract(debt, debtPaid)); + vault.liquidated(AmountMath.subtract(debt, runToBurn)); // remaining funds are left on the vault for the user to close and claim return vault; @@ -119,9 +85,9 @@ const liquidationDetailTerms = debtBrand => OracleTolerance: makeRatio(30n, debtBrand), AMMMaxSlippage: makeRatio(30n, debtBrand), }); +/** @typedef {ReturnType} LiquidationTerms */ harden(liquidate); -harden(partitionProceeds); harden(liquidationDetailTerms); -export { liquidate, partitionProceeds, liquidationDetailTerms }; +export { liquidate, liquidationDetailTerms }; diff --git a/packages/run-protocol/src/vaultFactory/params.js b/packages/run-protocol/src/vaultFactory/params.js index 85e9aa158f2..047124a9023 100644 --- a/packages/run-protocol/src/vaultFactory/params.js +++ b/packages/run-protocol/src/vaultFactory/params.js @@ -26,7 +26,7 @@ export const MIN_INITIAL_DEBT_KEY = 'MinInitialDebt'; /** * @param {Amount} electorateInvitationAmount * @param {Installation} liquidationInstall - * @param {object} liquidationTerms + * @param {import('./liquidation.js').LiquidationTerms} liquidationTerms * @param {Amount} minInitialDebt */ const makeVaultDirectorParams = ( @@ -107,29 +107,33 @@ const makeVaultDirectorParamManager = async ( }; /** - * @param {ERef} priceAuthority - * @param {LoanTiming} loanTiming - * @param {Installation} liquidationInstall - * @param {ERef} timerService - * @param {Amount} invitationAmount - * @param {VaultManagerParamValues} vaultManagerParams - * @param {XYKAMMPublicFacet} ammPublicFacet - * @param {object} liquidationTerms - * @param {Amount} minInitialDebt - * @param {bigint=} bootstrapPaymentValue + * @param {{ + * invitationAmount: Amount, + * minInitialDebt: Amount, + * bootstrapPaymentValue: bigint, + * priceAuthority: ERef, + * timer: ERef, + * reservePublicFacet: AssetReservePublicFacet, + * liquidationInstall: Installation, + * loanTiming: LoanTiming, + * liquidationTerms: import('./liquidation.js').LiquidationTerms, + * vaultManagerParams: VaultManagerParamValues, + * ammPublicFacet: XYKAMMPublicFacet, + * }} opts */ -const makeGovernedTerms = ( - priceAuthority, - loanTiming, - liquidationInstall, - timerService, - invitationAmount, - vaultManagerParams, +const makeGovernedTerms = ({ ammPublicFacet, + bootstrapPaymentValue, + invitationAmount, + liquidationInstall, liquidationTerms, + loanTiming, minInitialDebt, - bootstrapPaymentValue = 0n, -) => { + priceAuthority, + reservePublicFacet, + timer, + vaultManagerParams, +}) => { const loanTimingParams = makeParamManagerSync({ [CHARGING_PERIOD_KEY]: ['nat', loanTiming.chargingPeriod], [RECORDING_PERIOD_KEY]: ['nat', loanTiming.recordingPeriod], @@ -142,7 +146,8 @@ const makeGovernedTerms = ( priceAuthority, loanParams, loanTimingParams, - timerService, + reservePublicFacet, + timerService: timer, governedParams: makeVaultDirectorParams( invitationAmount, liquidationInstall, diff --git a/packages/run-protocol/src/vaultFactory/types.js b/packages/run-protocol/src/vaultFactory/types.js index e0a1d6c2850..22a8cad0392 100644 --- a/packages/run-protocol/src/vaultFactory/types.js +++ b/packages/run-protocol/src/vaultFactory/types.js @@ -1,10 +1,14 @@ // @ts-check -/** @typedef {import('./vault').VaultUIState} VaultUIState */ -/** @typedef {import('./vault').Vault} Vault */ -/** @typedef {import('./vaultKit').VaultKit} VaultKit */ -/** @typedef {import('./vaultManager').VaultManager} VaultManager */ -/** @typedef {import('./vaultManager').CollateralManager} CollateralManager */ +/** + * @typedef {import('./vault').VaultUIState} VaultUIState + * @typedef {import('./vault').Vault} Vault + * @typedef {import('./vaultKit').VaultKit} VaultKit + * @typedef {import('./vaultManager').VaultManager} VaultManager + * @typedef {import('./vaultManager').CollateralManager} CollateralManager + * @typedef {import('../reserve/assetReserve.js').AssetReserveCreatorFacet} AssetReserveCreatorFacet + * @typedef {import('../reserve/assetReserve.js').AssetReservePublicFacet} AssetReservePublicFacet + */ /** * @typedef {object} AutoswapLocal @@ -45,7 +49,6 @@ * @property {AddVaultType} addVaultType * @property {() => Promise>} getCollaterals * @property {() => Allocation} getRewardAllocation - * @property {() => Allocation} getPenaltyAllocation * @property {() => Instance} getContractGovernor * @property {() => Promise} makeCollectFeesInvitation */ @@ -112,7 +115,7 @@ /** * @typedef {object} Liquidator - * @property {() => Promise} makeLiquidateInvitation + * @property {() => Promise }, void>>} makeLiquidateInvitation */ /** diff --git a/packages/run-protocol/src/vaultFactory/vaultDirector.js b/packages/run-protocol/src/vaultFactory/vaultDirector.js index 3824f72c81b..0ee764c7d9a 100644 --- a/packages/run-protocol/src/vaultFactory/vaultDirector.js +++ b/packages/run-protocol/src/vaultFactory/vaultDirector.js @@ -37,7 +37,6 @@ const { details: X } = assert; * electionManager: Instance, * directorParamManager: import('@agoric/governance/src/contractGovernance/typedParamManager').TypedParamManager, * mintSeat: ZCFSeat, - * penaltyPoolSeat: ZCFSeat, * rewardPoolSeat: ZCFSeat, * vaultParamManagers: Store, * zcf: import('./vaultFactory.js').VaultFactoryZCF, @@ -64,7 +63,6 @@ const initState = (zcf, directorParamManager, debtMint) => { /** For temporary staging of newly minted tokens */ const { zcfSeat: mintSeat } = zcf.makeEmptySeatKit(); const { zcfSeat: rewardPoolSeat } = zcf.makeEmptySeatKit(); - const { zcfSeat: penaltyPoolSeat } = zcf.makeEmptySeatKit(); const collateralTypes = makeScalarMap('brand'); @@ -76,7 +74,6 @@ const initState = (zcf, directorParamManager, debtMint) => { directorParamManager, mintSeat, rewardPoolSeat, - penaltyPoolSeat, vaultParamManagers, zcf, }; @@ -153,11 +150,21 @@ const getCollaterals = async ({ state }) => { ); }; +/** + * @param {import('@agoric/governance/src/contractGovernance/typedParamManager').TypedParamManager} directorParamManager + */ const getLiquidationConfig = directorParamManager => ({ install: directorParamManager.getLiquidationInstall(), terms: directorParamManager.getLiquidationTerms(), }); +/** + * + * @param {*} govParams + * @param {VaultManager} vaultManager + * @param {*} oldInstall + * @param {*} oldTerms + */ const watchGovernance = (govParams, vaultManager, oldInstall, oldTerms) => { const subscription = govParams.getSubscription(); observeIteration(subscription, { @@ -194,7 +201,6 @@ const machineBehavior = { debtMint, collateralTypes, mintSeat, - penaltyPoolSeat, rewardPoolSeat, vaultParamManagers, directorParamManager, @@ -279,7 +285,6 @@ const machineBehavior = { zcf.getTerms().priceAuthority, factoryPowers, timerService, - penaltyPoolSeat, startTimeStamp, ); collateralTypes.init(collateralBrand, vm); @@ -306,9 +311,6 @@ const machineBehavior = { /** @param {MethodContext} context */ getRewardAllocation: ({ state }) => state.rewardPoolSeat.getCurrentAllocation(), - /** @param {MethodContext} context */ - getPenaltyAllocation: ({ state }) => - state.penaltyPoolSeat.getCurrentAllocation(), }; const creatorBehavior = { @@ -412,10 +414,11 @@ const behavior = { * ammPublicFacet: AutoswapPublicFacet, * liquidationInstall: Installation, * loanTimingParams: {ChargingPeriod: ParamRecord<'nat'>, RecordingPeriod: ParamRecord<'nat'>}, + * reservePublicFacet: AssetReservePublicFacet, * timerService: TimerService, * priceAuthority: ERef * }>} zcf - * @param {import('./params.js').VaultDirectorParams} directorParamManager + * @param {import('@agoric/governance/src/contractGovernance/typedParamManager').TypedParamManager} directorParamManager * @param {ZCFMint<"nat">} debtMint */ const makeVaultDirector = defineKindMulti('VaultDirector', initState, behavior); diff --git a/packages/run-protocol/src/vaultFactory/vaultFactory.js b/packages/run-protocol/src/vaultFactory/vaultFactory.js index 2c7a756990a..4a471f94851 100644 --- a/packages/run-protocol/src/vaultFactory/vaultFactory.js +++ b/packages/run-protocol/src/vaultFactory/vaultFactory.js @@ -33,9 +33,11 @@ import { makeVaultDirector } from './vaultDirector.js'; * ammPublicFacet: AutoswapPublicFacet, * liquidationInstall: Installation, * loanTimingParams: {ChargingPeriod: ParamRecord<'nat'>, RecordingPeriod: ParamRecord<'nat'>}, - * timerService: TimerService, * minInitialDebt: Amount, - * priceAuthority: ERef}>} VaultFactoryZCF + * priceAuthority: ERef, + * reservePublicFacet: AssetReservePublicFacet, + * timerService: TimerService, + * }>} VaultFactoryZCF */ /** diff --git a/packages/run-protocol/src/vaultFactory/vaultManager.js b/packages/run-protocol/src/vaultFactory/vaultManager.js index 5cf73cf0c1d..1e75b4a9c8f 100644 --- a/packages/run-protocol/src/vaultFactory/vaultManager.js +++ b/packages/run-protocol/src/vaultFactory/vaultManager.js @@ -56,12 +56,11 @@ const trace = makeTracer('VM', false); * debtBrand: Brand<'nat'>, * debtMint: ZCFMint<'nat'>, * factoryPowers: import('./vaultDirector.js').FactoryPowersFacet, - * penaltyPoolSeat: ZCFSeat, * periodNotifier: ERef>, * poolIncrementSeat: ZCFSeat, * priceAuthority: ERef, * prioritizedVaults: ReturnType, - * zcf: ZCF, + * zcf: import('./vaultFactory.js').VaultFactoryZCF, * }>} ImmutableState */ @@ -97,13 +96,12 @@ let outstandingQuote = null; /** * Create state for the Vault Manager kind * - * @param {ZCF} zcf + * @param {import('./vaultFactory.js').VaultFactoryZCF} zcf * @param {ZCFMint<'nat'>} debtMint * @param {Brand} collateralBrand * @param {ERef} priceAuthority * @param {import('./vaultDirector.js').FactoryPowersFacet} factoryPowers * @param {ERef} timerService - * @param {ZCFSeat} penaltyPoolSeat * @param {Timestamp} startTimeStamp */ const initState = ( @@ -113,7 +111,6 @@ const initState = ( priceAuthority, factoryPowers, timerService, - penaltyPoolSeat, startTimeStamp, ) => { const periodNotifier = E(timerService).makeNotifier( @@ -127,7 +124,6 @@ const initState = ( debtBrand: debtMint.getIssuerRecord().brand, debtMint, factoryPowers, - penaltyPoolSeat, periodNotifier, poolIncrementSeat: zcf.makeEmptySeatKit().zcfSeat, priceAuthority, @@ -354,7 +350,7 @@ const helperBehavior = { * @param {[key: string, vaultKit: Vault]} record */ liquidateAndRemove: ({ state, facets }, [key, vault]) => { - const { factoryPowers, penaltyPoolSeat, prioritizedVaults, zcf } = state; + const { factoryPowers, prioritizedVaults, zcf } = state; trace('liquidating', vault.getVaultSeat().getProposal()); // Start liquidation (vaultState: LIQUIDATING) @@ -367,7 +363,6 @@ const helperBehavior = { (amount, seat) => facets.manager.burnAndRecord(amount, seat), liquidator, state.collateralBrand, - penaltyPoolSeat, factoryPowers.getGovernedParams().getLiquidationPenalty(), ) .then(() => { @@ -532,7 +527,8 @@ const selfBehavior = { liquidationTerms, ) => { const { zcf, debtBrand, collateralBrand } = state; - const { ammPublicFacet, priceAuthority, timerService } = zcf.getTerms(); + const { ammPublicFacet, priceAuthority, reservePublicFacet, timerService } = + zcf.getTerms(); const zoe = zcf.getZoeService(); const collateralIssuer = zcf.getIssuerForBrand(collateralBrand); const debtIssuer = zcf.getIssuerForBrand(debtBrand); @@ -548,9 +544,10 @@ const selfBehavior = { harden({ ...liquidationTerms, amm: ammPublicFacet, + debtBrand, + reservePublicFacet, priceAuthority, timerService, - debtBrand, }), ); trace('setup liquidator complete', { @@ -631,7 +628,6 @@ const makeVaultManagerKit = defineKindMulti( * @param {ERef} priceAuthority * @param {import('./vaultDirector.js').FactoryPowersFacet} factoryPowers * @param {ERef} timerService - * @param {ZCFSeat} penaltyPoolSeat * @param {Timestamp} startTimeStamp */ export const makeVaultManager = pickFacet(makeVaultManagerKit, 'self'); diff --git a/packages/run-protocol/test/reserve/setup.js b/packages/run-protocol/test/reserve/setup.js index cde3b854dc4..69762aaa395 100644 --- a/packages/run-protocol/test/reserve/setup.js +++ b/packages/run-protocol/test/reserve/setup.js @@ -54,7 +54,7 @@ const setupReserveBootstrap = async ( /** * NOTE: called separately by each test so contracts don't interfere * - * @param {*} t + * @param {import("ava").ExecutionContext} t * @param {{ committeeName: string, committeeSize: number}} electorateTerms * @param {ManualTimer | undefined=} timer * @returns {{ diff --git a/packages/run-protocol/test/swingsetTests/mockAmm.js b/packages/run-protocol/test/swingsetTests/mockAmm.js index 5bd6497441f..3468cc5a884 100644 --- a/packages/run-protocol/test/swingsetTests/mockAmm.js +++ b/packages/run-protocol/test/swingsetTests/mockAmm.js @@ -2,6 +2,7 @@ import { Far } from '@endo/marshal'; +/** @type {any} */ export const ammMock = Far('mock AMM', { getInputPrice(amountIn, amountOut) { return { amountIn, amountOut }; diff --git a/packages/run-protocol/test/swingsetTests/setup.js b/packages/run-protocol/test/swingsetTests/setup.js index 1fbcdb18802..4aa67757589 100644 --- a/packages/run-protocol/test/swingsetTests/setup.js +++ b/packages/run-protocol/test/swingsetTests/setup.js @@ -5,7 +5,7 @@ import { makeIssuerKit, AmountMath } from '@agoric/ertp'; import { makeRatio } from '@agoric/zoe/src/contractSupport/index.js'; import buildManualTimer from '@agoric/zoe/tools/manualTimer'; -import { makeGovernedTerms } from '../../src/vaultFactory/params'; +import { makeGovernedTerms as makeVaultFactoryTerms } from '../../src/vaultFactory/params'; import { ammMock } from './mockAmm'; import { liquidationDetailTerms } from '../../src/vaultFactory/liquidation'; @@ -184,7 +184,7 @@ const buildOwner = async ( const [moolaIssuer] = issuers; const runBrand = await E(E(zoe).getFeeIssuer()).getBrand(); - const rates = makeRates(runBrand); + const vaultManagerParams = makeRates(runBrand); const loanTiming = { chargingPeriod: SECONDS_PER_DAY, @@ -199,18 +199,23 @@ const buildOwner = async ( E(E(zoe).getInvitationIssuer()).getAmountOf(poserInvitationP), ]); - const terms = makeGovernedTerms( - priceAuthorityKit.priceAuthority, + /** @type {AssetReservePublicFacet} */ + // @ts-expect-error cast, never used + const reservePublicFacet = null; + + const terms = makeVaultFactoryTerms({ + priceAuthority: priceAuthorityKit.priceAuthority, loanTiming, - installations.liquidateMinimum, + liquidationInstall: installations.liquidateMinimum, timer, - poserInvitationAmount, - rates, - // @ts-expect-error It's not a real AMM public facet - ammMock, - liquidationDetailTerms(runBrand), - AmountMath.make(runBrand, 100n), - ); + invitationAmount: poserInvitationAmount, + vaultManagerParams, + ammPublicFacet: ammMock, + liquidationTerms: liquidationDetailTerms(runBrand), + minInitialDebt: AmountMath.make(runBrand, 100n), + bootstrapPaymentValue: 0n, + reservePublicFacet, + }); const privateVaultFactoryArgs = { feeMintAccess, initialPoserInvitation }; @@ -235,7 +240,7 @@ const buildOwner = async ( await E(E(governorFacets.creatorFacet).getCreatorFacet()).addVaultType( moolaIssuer, 'Moola', - rates, + vaultManagerParams, ); const QUOTE_INTERVAL = 24n * 60n * 60n; diff --git a/packages/run-protocol/test/vaultFactory/test-liquidation-helpers.js b/packages/run-protocol/test/vaultFactory/test-liquidation-helpers.js deleted file mode 100644 index b681c99421b..00000000000 --- a/packages/run-protocol/test/vaultFactory/test-liquidation-helpers.js +++ /dev/null @@ -1,39 +0,0 @@ -// @ts-check -// Must be first to set up globals -import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; - -import { AmountMath } from '@agoric/ertp'; -import { Far } from '@endo/marshal'; -import { partitionProceeds } from '../../src/vaultFactory/liquidation.js'; - -export const mockBrand = Far('brand'); - -const amount = n => AmountMath.make(mockBrand, BigInt(n)); - -const partitionTest = ( - [proceeds, debt, penaltyPortion], - [debtPaid, penaltyProceeds, runToBurn], -) => { - test(`partitionProceeds: (${proceeds} for ${debt} with ${penaltyPortion} penalty)`, t => { - const result = partitionProceeds( - amount(proceeds), - amount(debt), - amount(penaltyPortion), - ); - t.deepEqual(result, { - debtPaid: amount(debtPaid), - penaltyProceeds: amount(penaltyProceeds), - runToBurn: amount(runToBurn), - }); - }); -}; - -// no proceeds -partitionTest([0, 0, 0], [0, 0, 0]); -partitionTest([0, 100, 10], [0, 0, 0]); -// proceeds gte debt -partitionTest([100, 100, 10], [100, 10, 90]); -partitionTest([200, 100, 10], [100, 10, 90]); -// proceeds less than debt -partitionTest([100, 200, 10], [100, 10, 90]); -partitionTest([100, 200, 200], [100, 100, 0]); diff --git a/packages/run-protocol/test/vaultFactory/test-liquidator.js b/packages/run-protocol/test/vaultFactory/test-liquidator.js index b52a7b65a1a..d8860659278 100644 --- a/packages/run-protocol/test/vaultFactory/test-liquidator.js +++ b/packages/run-protocol/test/vaultFactory/test-liquidator.js @@ -1,6 +1,6 @@ -// @ts-nocheck +// @ts-check -import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; +import { test as unknownTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; import '@agoric/zoe/exported.js'; import { E } from '@endo/eventual-send'; @@ -20,6 +20,7 @@ import { startEconomicCommittee, startVaultFactory, setupAmm, + setupReserve, } from '../../src/proposals/econ-behaviors.js'; import '../../src/vaultFactory/types.js'; import * as Collect from '../../src/collect.js'; @@ -33,6 +34,9 @@ import { } from '../supports.js'; import { unsafeMakeBundleCache } from '../bundleTool.js'; +/** @type {import('ava').TestInterface} */ +const test = unknownTest; + // #region Support // TODO path resolve these so refactors detect @@ -82,15 +86,19 @@ test.before(async t => { const runIssuer = E(zoe).getFeeIssuer(); const runBrand = await E(runIssuer).getBrand(); const aethKit = makeIssuerKit('aEth'); - const loader = await unsafeMakeBundleCache('./bundles/'); // package-relative + const bundleCache = await unsafeMakeBundleCache('./bundles/'); // package-relative // note that the liquidation might be a different bundle name // Collect.mapValues(contractRoots, (root, k) => loader.load(root, k)), const bundles = await Collect.allValues({ - faucet: loader.load(contractRoots.faucet, 'faucet'), - liquidate: loader.load(contractRoots.liquidate, 'liquidateIncrementally'), - VaultFactory: loader.load(contractRoots.VaultFactory, 'VaultFactory'), - amm: loader.load(contractRoots.amm, 'amm'), + faucet: bundleCache.load(contractRoots.faucet, 'faucet'), + liquidate: bundleCache.load( + contractRoots.liquidate, + 'liquidateIncrementally', + ), + VaultFactory: bundleCache.load(contractRoots.VaultFactory, 'VaultFactory'), + amm: bundleCache.load(contractRoots.amm, 'amm'), + reserve: bundleCache.load(contractRoots.reserve, 'reserve'), }); const installation = Collect.mapValues(bundles, bundle => E(zoe).install(bundle), @@ -112,7 +120,7 @@ test.before(async t => { aethInitialLiquidity: AmountMath.make(aethKit.brand, 900_000_000n), }; const frozenCtx = await deeplyFulfilled(harden(contextPs)); - t.context = { ...frozenCtx }; + t.context = { ...frozenCtx, bundleCache }; trace(t, 'CONTEXT'); }); @@ -142,7 +150,6 @@ const setupAmmAndElectorate = async (t, aethLiquidity, runLiquidity) => { t.context.committee = makeVoterTool( zoe, space.consume.economicCommitteeCreatorFacet, - // @ts-expect-error TODO: add vaultFactoryGovernorCreator to vats/src/types.js space.consume.vaultFactoryGovernorCreator, counter, ); @@ -185,7 +192,7 @@ const setupAmmAndElectorate = async (t, aethLiquidity, runLiquidity) => { /** * - * @param {ExecutionContext} t + * @param {import('ava').ExecutionContext} t * @param {bigint} runInitialLiquidity */ const getRunFromFaucet = async (t, runInitialLiquidity) => { @@ -218,7 +225,7 @@ const getRunFromFaucet = async (t, runInitialLiquidity) => { /** * NOTE: called separately by each test so AMM/zoe/priceAuthority don't interfere * - * @param {ExecutionContext} t + * @param {import('ava').ExecutionContext} t * @param {Amount} initialPrice * @param {Amount} priceBase * @param {TimerService} timer @@ -273,6 +280,10 @@ async function setupServices( const { installation: { produce: iProduce }, } = space; + // make the installation available for setupReserve + iProduce.reserve.resolve(t.context.installation.reserve); + // produce the reserve instance in the space + await setupReserve(space); iProduce.VaultFactory.resolve(t.context.installation.VaultFactory); iProduce.liquidate.resolve(t.context.installation.liquidate); await startVaultFactory(space, { loanParams: loanTiming }, minInitialDebt); @@ -383,9 +394,9 @@ const makeDriver = async (t, initialPrice, priceBase) => { }, /** * - * @param {Vault.Phase} phase - * @param {object} likeExpected - * @param {undefined|AT_NEXT|number} optSince + * @param {import('../../src/vaultFactory/vault.js').VaultPhase} phase + * @param {object} [likeExpected] + * @param {AT_NEXT|number} [optSince] */ notified: async (phase, likeExpected, optSince) => { // optSince can be AT_NEXT in order to wait for the @@ -477,7 +488,7 @@ const makeDriver = async (t, initialPrice, priceBase) => { }, managerNotified: async (likeExpected, optSince) => { managerNotification = await E(managerNotifier).getUpdateSince( - optSince === AT_NEXT ? managerNotifier.updateCount : optSince, + optSince === AT_NEXT ? managerNotification.updateCount : optSince, ); trace(t, 'manager notifier', managerNotification); if (likeExpected) { diff --git a/packages/run-protocol/test/vaultFactory/test-vaultFactory.js b/packages/run-protocol/test/vaultFactory/test-vaultFactory.js index 9993385ac12..338be019dbc 100644 --- a/packages/run-protocol/test/vaultFactory/test-vaultFactory.js +++ b/packages/run-protocol/test/vaultFactory/test-vaultFactory.js @@ -1,6 +1,6 @@ -// @ts-nocheck +// @ts-check -import { test } from '@agoric/zoe/tools/prepare-test-env-ava.js'; +import { test as unknownTest } from '@agoric/zoe/tools/prepare-test-env-ava.js'; import '@agoric/zoe/exported.js'; import { E } from '@endo/eventual-send'; @@ -28,6 +28,7 @@ import { startEconomicCommittee, startVaultFactory, setupAmm, + setupReserve, } from '../../src/proposals/econ-behaviors.js'; import '../../src/vaultFactory/types.js'; import * as Collect from '../../src/collect.js'; @@ -41,6 +42,9 @@ import { } from '../supports.js'; import { unsafeMakeBundleCache } from '../bundleTool.js'; +/** @type {import('ava').TestInterface} */ +const test = unknownTest; + // #region Support // TODO path resolve these so refactors detect @@ -49,6 +53,7 @@ const contractRoots = { liquidate: './src/vaultFactory/liquidateMinimum.js', VaultFactory: './src/vaultFactory/vaultFactory.js', amm: './src/vpool-xyk-amm/multipoolMarketMaker.js', + reserve: './src/reserve/assetReserve.js', }; /** @typedef {import('../../src/vaultFactory/vaultFactory').VaultFactoryContract} VFC */ @@ -92,13 +97,15 @@ test.before(async t => { const runIssuer = E(zoe).getFeeIssuer(); const runBrand = E(runIssuer).getBrand(); const aethKit = makeIssuerKit('aEth'); - const loader = await unsafeMakeBundleCache('./bundles/'); // package-relative + + const bundleCache = await unsafeMakeBundleCache('./bundles/'); // package-relative // note that the liquidation might be a different bundle name const bundles = await Collect.allValues({ - faucet: loader.load(contractRoots.faucet, 'faucet'), - liquidate: loader.load(contractRoots.liquidate, 'liquidateMinimum'), - VaultFactory: loader.load(contractRoots.VaultFactory, 'VaultFactory'), - amm: loader.load(contractRoots.amm, 'amm'), + faucet: bundleCache.load(contractRoots.faucet, 'faucet'), + liquidate: bundleCache.load(contractRoots.liquidate, 'liquidateMinimum'), + VaultFactory: bundleCache.load(contractRoots.VaultFactory, 'VaultFactory'), + amm: bundleCache.load(contractRoots.amm, 'amm'), + reserve: bundleCache.load(contractRoots.reserve, 'reserve'), }); const installation = Collect.mapValues(bundles, bundle => E(zoe).install(bundle), @@ -121,7 +128,7 @@ test.before(async t => { aethInitialLiquidity: AmountMath.make(aethKit.brand, 300n), }; const frozenCtx = await deeplyFulfilled(harden(contextPs)); - t.context = { ...frozenCtx }; + t.context = { ...frozenCtx, bundleCache }; trace(t, 'CONTEXT'); }); @@ -183,7 +190,7 @@ const setupAmmAndElectorate = async (t, aethLiquidity, runLiquidity) => { /** * - * @param {ExecutionContext} t + * @param {import('ava').ExecutionContext} t * @param {bigint} runInitialLiquidity */ const getRunFromFaucet = async (t, runInitialLiquidity) => { @@ -195,6 +202,7 @@ const getRunFromFaucet = async (t, runInitialLiquidity) => { } = t.context; /** @type {Promise>} */ // On-chain, there will be pre-existing RUN. The faucet replicates that + // @ts-expect-error const { creatorFacet: faucetCreator } = await E(zoe).startInstance( installation, {}, @@ -217,8 +225,8 @@ const getRunFromFaucet = async (t, runInitialLiquidity) => { /** * NOTE: called separately by each test so AMM/zoe/priceAuthority don't interfere * - * @param {ExecutionContext} t - * @param {Array | Ratio} priceOrList + * @param {import('ava').ExecutionContext} t + * @param {Array | Ratio} priceOrList * @param {Amount} unitAmountIn * @param {TimerService} timer * @param {unknown} quoteInterval @@ -287,6 +295,10 @@ async function setupServices( const { installation: { produce: iProduce }, } = space; + // make the installation available for setupReserve + iProduce.reserve.resolve(t.context.installation.reserve); + // produce the reserve instance in the space + await setupReserve(space); iProduce.VaultFactory.resolve(t.context.installation.VaultFactory); iProduce.liquidate.resolve(t.context.installation.liquidate); await startVaultFactory(space, { loanParams: loanTiming }, minInitialDebt); @@ -298,12 +310,13 @@ async function setupServices( ); // Add a vault that will lend on aeth collateral + /** @type {Promise} */ const aethVaultManagerP = E(vaultFactoryCreatorFacet).addVaultType( aethIssuer, 'AEth', rates, ); - /** @type {[any, VaultFactory, VFC['publicFacet']]} */ + /** @type {[any, VaultFactory, VFC['publicFacet'], VaultManager, PriceAuthority]} */ // @ts-expect-error cast const [ governorInstance, @@ -460,9 +473,6 @@ test('first', async t => { 'unused collateral remains after liquidation', ); - t.deepEqual(await E(vaultFactory).getPenaltyAllocation(), { - RUN: AmountMath.make(runBrand, 30n), - }); t.deepEqual(await E(vaultFactory).getRewardAllocation(), { RUN: AmountMath.make(runBrand, 24n), }); @@ -546,11 +556,13 @@ test('price drop', async t => { ); trace(t, 'pa2', priceAuthority); + // @ts-expect-error mock priceAuthority.setPrice(makeRatio(677n, runBrand, 900n, aethBrand)); trace(t, 'price dropped a little'); notification = await E(vaultNotifier).getUpdateSince(); t.is(notification.value.vaultState, Phase.ACTIVE); + // @ts-expect-error mock await E(priceAuthority).setPrice(makeRatio(636n, runBrand, 900n, aethBrand)); notification = await E(vaultNotifier).getUpdateSince( notification.updateCount, @@ -570,6 +582,7 @@ test('price drop', async t => { ); trace(t, 'debt remains', AmountMath.make(runBrand, 284n)); + // @ts-expect-error mock await E(priceAuthority).setPrice(makeRatio(1000n, runBrand, 900n, aethBrand)); trace(t, 'debt gone'); notification = await E(vaultNotifier).getUpdateSince( @@ -1691,6 +1704,7 @@ test('mutable liquidity triggers and interest', async t => { t.deepEqual(aliceUpdate.value.debtSnapshot.debt, aliceRunDebtLevel); trace(t, 'alice reduce collateral'); + // @ts-expect-error mock await E(priceAuthority).setPrice(makeRatio(7n, runBrand, 1n, aethBrand)); trace(t, 'changed price to 7'); @@ -2291,9 +2305,7 @@ test('addVaultType: invalid args do not modify state', async t => { .addVaultType(chit.issuer, kw, null), ); await failsForSameReason( - E(vaultFactory) - // @ts-expect-error bad args on purpose for test - .addVaultType(chit.issuer, 'bogus kw', params), + E(vaultFactory).addVaultType(chit.issuer, 'bogus kw', params), ); // The keyword in the vault manager is not "stuck"; it's still available: @@ -2326,6 +2338,7 @@ test('addVaultType: extra, unexpected params', async t => { }; await t.throwsAsync( + // @ts-expect-error bad args E(vaultFactory).addVaultType(chit.issuer, 'Chit', missingParams), { message: /Must have same property names/ }, ); diff --git a/packages/zoe/src/contractSupport/zoeHelpers.js b/packages/zoe/src/contractSupport/zoeHelpers.js index 6a4df1e4049..702059959b5 100644 --- a/packages/zoe/src/contractSupport/zoeHelpers.js +++ b/packages/zoe/src/contractSupport/zoeHelpers.js @@ -283,10 +283,13 @@ const reverse = (keywordRecord = {}) => { * contract instance (contractA) and depositing the payouts in another * seat in the current contract instance (contractA). * + * @template {object=} A Offer args + * @template {object=} R Offer result + * * @param {ZCF} zcf * Zoe Contract Facet for contractA * - * @param {ERef} invitation + * @param {ERef>} invitation * Invitation to contractB * * @param {KeywordKeywordRecord=} keywordMapping @@ -304,10 +307,10 @@ const reverse = (keywordRecord = {}) => { * The seat in contractA to deposit the payout of the offer to. * If `toSeat` is not provided, this defaults to the `fromSeat`. * - * @param {object=} offerArgs + * @param {A} [offerArgs] * Aditional contract-specific optional arguments in a record. * - * @returns {Promise<{userSeatPromise: Promise, deposited: Promise}>} + * @returns {Promise<{userSeatPromise: Promise>, deposited: Promise}>} * A promise for the userSeat for the offer to the other contract, and a * promise (`deposited`) which resolves when the payout for the offer has been * deposited to the `toSeat` diff --git a/packages/zoe/src/contracts/otcDesk.js b/packages/zoe/src/contracts/otcDesk.js index 8523e4bfd33..4147ebca83c 100644 --- a/packages/zoe/src/contracts/otcDesk.js +++ b/packages/zoe/src/contracts/otcDesk.js @@ -66,6 +66,7 @@ const start = zcf => { * @returns {Promise} */ const makeQuote = async (price, assets, timeAuthority, deadline) => { + /** @type {{ creatorInvitation: Invitation} } */ const { creatorInvitation } = await E(zoe).startInstance( coveredCallInstallation, zcf.getTerms().issuers, diff --git a/packages/zoe/src/types.js b/packages/zoe/src/types.js index 0e0a6c5df6a..7b6bfe65e66 100644 --- a/packages/zoe/src/types.js +++ b/packages/zoe/src/types.js @@ -43,6 +43,7 @@ */ /** + * @template [OfferArgs] * @template [OfferResult] * @typedef {Payment<'set'>} Invitation */