@agoric/inter-protocol 0.14.1-dev-13c169c.0 → 0.14.1-dev-72b0aa9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/bundle-auctioneer-js-meta.json +116 -116
- package/bundles/bundle-auctioneer.js +1 -1
- package/bundles/bundle-econCommitteeCharter-js-meta.json +71 -71
- package/bundles/bundle-feeDistributor-js-meta.json +57 -57
- package/bundles/bundle-fluxAggregatorKit-js-meta.json +111 -111
- package/bundles/bundle-fluxAggregatorKit.js +1 -1
- package/bundles/bundle-psm-js-meta.json +145 -145
- package/bundles/bundle-psm.js +1 -1
- package/bundles/bundle-reserve-js-meta.json +108 -108
- package/bundles/bundle-reserve.js +1 -1
- package/bundles/bundle-scaledPriceAuthority-js-meta.json +78 -78
- package/bundles/bundle-vaultFactory-js-meta.json +136 -132
- package/bundles/bundle-vaultFactory.js +1 -1
- package/package.json +17 -17
- package/src/contractSupport.js +16 -45
- package/src/price/fluxAggregatorKit.js +1 -15
- package/src/proposals/econ-behaviors.js +4 -4
- package/src/reserve/assetReserveKit.js +1 -7
- package/src/reserve/doc.md +4 -4
- package/src/vaultFactory/Liquidation.md +1 -1
- package/src/vaultFactory/liquidation.js +8 -23
- package/src/vaultFactory/proceeds.js +274 -0
- package/src/vaultFactory/types.js +1 -10
- package/src/vaultFactory/vaultDirector.js +3 -42
- package/src/vaultFactory/vaultFactory.js +3 -3
- package/src/vaultFactory/vaultHolder.js +1 -9
- package/src/vaultFactory/vaultManager.js +163 -350
|
@@ -37,17 +37,13 @@ import {
|
|
|
37
37
|
import { TransferPartShape } from '@agoric/zoe/src/contractSupport/atomicTransfer.js';
|
|
38
38
|
import {
|
|
39
39
|
atomicRearrange,
|
|
40
|
-
ceilDivideBy,
|
|
41
40
|
ceilMultiplyBy,
|
|
42
41
|
floorDivideBy,
|
|
43
|
-
floorMultiplyBy,
|
|
44
42
|
getAmountIn,
|
|
45
43
|
getAmountOut,
|
|
46
44
|
makeEphemeraProvider,
|
|
47
45
|
makeRatio,
|
|
48
|
-
makeRatioFromAmounts,
|
|
49
46
|
makeRecorderTopic,
|
|
50
|
-
multiplyRatios,
|
|
51
47
|
offerTo,
|
|
52
48
|
SubscriberShape,
|
|
53
49
|
TopicsRecordShape,
|
|
@@ -55,12 +51,17 @@ import {
|
|
|
55
51
|
import { PriceQuoteShape, SeatShape } from '@agoric/zoe/src/typeGuards.js';
|
|
56
52
|
import { E } from '@endo/eventual-send';
|
|
57
53
|
import { AuctionPFShape } from '../auction/auctioneer.js';
|
|
58
|
-
import {
|
|
54
|
+
import {
|
|
55
|
+
checkDebtLimit,
|
|
56
|
+
makeNatAmountShape,
|
|
57
|
+
quoteAsRatio,
|
|
58
|
+
} from '../contractSupport.js';
|
|
59
59
|
import { chargeInterest } from '../interest.js';
|
|
60
|
-
import { getLiquidatableVaults
|
|
60
|
+
import { getLiquidatableVaults } from './liquidation.js';
|
|
61
61
|
import { calculateMinimumCollateralization, minimumPrice } from './math.js';
|
|
62
62
|
import { makePrioritizedVaults } from './prioritizedVaults.js';
|
|
63
63
|
import { Phase, prepareVault } from './vault.js';
|
|
64
|
+
import { calculateDistributionPlan } from './proceeds.js';
|
|
64
65
|
|
|
65
66
|
const { details: X, Fail, quote: q } = assert;
|
|
66
67
|
|
|
@@ -154,10 +155,6 @@ const trace = makeTracer('VM');
|
|
|
154
155
|
* }} MutableState
|
|
155
156
|
*/
|
|
156
157
|
|
|
157
|
-
/** @param {Pick<PriceDescription, 'amountIn' | 'amountOut'>} quoteAmount */
|
|
158
|
-
const quoteAsRatio = quoteAmount =>
|
|
159
|
-
makeRatioFromAmounts(quoteAmount.amountIn, quoteAmount.amountOut);
|
|
160
|
-
|
|
161
158
|
/**
|
|
162
159
|
* @type {(brand: Brand) => {
|
|
163
160
|
* prioritizedVaults: ReturnType<typeof makePrioritizedVaults>,
|
|
@@ -256,8 +253,6 @@ export const prepareVaultManagerKit = (
|
|
|
256
253
|
{
|
|
257
254
|
collateral: M.interface('collateral', {
|
|
258
255
|
makeVaultInvitation: M.call().returns(M.promise()),
|
|
259
|
-
getSubscriber: M.call().returns(SubscriberShape),
|
|
260
|
-
getMetrics: M.call().returns(SubscriberShape),
|
|
261
256
|
getPublicTopics: M.call().returns(TopicsRecordShape),
|
|
262
257
|
getQuotes: M.call().returns(NotifierShape),
|
|
263
258
|
getCompoundedInterest: M.call().returns(RatioShape),
|
|
@@ -320,14 +315,6 @@ export const prepareVaultManagerKit = (
|
|
|
320
315
|
}),
|
|
321
316
|
);
|
|
322
317
|
},
|
|
323
|
-
/** @deprecated use getPublicTopics */
|
|
324
|
-
getSubscriber() {
|
|
325
|
-
return this.state.assetTopicKit.subscriber;
|
|
326
|
-
},
|
|
327
|
-
/** @deprecated use getPublicTopics */
|
|
328
|
-
getMetrics() {
|
|
329
|
-
return this.state.metricsTopicKit.subscriber;
|
|
330
|
-
},
|
|
331
318
|
getQuotes() {
|
|
332
319
|
const ephemera = collateralEphemera(this.state.collateralBrand);
|
|
333
320
|
return ephemera.storedQuotesNotifier;
|
|
@@ -506,26 +493,17 @@ export const prepareVaultManagerKit = (
|
|
|
506
493
|
// don't wait for response
|
|
507
494
|
void E.when(invitation, invite => {
|
|
508
495
|
const proposal = { give: { Collateral: penalty } };
|
|
509
|
-
|
|
496
|
+
return offerTo(
|
|
510
497
|
zcf,
|
|
511
498
|
invite,
|
|
512
499
|
{ [seatKeyword]: 'Collateral' },
|
|
513
500
|
proposal,
|
|
514
501
|
seat,
|
|
515
502
|
);
|
|
503
|
+
}).catch(reason => {
|
|
504
|
+
console.error('sendToReserve failed', reason);
|
|
516
505
|
});
|
|
517
506
|
},
|
|
518
|
-
/** @type {(collatSold: Amount<'nat'>, completed: number, aborted?: number) => void} */
|
|
519
|
-
markCollateralLiquidated(collatSold, completed, aborted = 0) {
|
|
520
|
-
const { state } = this;
|
|
521
|
-
|
|
522
|
-
state.totalCollateral = AmountMath.subtract(
|
|
523
|
-
state.totalCollateral,
|
|
524
|
-
collatSold,
|
|
525
|
-
);
|
|
526
|
-
state.numLiquidationsCompleted += completed;
|
|
527
|
-
state.numLiquidationsAborted += aborted;
|
|
528
|
-
},
|
|
529
507
|
markLiquidating(debt, collateral) {
|
|
530
508
|
const { state } = this;
|
|
531
509
|
|
|
@@ -540,9 +518,10 @@ export const prepareVaultManagerKit = (
|
|
|
540
518
|
*
|
|
541
519
|
* @param {Amount<'nat'>} debt
|
|
542
520
|
* @param {Amount<'nat'>} collateral
|
|
543
|
-
* @param {
|
|
521
|
+
* @param {Amount<'nat'>} overage
|
|
522
|
+
* @param {Amount<'nat'>} shortfall
|
|
544
523
|
*/
|
|
545
|
-
markDoneLiquidating(debt, collateral,
|
|
524
|
+
markDoneLiquidating(debt, collateral, overage, shortfall) {
|
|
546
525
|
const { state } = this;
|
|
547
526
|
|
|
548
527
|
// update liquidation state
|
|
@@ -558,7 +537,6 @@ export const prepareVaultManagerKit = (
|
|
|
558
537
|
|
|
559
538
|
// record shortfall and proceeds
|
|
560
539
|
|
|
561
|
-
const { overage, shortfall } = accounting;
|
|
562
540
|
// cumulative values
|
|
563
541
|
state.totalOverageReceived = AmountMath.add(
|
|
564
542
|
state.totalOverageReceived,
|
|
@@ -582,25 +560,6 @@ export const prepareVaultManagerKit = (
|
|
|
582
560
|
console.error('🚨 failed to report liquidation shortfall', err);
|
|
583
561
|
});
|
|
584
562
|
},
|
|
585
|
-
/**
|
|
586
|
-
* If interest was charged between liquidating and liquidated, erase it.
|
|
587
|
-
*
|
|
588
|
-
* @param {Amount<'nat'>} priorDebtAmount
|
|
589
|
-
* @param {Amount<'nat'>} currentDebt
|
|
590
|
-
*/
|
|
591
|
-
subtractPhantomInterest(priorDebtAmount, currentDebt) {
|
|
592
|
-
const { state } = this;
|
|
593
|
-
const difference = AmountMath.subtract(currentDebt, priorDebtAmount);
|
|
594
|
-
|
|
595
|
-
if (!AmountMath.isEmpty(difference)) {
|
|
596
|
-
state.totalDebt = AmountMath.subtract(state.totalDebt, difference);
|
|
597
|
-
}
|
|
598
|
-
},
|
|
599
|
-
markRestoreDebt(debt) {
|
|
600
|
-
const { state } = this;
|
|
601
|
-
|
|
602
|
-
state.totalDebt = AmountMath.add(state.totalDebt, debt);
|
|
603
|
-
},
|
|
604
563
|
writeMetrics() {
|
|
605
564
|
const { state } = this;
|
|
606
565
|
const { collateralBrand, retainedCollateralSeat, metricsTopicKit } =
|
|
@@ -638,16 +597,25 @@ export const prepareVaultManagerKit = (
|
|
|
638
597
|
return E(metricsTopicKit.recorder).write(payload);
|
|
639
598
|
},
|
|
640
599
|
|
|
641
|
-
|
|
600
|
+
/**
|
|
601
|
+
* This is designed to tolerate an incomplete plan, in case calculateDistributionPlan encounters
|
|
602
|
+
* an error during its calculation. We don't have a way to induce such errors in CI so we've
|
|
603
|
+
* done so manually in dev and verified this function recovers as expected.
|
|
604
|
+
*
|
|
605
|
+
* @param {AmountKeywordRecord} proceeds
|
|
606
|
+
* @param {Amount<'nat'>} totalDebt
|
|
607
|
+
* @param {Pick<PriceQuote, 'quoteAmount'>} oraclePriceAtStart
|
|
608
|
+
* @param {MapStore<Vault, { collateralAmount: Amount<'nat'>, debtAmount: Amount<'nat'>}>} vaultData
|
|
609
|
+
* @param {Amount<'nat'>} totalCollateral
|
|
610
|
+
*/
|
|
611
|
+
planProceedsDistribution(
|
|
642
612
|
proceeds,
|
|
643
613
|
totalDebt,
|
|
644
614
|
oraclePriceAtStart,
|
|
645
|
-
liqSeat,
|
|
646
615
|
vaultData,
|
|
647
616
|
totalCollateral,
|
|
648
617
|
) {
|
|
649
618
|
const { state, facets } = this;
|
|
650
|
-
const { collateralBrand, debtBrand, liquidatingVaults } = this.state;
|
|
651
619
|
|
|
652
620
|
const { Collateral: collateralProceeds } = proceeds;
|
|
653
621
|
/** @type {Amount<'nat'>} */
|
|
@@ -660,312 +628,135 @@ export const prepareVaultManagerKit = (
|
|
|
660
628
|
collateralSold,
|
|
661
629
|
);
|
|
662
630
|
|
|
663
|
-
const mintedProceeds =
|
|
664
|
-
proceeds.Minted || AmountMath.makeEmpty(debtBrand);
|
|
665
|
-
const accounting = liquidationResults(totalDebt, mintedProceeds);
|
|
666
|
-
|
|
667
|
-
/** @type {Array<Vault>} */
|
|
668
|
-
const vaultsToLiquidate = [];
|
|
669
|
-
const liquidateAll = () => {
|
|
670
|
-
for (const vault of vaultsToLiquidate) {
|
|
671
|
-
vault.liquidated();
|
|
672
|
-
liquidatingVaults.delete(vault);
|
|
673
|
-
}
|
|
674
|
-
};
|
|
675
|
-
|
|
676
631
|
const penaltyRate = facets.self
|
|
677
632
|
.getGovernedParams()
|
|
678
633
|
.getLiquidationPenalty();
|
|
679
|
-
// charged in collateral
|
|
680
|
-
const totalPenalty = ceilMultiplyBy(
|
|
681
|
-
totalDebt,
|
|
682
|
-
multiplyRatios(
|
|
683
|
-
penaltyRate,
|
|
684
|
-
quoteAsRatio(oraclePriceAtStart.quoteAmount.value[0]),
|
|
685
|
-
),
|
|
686
|
-
);
|
|
687
|
-
const debtPortion = makeRatioFromAmounts(totalPenalty, totalDebt);
|
|
688
|
-
|
|
689
|
-
// Liquidation.md describes how to process liquidation proceeds
|
|
690
634
|
const bestToWorst = [...vaultData.entries()].reverse();
|
|
691
|
-
if (AmountMath.isEmpty(accounting.shortfall)) {
|
|
692
|
-
// Flow #1: no shortfall
|
|
693
|
-
|
|
694
|
-
const collateralToDistribute = AmountMath.isGTE(
|
|
695
|
-
collateralProceeds,
|
|
696
|
-
totalPenalty,
|
|
697
|
-
);
|
|
698
635
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
for (const [vault, amounts] of bestToWorst) {
|
|
715
|
-
const { collateralAmount: vCollat, debtAmount } = amounts;
|
|
716
|
-
facets.helper.subtractPhantomInterest(
|
|
717
|
-
debtAmount,
|
|
718
|
-
vault.getCurrentDebt(),
|
|
719
|
-
);
|
|
636
|
+
// unzip the entry tuples
|
|
637
|
+
const vaultsInPlan = /** @type {Vault[]} */ ([]);
|
|
638
|
+
const vaultsBalances =
|
|
639
|
+
/** @type {import('./proceeds.js').VaultBalances[]} */ ([]);
|
|
640
|
+
for (const [vault, balances] of bestToWorst) {
|
|
641
|
+
vaultsInPlan.push(vault);
|
|
642
|
+
vaultsBalances.push({
|
|
643
|
+
collateral: balances.collateralAmount,
|
|
644
|
+
// if interest accrued during sale, the current debt will be higher
|
|
645
|
+
presaleDebt: balances.debtAmount,
|
|
646
|
+
currentDebt: vault.getCurrentDebt(),
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
harden(vaultsInPlan);
|
|
650
|
+
harden(vaultsBalances);
|
|
720
651
|
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
652
|
+
const plan = calculateDistributionPlan({
|
|
653
|
+
proceeds,
|
|
654
|
+
totalDebt,
|
|
655
|
+
totalCollateral,
|
|
656
|
+
oraclePriceAtStart: oraclePriceAtStart.quoteAmount.value[0],
|
|
657
|
+
vaultsBalances,
|
|
658
|
+
penaltyRate,
|
|
659
|
+
});
|
|
660
|
+
return { plan, vaultsInPlan };
|
|
661
|
+
},
|
|
725
662
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
663
|
+
/**
|
|
664
|
+
* This is designed to tolerate an incomplete plan, in case calculateDistributionPlan encounters
|
|
665
|
+
* an error during its calculation. We don't have a way to induce such errors in CI so we've
|
|
666
|
+
* done so manually in dev and verified this function recovers as expected.
|
|
667
|
+
*
|
|
668
|
+
* @param {object} obj
|
|
669
|
+
* @param {import('./proceeds.js').DistributionPlan} obj.plan
|
|
670
|
+
* @param {Array<Vault>} obj.vaultsInPlan
|
|
671
|
+
* @param {ZCFSeat} obj.liqSeat
|
|
672
|
+
* @param {Amount<'nat'>} obj.totalCollateral
|
|
673
|
+
* @param {Amount<'nat'>} obj.totalDebt
|
|
674
|
+
* @returns {void}
|
|
675
|
+
*/
|
|
676
|
+
distributeProceeds({
|
|
677
|
+
plan,
|
|
678
|
+
vaultsInPlan,
|
|
679
|
+
liqSeat,
|
|
680
|
+
totalCollateral,
|
|
681
|
+
totalDebt,
|
|
682
|
+
}) {
|
|
683
|
+
const { state, facets } = this;
|
|
684
|
+
// Putting all the rearrangements after the loop ensures that errors
|
|
685
|
+
// in the calculations don't result in paying back some vaults and
|
|
686
|
+
// leaving others hanging.
|
|
687
|
+
if (plan.transfersToVault.length > 0) {
|
|
688
|
+
const transfers = plan.transfersToVault.map(
|
|
689
|
+
([vaultIndex, amounts]) =>
|
|
690
|
+
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart} */ ([
|
|
738
691
|
liqSeat,
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
])
|
|
742
|
-
}
|
|
743
|
-
vaultsToLiquidate.push(vault);
|
|
744
|
-
}
|
|
745
|
-
if (transfers.length > 0) {
|
|
746
|
-
atomicRearrange(zcf, harden(transfers));
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
const forReserve = collateralToDistribute
|
|
750
|
-
? AmountMath.add(leftToStage, totalPenalty)
|
|
751
|
-
: collateralProceeds;
|
|
752
|
-
facets.helper.sendToReserve(forReserve, liqSeat);
|
|
753
|
-
|
|
754
|
-
facets.helper.markCollateralLiquidated(
|
|
755
|
-
totalCollateral,
|
|
756
|
-
vaultData.getSize(),
|
|
757
|
-
);
|
|
758
|
-
|
|
759
|
-
facets.helper.markDoneLiquidating(
|
|
760
|
-
totalDebt,
|
|
761
|
-
totalCollateral,
|
|
762
|
-
accounting,
|
|
763
|
-
);
|
|
764
|
-
} else if (AmountMath.isEmpty(collateralProceeds)) {
|
|
765
|
-
// Flow #2a
|
|
766
|
-
|
|
767
|
-
// charge penalty if proceeds are sufficient
|
|
768
|
-
const penaltyInMinted = ceilMultiplyBy(totalDebt, penaltyRate);
|
|
769
|
-
const recoveredDebt = AmountMath.min(
|
|
770
|
-
AmountMath.add(totalDebt, penaltyInMinted),
|
|
771
|
-
mintedProceeds,
|
|
772
|
-
);
|
|
773
|
-
|
|
774
|
-
facets.helper.burnToCoverDebt(
|
|
775
|
-
recoveredDebt,
|
|
776
|
-
mintedProceeds,
|
|
777
|
-
liqSeat,
|
|
692
|
+
vaultsInPlan[vaultIndex].getVaultSeat(),
|
|
693
|
+
amounts,
|
|
694
|
+
]),
|
|
778
695
|
);
|
|
696
|
+
atomicRearrange(zcf, harden(transfers));
|
|
697
|
+
}
|
|
779
698
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
const
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
|
|
791
|
-
const transfers = [];
|
|
792
|
-
|
|
793
|
-
// iterate from best to worst returning remaining funds to vaults
|
|
794
|
-
/** @type {Array<[Vault, { collateralAmount: Amount<'nat'>, debtAmount: Amount<'nat'>}]>} */
|
|
795
|
-
for (const [vault, balance] of bestToWorst) {
|
|
796
|
-
// from best to worst, return minted above penalty if any remains
|
|
797
|
-
const { collateralAmount: vCollat, debtAmount } = balance;
|
|
798
|
-
const vaultShare = floorMultiplyBy(vCollat, vaultPortion);
|
|
799
|
-
facets.helper.subtractPhantomInterest(
|
|
800
|
-
debtAmount,
|
|
801
|
-
vault.getCurrentDebt(),
|
|
802
|
-
);
|
|
803
|
-
|
|
804
|
-
if (!AmountMath.isEmpty(mintedRemaining)) {
|
|
805
|
-
const mintedToReturn = AmountMath.isGTE(
|
|
806
|
-
mintedRemaining,
|
|
807
|
-
vaultShare,
|
|
808
|
-
)
|
|
809
|
-
? vaultShare
|
|
810
|
-
: mintedRemaining;
|
|
811
|
-
mintedRemaining = AmountMath.subtract(
|
|
812
|
-
mintedRemaining,
|
|
813
|
-
mintedToReturn,
|
|
814
|
-
);
|
|
815
|
-
const seat = vault.getVaultSeat();
|
|
816
|
-
transfers.push([liqSeat, seat, { Minted: mintedToReturn }]);
|
|
817
|
-
}
|
|
818
|
-
vaultsToLiquidate.push(vault);
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
if (transfers.length > 0) {
|
|
822
|
-
atomicRearrange(zcf, harden(transfers));
|
|
823
|
-
}
|
|
699
|
+
const { prioritizedVaults } = collateralEphemera(
|
|
700
|
+
totalCollateral.brand,
|
|
701
|
+
);
|
|
702
|
+
state.numLiquidationsAborted += plan.vaultsToReinstate.length;
|
|
703
|
+
for (const vaultIndex of plan.vaultsToReinstate) {
|
|
704
|
+
const vault = vaultsInPlan[vaultIndex];
|
|
705
|
+
const vaultId = vault.abortLiquidation();
|
|
706
|
+
prioritizedVaults.addVault(vaultId, vault);
|
|
707
|
+
state.liquidatingVaults.delete(vault);
|
|
708
|
+
}
|
|
824
709
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
710
|
+
if (!AmountMath.isEmpty(plan.phantomDebt)) {
|
|
711
|
+
state.totalDebt = AmountMath.subtract(
|
|
712
|
+
state.totalDebt,
|
|
713
|
+
plan.phantomDebt,
|
|
828
714
|
);
|
|
715
|
+
}
|
|
829
716
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
717
|
+
facets.helper.burnToCoverDebt(
|
|
718
|
+
plan.debtToBurn,
|
|
719
|
+
plan.mintedProceeds,
|
|
720
|
+
liqSeat,
|
|
721
|
+
);
|
|
722
|
+
if (!AmountMath.isEmpty(plan.mintedForReserve)) {
|
|
723
|
+
facets.helper.sendToReserve(
|
|
724
|
+
plan.mintedForReserve,
|
|
725
|
+
liqSeat,
|
|
726
|
+
'Minted',
|
|
834
727
|
);
|
|
835
|
-
}
|
|
836
|
-
// Flow #2b: There's unsold collateral; some vaults may be revived.
|
|
837
|
-
|
|
838
|
-
facets.helper.burnToCoverDebt(totalDebt, mintedProceeds, liqSeat);
|
|
839
|
-
facets.helper.sendToReserve(accounting.overage, liqSeat, 'Minted');
|
|
728
|
+
}
|
|
840
729
|
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
730
|
+
// send all that's left in the seat
|
|
731
|
+
const collateralInLiqSeat = liqSeat.getCurrentAllocation().Collateral;
|
|
732
|
+
if (!AmountMath.isEmpty(collateralInLiqSeat)) {
|
|
733
|
+
facets.helper.sendToReserve(collateralInLiqSeat, liqSeat);
|
|
734
|
+
}
|
|
735
|
+
// if it didn't match what was expected, report
|
|
736
|
+
if (!AmountMath.isEqual(collateralInLiqSeat, plan.collatRemaining)) {
|
|
737
|
+
console.error(
|
|
738
|
+
`⚠️ Excess collateral remaining sent to reserve. Expected ${q(
|
|
739
|
+
plan.collatRemaining,
|
|
740
|
+
)}, sent ${q(collateralInLiqSeat)}`,
|
|
845
741
|
);
|
|
742
|
+
}
|
|
846
743
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
/** @type {import('@agoric/zoe/src/contractSupport/atomicTransfer.js').TransferPart[]} */
|
|
854
|
-
const transfers = [];
|
|
855
|
-
let liquidated = 0;
|
|
856
|
-
/** @type {Array<Vault>} */
|
|
857
|
-
const vaultsToReinstate = [];
|
|
858
|
-
const reinstateAll = () => {
|
|
859
|
-
const { prioritizedVaults } = collateralEphemera(collateralBrand);
|
|
860
|
-
for (const vault of vaultsToReinstate) {
|
|
861
|
-
const vaultId = vault.abortLiquidation();
|
|
862
|
-
prioritizedVaults.addVault(vaultId, vault);
|
|
863
|
-
liquidatingVaults.delete(vault);
|
|
864
|
-
}
|
|
865
|
-
};
|
|
866
|
-
|
|
867
|
-
let collateralReduction = AmountMath.makeEmpty(collateralBrand);
|
|
868
|
-
let shortfallToReserve = accounting.shortfall;
|
|
869
|
-
const reduceCollateral = amount =>
|
|
870
|
-
(collateralReduction = AmountMath.add(
|
|
871
|
-
collateralReduction,
|
|
872
|
-
amount,
|
|
873
|
-
));
|
|
874
|
-
|
|
875
|
-
// iterate from best to worst attempting to reconstitute, by
|
|
876
|
-
// returning remaining funds to vaults
|
|
877
|
-
/** @type {Array<[Vault, { collateralAmount: Amount<'nat'>, debtAmount: Amount<'nat'>}]>} */
|
|
878
|
-
for (const [vault, balance] of bestToWorst) {
|
|
879
|
-
const { collateralAmount: vCollat, debtAmount } = balance;
|
|
880
|
-
|
|
881
|
-
// according to #7123, Collateral for penalty =
|
|
882
|
-
// vault debt / total debt * total liquidation penalty
|
|
883
|
-
const vaultPenalty = ceilMultiplyBy(
|
|
884
|
-
debtAmount,
|
|
885
|
-
makeRatioFromAmounts(totalPenalty, totalDebt),
|
|
886
|
-
);
|
|
887
|
-
const collatPostPenalty = AmountMath.subtract(
|
|
888
|
-
vCollat,
|
|
889
|
-
vaultPenalty,
|
|
890
|
-
);
|
|
891
|
-
const vaultDebt = floorMultiplyBy(debtAmount, debtPortion);
|
|
892
|
-
if (
|
|
893
|
-
reconstituteVaults &&
|
|
894
|
-
!AmountMath.isEmpty(collatPostPenalty) &&
|
|
895
|
-
AmountMath.isGTE(collatRemaining, collatPostPenalty) &&
|
|
896
|
-
AmountMath.isGTE(totalDebt, debtAmount)
|
|
897
|
-
) {
|
|
898
|
-
collatRemaining = AmountMath.subtract(
|
|
899
|
-
collatRemaining,
|
|
900
|
-
collatPostPenalty,
|
|
901
|
-
);
|
|
902
|
-
shortfallToReserve = AmountMath.isGTE(
|
|
903
|
-
shortfallToReserve,
|
|
904
|
-
debtAmount,
|
|
905
|
-
)
|
|
906
|
-
? AmountMath.subtract(shortfallToReserve, debtAmount)
|
|
907
|
-
: AmountMath.makeEmptyFromAmount(shortfallToReserve);
|
|
908
|
-
const seat = vault.getVaultSeat();
|
|
909
|
-
// must reinstate after atomicRearrange(), so we record them.
|
|
910
|
-
vaultsToReinstate.push(vault);
|
|
911
|
-
reduceCollateral(vaultDebt);
|
|
912
|
-
transfers.push([
|
|
913
|
-
liqSeat,
|
|
914
|
-
seat,
|
|
915
|
-
{ Collateral: collatPostPenalty },
|
|
916
|
-
]);
|
|
917
|
-
} else {
|
|
918
|
-
reconstituteVaults = false;
|
|
919
|
-
liquidated += 1;
|
|
920
|
-
facets.helper.subtractPhantomInterest(
|
|
921
|
-
debtAmount,
|
|
922
|
-
vault.getCurrentDebt(),
|
|
923
|
-
);
|
|
924
|
-
|
|
925
|
-
reduceCollateral(vCollat);
|
|
926
|
-
vaultsToLiquidate.push(vault);
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
// Putting all the rearrangements after the loop ensures that errors
|
|
931
|
-
// in the calculations don't result in paying back some vaults and
|
|
932
|
-
// leaving others hanging.
|
|
933
|
-
if (transfers.length > 0) {
|
|
934
|
-
atomicRearrange(zcf, harden(transfers));
|
|
935
|
-
}
|
|
936
|
-
reinstateAll();
|
|
937
|
-
|
|
938
|
-
facets.helper.markCollateralLiquidated(
|
|
939
|
-
collateralReduction,
|
|
940
|
-
liquidated,
|
|
941
|
-
transfers.length,
|
|
942
|
-
);
|
|
744
|
+
// 'totalCollateralSold' is only for this liquidation event
|
|
745
|
+
// 'state.totalCollateralSold' represents all active vaults
|
|
746
|
+
const actualCollateralSold = plan.actualCollateralSold;
|
|
747
|
+
state.totalCollateral = AmountMath.isEmpty(actualCollateralSold)
|
|
748
|
+
? AmountMath.subtract(state.totalCollateral, totalCollateral)
|
|
749
|
+
: AmountMath.subtract(state.totalCollateral, actualCollateralSold);
|
|
943
750
|
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
751
|
+
facets.helper.markDoneLiquidating(
|
|
752
|
+
totalDebt,
|
|
753
|
+
totalCollateral,
|
|
754
|
+
plan.overage,
|
|
755
|
+
plan.shortfallToReserve,
|
|
756
|
+
);
|
|
947
757
|
|
|
948
|
-
if (
|
|
949
|
-
!AmountMath.isEqual(
|
|
950
|
-
collateralInLiqSeat,
|
|
951
|
-
AmountMath.add(collatRemaining, totalPenalty),
|
|
952
|
-
)
|
|
953
|
-
) {
|
|
954
|
-
console.error(
|
|
955
|
-
`🚨 Excess collateral remaining sent to reserve. Expected ${q(
|
|
956
|
-
collatRemaining,
|
|
957
|
-
)}, sent ${q(collateralInLiqSeat)}`,
|
|
958
|
-
);
|
|
959
|
-
}
|
|
960
|
-
facets.helper.markDoneLiquidating(totalDebt, totalCollateral, {
|
|
961
|
-
...accounting,
|
|
962
|
-
shortfall: shortfallToReserve,
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
758
|
// liqSeat should be empty at this point, except that funds are sent
|
|
966
759
|
// asynchronously to the reserve.
|
|
967
|
-
liquidateAll();
|
|
968
|
-
return facets.helper.writeMetrics();
|
|
969
760
|
},
|
|
970
761
|
},
|
|
971
762
|
|
|
@@ -1278,6 +1069,7 @@ export const prepareVaultManagerKit = (
|
|
|
1278
1069
|
|
|
1279
1070
|
const liqMargin = self.getGovernedParams().getLiquidationMargin();
|
|
1280
1071
|
|
|
1072
|
+
// totals *among* vaults being liquidated
|
|
1281
1073
|
const { totalDebt, totalCollateral, vaultData, liqSeat } =
|
|
1282
1074
|
getLiquidatableVaults(
|
|
1283
1075
|
zcf,
|
|
@@ -1330,14 +1122,35 @@ export const prepareVaultManagerKit = (
|
|
|
1330
1122
|
);
|
|
1331
1123
|
|
|
1332
1124
|
trace(`LiqV after long wait`, proceeds);
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1125
|
+
try {
|
|
1126
|
+
const { plan, vaultsInPlan } = helper.planProceedsDistribution(
|
|
1127
|
+
proceeds,
|
|
1128
|
+
totalDebt,
|
|
1129
|
+
storedCollateralQuote,
|
|
1130
|
+
vaultData,
|
|
1131
|
+
totalCollateral,
|
|
1132
|
+
);
|
|
1133
|
+
trace('PLAN', plan);
|
|
1134
|
+
// distributeProceeds may reconstitute vaults, removing them from liquidatingVaults
|
|
1135
|
+
helper.distributeProceeds({
|
|
1136
|
+
liqSeat,
|
|
1137
|
+
plan,
|
|
1138
|
+
totalCollateral,
|
|
1139
|
+
totalDebt,
|
|
1140
|
+
vaultsInPlan,
|
|
1141
|
+
});
|
|
1142
|
+
} catch (err) {
|
|
1143
|
+
console.error('🚨 Error distributing proceeds:', err);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
// for all non-reconstituted vaults, transition to 'liquidated' state
|
|
1147
|
+
state.numLiquidationsCompleted += liquidatingVaults.getSize();
|
|
1148
|
+
for (const vault of liquidatingVaults.values()) {
|
|
1149
|
+
vault.liquidated();
|
|
1150
|
+
liquidatingVaults.delete(vault);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
await facets.helper.writeMetrics();
|
|
1341
1154
|
},
|
|
1342
1155
|
},
|
|
1343
1156
|
},
|