@agoric/inter-protocol 0.13.2-dev-c693f8d.0 → 0.13.2-dev-d5b2b97.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agoric/inter-protocol",
3
- "version": "0.13.2-dev-c693f8d.0+c693f8d",
3
+ "version": "0.13.2-dev-d5b2b97.0+d5b2b97",
4
4
  "description": "Core cryptoeconomy contracts",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -29,18 +29,18 @@
29
29
  },
30
30
  "homepage": "https://github.com/Agoric/agoric-sdk#readme",
31
31
  "dependencies": {
32
- "@agoric/assert": "0.5.2-dev-c693f8d.0+c693f8d",
33
- "@agoric/ertp": "0.15.4-dev-c693f8d.0+c693f8d",
34
- "@agoric/governance": "0.9.2-dev-c693f8d.0+c693f8d",
35
- "@agoric/internal": "0.2.2-dev-c693f8d.0+c693f8d",
36
- "@agoric/notifier": "0.5.2-dev-c693f8d.0+c693f8d",
37
- "@agoric/store": "0.8.4-dev-c693f8d.0+c693f8d",
38
- "@agoric/swingset-liveslots": "0.9.1-dev-c693f8d.0+c693f8d",
39
- "@agoric/swingset-vat": "0.30.3-dev-c693f8d.0+c693f8d",
40
- "@agoric/time": "0.2.2-dev-c693f8d.0+c693f8d",
41
- "@agoric/vat-data": "0.4.4-dev-c693f8d.0+c693f8d",
42
- "@agoric/vats": "0.13.1-dev-c693f8d.0+c693f8d",
43
- "@agoric/zoe": "0.25.4-dev-c693f8d.0+c693f8d",
32
+ "@agoric/assert": "0.5.2-dev-d5b2b97.0+d5b2b97",
33
+ "@agoric/ertp": "0.15.4-dev-d5b2b97.0+d5b2b97",
34
+ "@agoric/governance": "0.9.2-dev-d5b2b97.0+d5b2b97",
35
+ "@agoric/internal": "0.2.2-dev-d5b2b97.0+d5b2b97",
36
+ "@agoric/notifier": "0.5.2-dev-d5b2b97.0+d5b2b97",
37
+ "@agoric/store": "0.8.4-dev-d5b2b97.0+d5b2b97",
38
+ "@agoric/swingset-liveslots": "0.9.1-dev-d5b2b97.0+d5b2b97",
39
+ "@agoric/swingset-vat": "0.30.3-dev-d5b2b97.0+d5b2b97",
40
+ "@agoric/time": "0.2.2-dev-d5b2b97.0+d5b2b97",
41
+ "@agoric/vat-data": "0.4.4-dev-d5b2b97.0+d5b2b97",
42
+ "@agoric/vats": "0.13.1-dev-d5b2b97.0+d5b2b97",
43
+ "@agoric/zoe": "0.25.4-dev-d5b2b97.0+d5b2b97",
44
44
  "@endo/bundle-source": "^2.4.2",
45
45
  "@endo/captp": "^2.0.18",
46
46
  "@endo/eventual-send": "^0.16.8",
@@ -50,8 +50,8 @@
50
50
  "@endo/promise-kit": "^0.2.52"
51
51
  },
52
52
  "devDependencies": {
53
- "@agoric/deploy-script-support": "0.9.5-dev-c693f8d.0+c693f8d",
54
- "@agoric/smart-wallet": "0.4.3-dev-c693f8d.0+c693f8d",
53
+ "@agoric/deploy-script-support": "0.9.5-dev-d5b2b97.0+d5b2b97",
54
+ "@agoric/smart-wallet": "0.4.3-dev-d5b2b97.0+d5b2b97",
55
55
  "@endo/init": "^0.5.52",
56
56
  "@fast-check/ava": "^1.1.3",
57
57
  "ava": "^5.1.0",
@@ -78,5 +78,5 @@
78
78
  "publishConfig": {
79
79
  "access": "public"
80
80
  },
81
- "gitHead": "c693f8d40e265277a711667b273c647adffb2191"
81
+ "gitHead": "d5b2b97c7023aec5ec5bbf4b58eada61b28cbd2d"
82
82
  }
@@ -102,6 +102,7 @@ const defaultConfig = /** @type {const} */ ({
102
102
  * liquidationPenalty: Rational,
103
103
  * interestRate: Rational,
104
104
  * loanFee: Rational,
105
+ * liquidationPadding?: Rational,
105
106
  * },
106
107
  * trades: Array<{ central: number, collateral: bigint}>
107
108
  * }>}
@@ -448,6 +449,10 @@ export const poolRates = (issuerName, record, kits, central) => {
448
449
  liquidationPenalty: toRatio(config.liquidationPenalty, central.brand),
449
450
  interestRate: toRatio(config.interestRate, central.brand),
450
451
  loanFee: toRatio(config.loanFee, central.brand),
452
+ // XXX not relevant to AMM pools but poolRates is also used for addVaultType
453
+ liquidationPadding:
454
+ config.liquidationPadding &&
455
+ toRatio(config.liquidationPadding, central.brand),
451
456
  };
452
457
  return { rates, initialValue: inCollateral(config.collateralValue) };
453
458
  };
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @file calculations specific to the Vault Factory contract
3
+ * See also ../interest-math.js
4
+ */
5
+
6
+ import { AmountMath } from '@agoric/ertp';
7
+ import { getAmountOut } from '@agoric/zoe/src/contractSupport/priceQuote.js';
8
+ import {
9
+ addRatios,
10
+ ceilMultiplyBy,
11
+ floorDivideBy,
12
+ } from '@agoric/zoe/src/contractSupport/ratio.js';
13
+ import { addSubtract } from '../contractSupport.js';
14
+
15
+ /**
16
+ * Calculate the minimum collateralization given the liquidation margin and the "padding"
17
+ * from that liquidation threshold.
18
+ *
19
+ * @param {Ratio} liquidationMargin
20
+ * @param {Ratio} liquidationPadding
21
+ * @returns {Ratio}
22
+ */
23
+ export const calculateMinimumCollateralization = (
24
+ liquidationMargin,
25
+ liquidationPadding,
26
+ ) => addRatios(liquidationMargin, liquidationPadding);
27
+
28
+ /**
29
+ * Calculate the maximum debt allowed based on the price quote and the lesser of
30
+ * the `liquidationMargin` or the `liquidationPadding`.
31
+ *
32
+ * @param {PriceQuote} quoteAmount
33
+ * @param {Ratio} liquidationMargin
34
+ * @param {Ratio} liquidationPadding
35
+ * @returns {Amount<'nat'>}
36
+ */
37
+ export const maxDebtForVault = (
38
+ quoteAmount,
39
+ liquidationMargin,
40
+ liquidationPadding,
41
+ ) => {
42
+ const debtByQuote = getAmountOut(quoteAmount);
43
+ const minimumCollateralization = calculateMinimumCollateralization(
44
+ liquidationMargin,
45
+ liquidationPadding,
46
+ );
47
+ // floorDivide because we want the debt ceiling lower
48
+ return floorDivideBy(debtByQuote, minimumCollateralization);
49
+ };
50
+
51
+ /**
52
+ * Calculate the fee, the amount to mint and the resulting debt.
53
+ * The give and the want together reflect a delta, where typically
54
+ * one is zero because they come from the gave/want of an offer
55
+ * proposal. If the `want` is zero, the `fee` will also be zero,
56
+ * so the simple math works.
57
+ *
58
+ * @param {Amount<'nat'>} currentDebt
59
+ * @param {Amount<'nat'>} give excess of currentDebt is returned in 'surplus'
60
+ * @param {Amount<'nat'>} want
61
+ * @param {Ratio} loanFee
62
+ */
63
+ export const calculateLoanCosts = (currentDebt, give, want, loanFee) => {
64
+ const maxGive = AmountMath.min(currentDebt, give);
65
+ const surplus = AmountMath.subtract(give, maxGive);
66
+ const fee = ceilMultiplyBy(want, loanFee);
67
+ const toMint = AmountMath.add(want, fee);
68
+ const newDebt = addSubtract(currentDebt, toMint, maxGive);
69
+ return { newDebt, toMint, fee, surplus };
70
+ };
@@ -2,13 +2,14 @@ import './types.js';
2
2
 
3
3
  import {
4
4
  CONTRACT_ELECTORATE,
5
- makeParamManagerSync,
6
5
  makeParamManager,
6
+ makeParamManagerSync,
7
7
  ParamTypes,
8
8
  } from '@agoric/governance';
9
9
  import { makeStoredPublisherKit } from '@agoric/notifier';
10
10
  import { M } from '@agoric/store';
11
11
  import { TimeMath } from '@agoric/time';
12
+ import { subtractRatios } from '@agoric/zoe/src/contractSupport/ratio.js';
12
13
  import { amountPattern, ratioPattern } from '../contractSupport.js';
13
14
 
14
15
  export const CHARGING_PERIOD_KEY = 'ChargingPeriod';
@@ -16,6 +17,7 @@ export const RECORDING_PERIOD_KEY = 'RecordingPeriod';
16
17
 
17
18
  export const DEBT_LIMIT_KEY = 'DebtLimit';
18
19
  export const LIQUIDATION_MARGIN_KEY = 'LiquidationMargin';
20
+ export const LIQUIDATION_PADDING_KEY = 'LiquidationPadding';
19
21
  export const LIQUIDATION_PENALTY_KEY = 'LiquidationPenalty';
20
22
  export const INTEREST_RATE_KEY = 'InterestRate';
21
23
  export const LOAN_FEE_KEY = 'LoanFee';
@@ -69,27 +71,48 @@ harden(makeVaultDirectorParams);
69
71
 
70
72
  /** @typedef {import('@agoric/governance/src/contractGovernance/typedParamManager').ParamTypesMapFromRecord<ReturnType<typeof makeVaultDirectorParams>>} VaultDirectorParams */
71
73
 
74
+ /** @type {(liquidationMargin: Ratio) => Ratio} */
75
+ const zeroRatio = liquidationMargin =>
76
+ subtractRatios(liquidationMargin, liquidationMargin);
77
+
72
78
  /**
73
79
  * @param {import('@agoric/notifier').StoredPublisherKit<GovernanceSubscriptionState>} publisherKit
74
80
  * @param {VaultManagerParamValues} initial
75
81
  */
76
- export const makeVaultParamManager = (publisherKit, initial) =>
82
+ export const makeVaultParamManager = (
83
+ publisherKit,
84
+ {
85
+ debtLimit,
86
+ interestRate,
87
+ liquidationMargin,
88
+ liquidationPadding = zeroRatio(liquidationMargin),
89
+ liquidationPenalty,
90
+ loanFee,
91
+ },
92
+ ) =>
77
93
  makeParamManagerSync(publisherKit, {
78
- [DEBT_LIMIT_KEY]: [ParamTypes.AMOUNT, initial.debtLimit],
79
- [LIQUIDATION_MARGIN_KEY]: [ParamTypes.RATIO, initial.liquidationMargin],
80
- [LIQUIDATION_PENALTY_KEY]: [ParamTypes.RATIO, initial.liquidationPenalty],
81
- [INTEREST_RATE_KEY]: [ParamTypes.RATIO, initial.interestRate],
82
- [LOAN_FEE_KEY]: [ParamTypes.RATIO, initial.loanFee],
94
+ [DEBT_LIMIT_KEY]: [ParamTypes.AMOUNT, debtLimit],
95
+ [INTEREST_RATE_KEY]: [ParamTypes.RATIO, interestRate],
96
+ [LIQUIDATION_PADDING_KEY]: [ParamTypes.RATIO, liquidationPadding],
97
+ [LIQUIDATION_MARGIN_KEY]: [ParamTypes.RATIO, liquidationMargin],
98
+ [LIQUIDATION_PENALTY_KEY]: [ParamTypes.RATIO, liquidationPenalty],
99
+ [LOAN_FEE_KEY]: [ParamTypes.RATIO, loanFee],
83
100
  });
84
101
  /** @typedef {ReturnType<typeof makeVaultParamManager>} VaultParamManager */
85
102
 
86
- export const vaultParamPattern = M.splitRecord({
87
- liquidationMargin: ratioPattern,
88
- liquidationPenalty: ratioPattern,
89
- interestRate: ratioPattern,
90
- loanFee: ratioPattern,
91
- debtLimit: amountPattern,
92
- });
103
+ export const vaultParamPattern = M.splitRecord(
104
+ {
105
+ liquidationMargin: ratioPattern,
106
+ liquidationPenalty: ratioPattern,
107
+ interestRate: ratioPattern,
108
+ loanFee: ratioPattern,
109
+ debtLimit: amountPattern,
110
+ },
111
+ {
112
+ // optional for backwards compatibility, e.g. with loadgen
113
+ liquidationPadding: ratioPattern,
114
+ },
115
+ );
93
116
 
94
117
  /**
95
118
  * @param {import('@agoric/notifier').StoredPublisherKit<GovernanceSubscriptionState>} publisherKit
@@ -35,6 +35,7 @@
35
35
  * @property {Ratio} loanFee - The fee (in BasisPoints) charged when opening
36
36
  * or increasing a loan.
37
37
  * @property {Amount<'nat'>} debtLimit
38
+ * @property {Ratio} [liquidationPadding] - vault must maintain this in order to remove collateral or add debt
38
39
  */
39
40
 
40
41
  /**
@@ -5,7 +5,6 @@ import { M, prepareExoClassKit } from '@agoric/vat-data';
5
5
  import {
6
6
  assertProposalShape,
7
7
  atomicTransfer,
8
- ceilMultiplyBy,
9
8
  floorMultiplyBy,
10
9
  makeRatioFromAmounts,
11
10
  } from '@agoric/zoe/src/contractSupport/index.js';
@@ -21,6 +20,7 @@ import { UnguardedHelperI } from '../typeGuards.js';
21
20
  import { prepareVaultKit } from './vaultKit.js';
22
21
 
23
22
  import '@agoric/zoe/exported.js';
23
+ import { calculateLoanCosts } from './math.js';
24
24
 
25
25
  const { quote: q, Fail } = assert;
26
26
 
@@ -126,7 +126,18 @@ const validTransitions = {
126
126
  * @param {Amount<'nat'>} newDebt
127
127
  * @returns {boolean}
128
128
  */
129
- const checkRestart = (newCollateralPre, maxDebtPre, newCollateral, newDebt) => {
129
+ const allocationsChangedSinceQuote = (
130
+ newCollateralPre,
131
+ maxDebtPre,
132
+ newCollateral,
133
+ newDebt,
134
+ ) => {
135
+ trace('allocationsChangedSinceQuote', {
136
+ newCollateralPre,
137
+ maxDebtPre,
138
+ newCollateral,
139
+ newDebt,
140
+ });
130
141
  if (AmountMath.isGTE(newCollateralPre, newCollateral)) {
131
142
  // The collateral did not go up. If the collateral decreased, we pro-rate maxDebt.
132
143
  // We can pro-rate maxDebt because the quote is either linear (price is
@@ -222,6 +233,30 @@ export const prepareVault = (baggage, marshaller, zcf) => {
222
233
  emptyDebt() {
223
234
  return AmountMath.makeEmpty(this.facets.helper.debtBrand());
224
235
  },
236
+ /**
237
+ * @typedef {{ give: { Collateral: Amount<'nat'>, Minted: Amount<'nat'> }, want: { Collateral: Amount<'nat'>, Minted: Amount<'nat'> } }} FullProposal
238
+ */
239
+ /**
240
+ * @param {ProposalRecord} partial
241
+ * @returns {FullProposal}
242
+ */
243
+ fullProposal(partial) {
244
+ assertOnlyKeys(partial, ['Collateral', 'Minted']);
245
+ return {
246
+ give: {
247
+ Collateral:
248
+ partial.give?.Collateral ||
249
+ this.facets.helper.emptyCollateral(),
250
+ Minted: partial.give?.Minted || this.facets.helper.emptyDebt(),
251
+ },
252
+ want: {
253
+ Collateral:
254
+ partial.want?.Collateral ||
255
+ this.facets.helper.emptyCollateral(),
256
+ Minted: partial.want?.Minted || this.facets.helper.emptyDebt(),
257
+ },
258
+ };
259
+ },
225
260
  // #endregion
226
261
 
227
262
  // #region Phase logic
@@ -459,13 +494,12 @@ export const prepareVault = (baggage, marshaller, zcf) => {
459
494
  loanFee(currentDebt, giveAmount, wantAmount) {
460
495
  const { state } = this;
461
496
 
462
- const fee = ceilMultiplyBy(
497
+ return calculateLoanCosts(
498
+ currentDebt,
499
+ giveAmount,
463
500
  wantAmount,
464
501
  state.manager.getGovernedParams().getLoanFee(),
465
502
  );
466
- const toMint = AmountMath.add(wantAmount, fee);
467
- const newDebt = addSubtract(currentDebt, toMint, giveAmount);
468
- return { newDebt, toMint, fee };
469
503
  },
470
504
 
471
505
  /**
@@ -480,77 +514,145 @@ export const prepareVault = (baggage, marshaller, zcf) => {
480
514
  const { self, helper } = facets;
481
515
  const { outerUpdater: updaterPre } = state;
482
516
  const { vaultSeat } = state;
483
- const proposal = clientSeat.getProposal();
484
- assertOnlyKeys(proposal, ['Collateral', 'Minted']);
517
+ const fp = helper.fullProposal(clientSeat.getProposal());
518
+
519
+ if (
520
+ allEmpty([
521
+ fp.give.Collateral,
522
+ fp.give.Minted,
523
+ fp.want.Collateral,
524
+ fp.want.Minted,
525
+ ])
526
+ ) {
527
+ clientSeat.exit();
528
+ return 'no transaction, as requested';
529
+ }
530
+
531
+ // Calculate the fee, the amount to mint and the resulting debt. We'll
532
+ // verify that the target debt doesn't violate the collateralization ratio,
533
+ // then mint, reallocate, and burn.
534
+ const { newDebt, fee, surplus, toMint } = helper.loanFee(
535
+ self.getCurrentDebt(),
536
+ fp.give.Minted,
537
+ fp.want.Minted,
538
+ );
485
539
 
486
540
  const normalizedDebtPre = self.getNormalizedDebt();
487
541
  const collateralPre = helper.getCollateralAllocated(vaultSeat);
488
-
489
- const giveColl = proposal.give.Collateral || helper.emptyCollateral();
490
- const wantColl = proposal.want.Collateral || helper.emptyCollateral();
542
+ const hasWants = !allEmpty([fp.want.Collateral, fp.want.Minted]);
543
+
544
+ if (!hasWants) {
545
+ // allow deposits regardless of max debt allowed
546
+ return helper.commitBalanceAdjustment(
547
+ clientSeat,
548
+ fp,
549
+ {
550
+ newDebt,
551
+ fee,
552
+ surplus,
553
+ toMint,
554
+ },
555
+ { normalizedDebtPre, collateralPre },
556
+ );
557
+ }
491
558
 
492
559
  const newCollateralPre = addSubtract(
493
560
  collateralPre,
494
- giveColl,
495
- wantColl,
561
+ fp.give.Collateral,
562
+ fp.want.Collateral,
496
563
  );
497
- // max debt supported by current Collateral as modified by proposal
564
+ // max debt supported by the vault Collateral implied by the proposal
498
565
  const maxDebtPre = await state.manager.maxDebtFor(newCollateralPre);
499
566
  updaterPre === state.outerUpdater ||
500
567
  Fail`Transfer during vault adjustment`;
501
568
  helper.assertActive();
502
569
 
503
- // After the `await`, we retrieve the vault's allocations again,
504
- // so we can compare to the debt limit based on the new values.
505
- const collateral = helper.getCollateralAllocated(vaultSeat);
506
- const newCollateral = addSubtract(collateral, giveColl, wantColl);
507
-
508
- const debt = self.getCurrentDebt();
509
- const giveMinted = AmountMath.min(
510
- proposal.give.Minted || helper.emptyDebt(),
511
- debt,
512
- );
513
- const wantMinted = proposal.want.Minted || helper.emptyDebt();
514
- if (allEmpty([giveColl, giveMinted, wantColl, wantMinted])) {
515
- clientSeat.exit();
516
- return 'no transaction, as requested';
517
- }
518
-
519
- // Calculate the fee, the amount to mint and the resulting debt. We'll
520
- // verify that the target debt doesn't violate the collateralization ratio,
521
- // then mint, reallocate, and burn.
522
- const { newDebt, fee, toMint } = helper.loanFee(
523
- debt,
524
- giveMinted,
525
- wantMinted,
526
- );
527
-
528
- trace('adjustBalancesHook', state.idInManager, {
570
+ trace('adjustBalancesHook after quote', state.idInManager, {
529
571
  newCollateralPre,
530
- newCollateral,
531
572
  fee,
532
573
  toMint,
574
+ maxDebtPre,
533
575
  newDebt,
534
576
  });
535
577
 
578
+ // While awaiting maxDebtFor, the allocations may have changed.
579
+ // After the `await`, we retrieve the vault's allocations again,
580
+ // so we can compare to the debt limit based on the new values.
581
+ const collateral = helper.getCollateralAllocated(vaultSeat);
582
+ const newCollateral = addSubtract(
583
+ collateral,
584
+ fp.give.Collateral,
585
+ fp.want.Collateral,
586
+ );
536
587
  if (
537
- checkRestart(newCollateralPre, maxDebtPre, newCollateral, newDebt)
588
+ allocationsChangedSinceQuote(
589
+ newCollateralPre,
590
+ maxDebtPre,
591
+ newCollateral,
592
+ newDebt,
593
+ )
538
594
  ) {
539
595
  return helper.adjustBalancesHook(clientSeat);
540
596
  }
541
597
 
542
- stageDelta(clientSeat, vaultSeat, giveColl, wantColl, 'Collateral');
598
+ return helper.commitBalanceAdjustment(
599
+ clientSeat,
600
+ fp,
601
+ {
602
+ newDebt,
603
+ fee,
604
+ surplus,
605
+ toMint,
606
+ },
607
+ { normalizedDebtPre, collateralPre },
608
+ );
609
+ },
610
+
611
+ /**
612
+ *
613
+ * @param {ZCFSeat} clientSeat
614
+ * @param {FullProposal} fp
615
+ * @param {ReturnType<typeof calculateLoanCosts>} costs
616
+ * @param {object} accounting
617
+ * @param {NormalizedDebt} accounting.normalizedDebtPre
618
+ * @param {Amount<'nat'>} accounting.collateralPre
619
+ * @returns {Promise<string>} success message
620
+ */
621
+ async commitBalanceAdjustment(
622
+ clientSeat,
623
+ fp,
624
+ { newDebt, fee, surplus, toMint },
625
+ { normalizedDebtPre, collateralPre },
626
+ ) {
627
+ const { state, facets } = this;
628
+ const { helper } = facets;
629
+ const { vaultSeat } = state;
630
+
631
+ const giveMintedTaken = AmountMath.subtract(fp.give.Minted, surplus);
632
+
633
+ // TODO use atomicRearrange https://github.com/Agoric/agoric-sdk/issues/5605
634
+ stageDelta(
635
+ clientSeat,
636
+ vaultSeat,
637
+ fp.give.Collateral,
638
+ fp.want.Collateral,
639
+ 'Collateral',
640
+ );
543
641
  // `wantMinted` is allocated in the reallocate and mint operation, and so not here
544
642
  stageDelta(
545
643
  clientSeat,
546
644
  vaultSeat,
547
- giveMinted,
645
+ giveMintedTaken,
548
646
  helper.emptyDebt(),
549
647
  'Minted',
550
648
  );
551
649
 
552
650
  /** @type {Array<ZCFSeat>} */
553
- const vaultSeatOpt = allEmpty([giveColl, giveMinted, wantColl])
651
+ const vaultSeatOpt = allEmpty([
652
+ fp.give.Collateral,
653
+ giveMintedTaken,
654
+ fp.want.Collateral,
655
+ ])
554
656
  ? []
555
657
  : [vaultSeat];
556
658
  state.manager.mintAndReallocate(
@@ -566,7 +668,7 @@ export const prepareVault = (baggage, marshaller, zcf) => {
566
668
  collateralPre,
567
669
  newDebt,
568
670
  );
569
- state.manager.burnAndRecord(giveMinted, vaultSeat);
671
+ state.manager.burnAndRecord(giveMintedTaken, vaultSeat);
570
672
  helper.assertVaultHoldsNoMinted();
571
673
 
572
674
  helper.updateUiState();
@@ -3,7 +3,7 @@ import '@agoric/zoe/exported.js';
3
3
  import { Far } from '@endo/marshal';
4
4
  import { prepareVaultHolder } from './vaultHolder.js';
5
5
 
6
- const trace = makeTracer('IV', false);
6
+ const trace = makeTracer('VK', false);
7
7
 
8
8
  /**
9
9
  *
@@ -41,7 +41,6 @@ import {
41
41
  assertProposalShape,
42
42
  atomicTransfer,
43
43
  ceilDivideBy,
44
- floorDivideBy,
45
44
  getAmountIn,
46
45
  getAmountOut,
47
46
  makeRatio,
@@ -53,6 +52,7 @@ import { E } from '@endo/eventual-send';
53
52
  import { checkDebtLimit } from '../contractSupport.js';
54
53
  import { chargeInterest } from '../interest.js';
55
54
  import { liquidate, makeQuote, updateQuote } from './liquidation.js';
55
+ import { maxDebtForVault } from './math.js';
56
56
  import { makePrioritizedVaults } from './prioritizedVaults.js';
57
57
  import { Phase, prepareVault } from './vault.js';
58
58
 
@@ -94,6 +94,7 @@ const trace = makeTracer('VM', false);
94
94
  * getRecordingPeriod: () => RelativeTime,
95
95
  * getDebtLimit: () => Amount<'nat'>,
96
96
  * getInterestRate: () => Ratio,
97
+ * getLiquidationPadding: () => Ratio,
97
98
  * getLiquidationMargin: () => Ratio,
98
99
  * getLiquidationPenalty: () => Ratio,
99
100
  * getLoanFee: () => Ratio,
@@ -435,6 +436,7 @@ export const prepareVaultManagerKit = (
435
436
  const { facets } = this;
436
437
  trace('reschedulePriceCheck', collateralBrand, {
437
438
  liquidationQueueing,
439
+ outstandingQuote: !!outstandingQuote,
438
440
  });
439
441
  // INTERLOCK: the first time through, start the activity to wait for
440
442
  // and process liquidations over time.
@@ -622,20 +624,23 @@ export const prepareVaultManagerKit = (
622
624
  },
623
625
 
624
626
  /**
627
+ * Consults a price authority to determine the max debt this manager
628
+ * config will allow for the collateral.
629
+ *
625
630
  * @param {Amount<'nat'>} collateralAmount
626
631
  */
627
632
  async maxDebtFor(collateralAmount) {
628
633
  trace('maxDebtFor', collateralAmount);
629
634
  assert(factoryPowers && priceAuthority);
630
- const quoteAmount = await E(priceAuthority).quoteGiven(
635
+ const quote = await E(priceAuthority).quoteGiven(
631
636
  collateralAmount,
632
637
  debtBrand,
633
638
  );
634
- trace('maxDebtFor got quote', quoteAmount);
635
- // floorDivide because we want the debt ceiling lower
636
- return floorDivideBy(
637
- getAmountOut(quoteAmount),
639
+ trace('maxDebtFor got quote', quote.quoteAmount.value[0]);
640
+ return maxDebtForVault(
641
+ quote,
638
642
  factoryPowers.getGovernedParams().getLiquidationMargin(),
643
+ factoryPowers.getGovernedParams().getLiquidationPadding(),
639
644
  );
640
645
  },
641
646
  /**