From de5daf495e3ce4ceeadc977ff1105f1033a44a0c Mon Sep 17 00:00:00 2001 From: Dean Tribble Date: Tue, 12 Apr 2022 16:45:36 -0700 Subject: [PATCH] refactor(vault): liquidate with offerArgs * turn off unneeded tracing * pass `debt` to liquidation as an offerArg * add trace info in liquidation --- .../src/vaultFactory/liquidateMinimum.js | 130 +++++++++--------- .../src/vaultFactory/liquidation.js | 3 +- .../run-protocol/src/vaultFactory/vault.js | 2 +- .../src/vaultFactory/vaultFactory.js | 7 +- .../src/vaultFactory/vaultManager.js | 3 +- 5 files changed, 76 insertions(+), 69 deletions(-) diff --git a/packages/run-protocol/src/vaultFactory/liquidateMinimum.js b/packages/run-protocol/src/vaultFactory/liquidateMinimum.js index 51956a78a628..1928f2b41359 100644 --- a/packages/run-protocol/src/vaultFactory/liquidateMinimum.js +++ b/packages/run-protocol/src/vaultFactory/liquidateMinimum.js @@ -8,7 +8,7 @@ import { Far } from '@endo/marshal'; import { makeDefaultLiquidationStrategy } from './liquidation.js'; import { makeTracer } from '../makeTracer.js'; -const trace = makeTracer('LM'); +const trace = makeTracer('LiqMin'); /** * This contract liquidates the minimum amount of vault's collateral necessary @@ -25,74 +25,75 @@ const start = async zcf => { const { amm } = zcf.getTerms(); /** - * @param {Amount<'nat'>} runDebt + * @param {ZCFSeat} debtorSeat + * @param {{ debt: Amount<'nat'> }} options */ - const makeDebtorHook = runDebt => { - const runBrand = runDebt.brand; - return async debtorSeat => { - const { - give: { In: amountIn }, - } = debtorSeat.getProposal(); - const inBefore = debtorSeat.getAmountAllocated('In'); - - const swapInvitation = E(amm).makeSwapOutInvitation(); - const liqProposal = harden({ - give: { In: amountIn }, - want: { Out: runDebt }, + const debtorHook = async (debtorSeat, { debt }) => { + const debtBrand = debt.brand; + const { + give: { In: amountIn }, + } = debtorSeat.getProposal(); + const swapInvitation = E(amm).makeSwapOutInvitation(); + const liqProposal = harden({ + give: { In: amountIn }, + want: { Out: debt }, + }); + trace(`OFFER TO DEBT: `, debt, amountIn); + const { deposited, userSeatPromise: liqSeat } = await offerTo( + zcf, + swapInvitation, + undefined, // The keywords were mapped already + liqProposal, + debtorSeat, + ); + + // Three (!) awaits coming here. We can't use Promise.all because + // offerTo() can return before getOfferResult() is valid, and we can't + // check whether swapIn liquidated assets until getOfferResult() returns. + // Of course, we also can't exit the seat until one or the other + // liquidation takes place. + const [offerResult, amounts, _deposited] = await Promise.all([ + E(liqSeat).getOfferResult(), + E(liqSeat).getCurrentAllocation(), + deposited, + ]); + trace('offerResult', offerResult, amounts); + + // if swapOut failed to make the trade, we'll sell it all + const sellAllIfUnsold = async () => { + const unsold = debtorSeat.getAmountAllocated('In'); + // We cannot easily directly check that the `offerTo` succeeded, so we + // check that amotun unsold is not what we started with. + if (!AmountMath.isEqual(amountIn, unsold)) { + trace('Changed', { inBefore: amountIn, unsold }); + return; + } + + trace('liquidating all collateral because swapIn did not succeed'); + const strategy = makeDefaultLiquidationStrategy(amm); + const { deposited: sellAllDeposited, userSeatPromise: sellAllSeat } = + await offerTo( + zcf, + strategy.makeInvitation(debt), + undefined, // The keywords were mapped already + strategy.makeProposal(amountIn, AmountMath.makeEmpty(debtBrand)), + debtorSeat, + ); + // await sellAllDeposited, but don't need the value + await Promise.all([ + E(sellAllSeat).getOfferResult(), + sellAllDeposited, + ]).catch(sellAllError => { + throw Error(`Unable to liquidate ${sellAllError}`); }); - trace(`OFFER TO DEBT: `, runDebt.value); - - const { deposited, userSeatPromise: liqSeat } = await offerTo( - zcf, - swapInvitation, - undefined, // The keywords were mapped already - liqProposal, - debtorSeat, - ); - - // Three (!) awaits coming here. We can't use Promise.all because - // offerTo() can return before getOfferResult() is valid, and we can't - // check whether swapIn liquidated assets until getOfferResult() returns. - // Of course, we also can't exit the seat until one or the other - // liquidation takes place. - await deposited; - await E(liqSeat).getOfferResult(); - - // if swapOut failed to make the trade, we'll sell it all - const sellAllIfUnsold = async () => { - if ( - !AmountMath.isEqual(inBefore, debtorSeat.getAmountAllocated('In')) - ) { - return; - } - - trace('liquidating all collateral because swapIn did not succeed'); - const strategy = makeDefaultLiquidationStrategy(amm); - const { deposited: sellAllDeposited, userSeatPromise: sellAllSeat } = - await offerTo( - zcf, - strategy.makeInvitation(runDebt), - undefined, // The keywords were mapped already - strategy.makeProposal(amountIn, AmountMath.makeEmpty(runBrand)), - debtorSeat, - ); - // await sellAllDeposited, but don't need the value - await Promise.all([ - E(sellAllSeat).getOfferResult(), - sellAllDeposited, - ]).catch(sellAllError => { - throw Error(`Unable to liquidate ${sellAllError}`); - }); - }; - await sellAllIfUnsold(); - - debtorSeat.exit(); }; + await sellAllIfUnsold(); + trace(`Liq results`, debt, debtorSeat.getAmountAllocated('RUN', debtBrand)); + debtorSeat.exit(); }; const creatorFacet = Far('debtorInvitationCreator', { - makeDebtorInvitation: runDebt => - zcf.makeInvitation(makeDebtorHook(runDebt), 'Liquidate'), + makeDebtorInvitation: () => zcf.makeInvitation(debtorHook, 'Liquidate'), }); return harden({ creatorFacet }); @@ -104,8 +105,7 @@ const start = async zcf => { * @param {LiquidationContract['creatorFacet']} creatorFacet */ const makeLiquidationStrategy = creatorFacet => { - const makeInvitation = async runDebt => - E(creatorFacet).makeDebtorInvitation(runDebt); + const makeInvitation = () => E(creatorFacet).makeDebtorInvitation(); const keywordMapping = () => harden({ diff --git a/packages/run-protocol/src/vaultFactory/liquidation.js b/packages/run-protocol/src/vaultFactory/liquidation.js index 90576f9f4e23..c116951c4bda 100644 --- a/packages/run-protocol/src/vaultFactory/liquidation.js +++ b/packages/run-protocol/src/vaultFactory/liquidation.js @@ -68,13 +68,14 @@ const liquidate = async ( collateralBrand, ); - // XXX problems with upgrade const { deposited, userSeatPromise: liqSeat } = await offerTo( zcf, strategy.makeInvitation(debt), strategy.keywordMapping(), strategy.makeProposal(collateralToSell, debt), vaultZcfSeat, + vaultZcfSeat, + harden({ debt }), ); trace(` offeredTo`, collateralToSell, debt); diff --git a/packages/run-protocol/src/vaultFactory/vault.js b/packages/run-protocol/src/vaultFactory/vault.js index 2af903cabe12..d272258b13a6 100644 --- a/packages/run-protocol/src/vaultFactory/vault.js +++ b/packages/run-protocol/src/vaultFactory/vault.js @@ -17,7 +17,7 @@ import { addSubtract, assertOnlyKeys, stageDelta } from '../contractSupport.js'; const { details: X, quote: q } = assert; -const trace = makeTracer('IV'); +const trace = makeTracer('IV', false); /** * @file This has most of the logic for a Vault, to borrow RUN against collateral. diff --git a/packages/run-protocol/src/vaultFactory/vaultFactory.js b/packages/run-protocol/src/vaultFactory/vaultFactory.js index 651e741246d6..d9b24b9b9e51 100644 --- a/packages/run-protocol/src/vaultFactory/vaultFactory.js +++ b/packages/run-protocol/src/vaultFactory/vaultFactory.js @@ -148,7 +148,12 @@ export const start = async (zcf, privateArgs) => { const { creatorFacet: liquidationFacet } = await E(zoe).startInstance( liquidationInstall, harden({ RUN: debtIssuer, Collateral: collateralIssuer }), - harden({ amm: ammPublicFacet }), + harden({ + amm: ammPublicFacet, + priceAuthority, + timerService, + debtBrand, + }), ); const liquidationStrategy = makeLiquidationStrategy(liquidationFacet); diff --git a/packages/run-protocol/src/vaultFactory/vaultManager.js b/packages/run-protocol/src/vaultFactory/vaultManager.js index 53ef19d1e13e..dbc8e1c7584b 100644 --- a/packages/run-protocol/src/vaultFactory/vaultManager.js +++ b/packages/run-protocol/src/vaultFactory/vaultManager.js @@ -27,7 +27,7 @@ import { chargeInterest } from '../interest.js'; const { details: X, quote: q } = assert; -const trace = makeTracer('VM'); +const trace = makeTracer('VM', false); /** * @typedef {{ @@ -157,6 +157,7 @@ export const makeVaultManager = ( liquidationInProgress = false; // XXX should notify interested parties console.error('liquidateAndRemove failed with', e); + throw e; }); };