Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identify modules that will require an updated #3

Open
Jorge-Lopes opened this issue Dec 29, 2023 · 6 comments
Open

Identify modules that will require an updated #3

Jorge-Lopes opened this issue Dec 29, 2023 · 6 comments
Labels
documentation Improvements or additions to documentation

Comments

@Jorge-Lopes
Copy link
Owner

Jorge-Lopes commented Dec 29, 2023

Current relevant data recorded on Vstorage:

Vaults:

  • published.vaultFactory
  • published.vaultFactory.governance
    • "ChargingPeriod"
    • "Electorate"
    • "MinInitialDebt"
    • "RecordingPeriod"
    • "ReferencedUI"
    • "ShortfallInvitation"
  • published.vaultFactory.managers
  • published.vaultFactory.managers.manager0
  • published.vaultFactory.managers.manager0.governance
    • "DebtLimit"
    • "InterestRate"
    • "LiquidationMargin"
    • "LiquidationPadding"
    • "LiquidationPenalty"
    • "MintFee"
  • published.vaultFactory.managers.manager0.metrics
    • "liquidatingCollateral"
    • "liquidatingDebt"
    • "lockedQuote"
    • "numActiveVaults"
    • "numLiquidatingVaults"
    • "numLiquidationsAborted"
    • "numLiquidationsCompleted"
    • "retainedCollateral"
    • "totalCollateral"
    • "totalCollateralSold"
    • "totalDebt"
    • "totalOverageReceived"
    • "totalProceedsReceived"
    • "totalShortfallReceived"
  • published.vaultFactory.managers.manager0.quotes
    • "quoteAmount”
  • published.vaultFactory.managers.manager0.vaults
  • published.vaultFactory.managers.manager0.vaults.vault0
    • "debtSnapshot"
    • "locked"
    • "vaultState"

Auctions

  • published.auction
  • published.auction.book0
    • "collateralAvailable":
    • "currentPriceLevel"
    • "remainingProceedsGoal"
    • "startCollateral"
    • "startPrice"
    • "startProceedsGoal"
  • published.auction.governance
    • "AuctionStartDelay"
    • "ClockStep"
    • "DiscountStep"
    • "Electorate"
    • "LowestRate"
    • "PriceLockPeriod"
    • "StartFrequency"
    • "StartingRate"
  • published.auction.schedule
    • "activeStartTime"
    • "nextDescendingStepTime"
    • "nextStartTime"
@Jorge-Lopes Jorge-Lopes added the documentation Improvements or additions to documentation label Dec 29, 2023
@Jorge-Lopes
Copy link
Owner Author

Jorge-Lopes commented Dec 29, 2023

Vaults

For each vault liquidated and added to an auction we need:

  • Vault manager
  • Collateral at time of liquidation
  • Debt at time of liquidation
  • Time of liquidation

What we have:

Data Path Logic required
Vault manager
Collateral vaultFactory.managers.managerID.vaults.vaultID.debtSnapshot No
Debt vaultFactory.managers.managerID.vaults.vaultID..locked No
Time
  • The vaultID is registered in the vault state, declared as idInManager, although the managerID cannot be retrieved from the vaults contract
  • The Time of liquidation is still an open question

How the vault node is created and manipulated

sequenceDiagram
    participant VM as Vault Manager
    participant V as Vault
    participant VK as VaultKit
    participant VH as VaultHolder
    VM ->> VM : create vaultStorageNode
    VM ->> V: makeVault(manager, vaultId, vaultStorageNode)
    V ->> VK: makeVaultKit(self, storageNode)
    VK ->> VH: makeVaultHolder(vault, storageNode)
    VH ->> VH: makeRecorderKit(storageNode);
    VH -->> VK: recorder
    VK ->> VK: vaultUpdater: recorder
    VK-->>V: VaultKit
    V ->> V: outerUpdater = vaultKit.vaultUpdater
    V -->> VM : Vault self facet
Loading

At vaults.js the method updateUiState is responsible for updating the state of the vault storageNode. Receiving as argument the output of the getStateSnapshot method.

Note: if the vault phase is set to CLOSED, the node will record its final state

  • updateUiState:
        updateUiState() {
          const { state, facets } = this;
          const { outerUpdater } = state;
          if (!outerUpdater) {
            // It's not an error to change to liquidating during transfer
            return;
          }
          const { phase } = state;
          const uiState = facets.helper.getStateSnapshot(phase);
          const brand = facets.helper.collateralBrand();
          trace(brand, 'updateUiState', state.idInManager, uiState);

          switch (phase) {
            case Phase.ACTIVE:
            case Phase.LIQUIDATING:
            case Phase.LIQUIDATED:
              void outerUpdater.write(uiState);
              break;
            case Phase.CLOSED:
              void outerUpdater.writeFinal(uiState);
              state.outerUpdater = null;
              break;
            default:
              throw Error(`unreachable vault phase: ${phase}`);
          }
        },
  • getStateSnapshot:
        getStateSnapshot(newPhase) {
          const { state, facets } = this;

          const { debtSnapshot: debt, interestSnapshot: interest } = state;
          /** @type {VaultNotification} */
          return harden({
            debtSnapshot: { debt, interest },
            locked: facets.self.getCollateralAmount(),
            // newPhase param is so that makeTransferInvitation can finish without setting the vault's phase
            // TODO refactor https://github.com/Agoric/agoric-sdk/issues/4415
            vaultState: newPhase,
          });
        },

@Jorge-Lopes
Copy link
Owner Author

Jorge-Lopes commented Dec 29, 2023

Auction

For each auction run we need:

  • Auction start time
  • Auction end time
  • Auction identifier
  • Starting state
    • Collateral offered
    • IST target
  • Ending state
    • Collateral sold
    • Collateral remaining
    • IST raised
    • IST target remaining

What we have:

Data Path Logic required
Auction identifier auction.bookID n.a
Auction start auction.schedule.activeStartTime Verify auction state
Auction end
Collateral offered auction.bookID.startCollateral n.a
IST target auction.bookID.startProceedsGoal n.a
Collateral sold n.a Collateral offered - Collateral remaining
Collateral remaining auction.bookID.collateralAvailable n.a
IST raised n.a IST target - IST target remaining
IST target remaining auction.bookID.remainingProceedsGoal n.a
  • Auction start and end time will be regulated by the auction.schedule, although this is not unique per auction and will require some logic implemented
sequenceDiagram
    participant A as Auctioneer
    participant AB as AuctionBook
    A ->> A : create bNode
    A ->> AB: makeAuctionBook(brands.Bid, brand, priceAuthority, bNode)
    AB ->> AB: makeRecorderKit(bNode);
    AB -->> A : auctionBook self facet
Loading

At auctionBook.js the method publishBookData is responsible for updating the state of the bookID storageNode.

  • publishBookData:
        publishBookData() {
          const { state } = this;

          const allocation = state.collateralSeat.getCurrentAllocation();
          const collateralAvailable =
            'Collateral' in allocation
              ? allocation.Collateral
              : makeEmpty(state.collateralBrand);

          const bookData = harden({
            startPrice: state.capturedPriceForRound,
            startProceedsGoal: state.startProceedsGoal,
            remainingProceedsGoal: state.remainingProceedsGoal,
            proceedsRaised: allocation.Bid,
            startCollateral: state.startCollateral,
            collateralAvailable,
            currentPriceLevel: state.curAuctionPrice,
          });
          return state.bookDataKit.recorder.write(bookData);
        },

@Jorge-Lopes
Copy link
Owner Author

Jorge-Lopes commented Jan 3, 2024

Post-auction

For each post-auction we need the following data about funds distribution:

  • Collateral asset for liquidation penalty sent to Reserve
  • Collateral asset returned to vault holders
  • Debt returned to vault holders (only happens in certain circumstances)
  • Shortfall value sent to Reserve

What we have:

Data Path Logic required
Collateral sent to Reserve n.a. a*
Collateral returned to vault n.a. b*
Debt returned to vault n.a. c*
Shortfall sent to Reserve d*

When an auction is finalised, the distributeProceeds method is executed, which will return an array of transfers with 4 types of entries:

  • [collateralSeat, seat, { Collateral: collateralAmount }]
  • [bidHoldingSeat, seat, { Bid: bidAmount }]
  • [bidHoldingSeat, reserveSeat, { Bid: bidAmount }]
  • [collateralSeat, reserveSeat, { Collateral: collateralAmount },{ [collateralKeyword]: collateralAmount }]

All transactions recorded on the transfers list will be atomically executed at the end of the distributeProceeds process.

IDEA Maybe we can deconstruct the transfers list and provide the relevant content to the Vstorage.
TODO: extend on this...

Collateral sent to Reserve (a*)

1- Multiple deposits made to the collateral.
After sending the corresponding amount of unsold collateral for each vault, the remaining collateral is sent to the reserve.
Depending on the scenario, this process can be executed on either:
source code
source code

Collateral returned to vault (b*)

1 - A single deposit was made for this Collateral
The amount of Collateral returned to the vault will be all the remaining collateral not sold on the auction.
source code

2- Multiple deposits made and none of the participants specified a goal or If proceeds are less than the total specified goals (case A and B)
The amount of Collateral returned to the vault will be a share of the remaining collateral according to the deposited amount. Done via the distributeProportionally method.
source code

3- Multiple deposits made and proceeds precisely match the total specified goals
Each depositor gets a share that equals their amount deposited multiplied by totalValueRatio computed above.
source code

4- Multiple deposits made and proceeds exceed the total specified goals, and depositor did not specified goal
source code

5- Multiple deposits made and proceeds exceed the total specified goals, and if proceedsGoal + value of unsoldCollateral >= limitedShare
This limitedShare represents the portion of value that depositors who specified a limit will receive based on their collateral deposited, taking into account the overall distributable value and the difference between total collateral and deposits from those who didn't specify a goal.
source code

6- Multiple deposits made and proceeds exceed the total specified goals, and there is not enough collateral to completely cover the gap above the proceedsGoal amount.
Each depositor gets a proportional share of unsoldCollateral plus enough Bid to reach their share.
source code

Debt returned to vault (c*)

1- Multiple deposits made and none of the participants specified a goal or If proceeds are less than the total specified goals (case A and B)
The amount of Debt returned to the vault will be a share of the remaining proceeds according to the deposited amount.
source code

2- Multiple deposits made and proceeds precisely match the total specified goals
Each depositor gets the goal it requested
source code

3- Multiple deposits made and proceeds exceed the total specified goals, and depositor did not specified goal
Each depositor gets the amount of Bid that needs to be added to fulfill the total value owed to the depositor.
source code

4- Multiple deposits made and proceeds exceed the total specified goals, and if proceedsGoal + value of unsoldCollateral >= limitedShare
Each depositor gets the goal it requested
source code

WIP

graph LR
A[Auction Outcome] --> B{Proportional Distribution?}
B -- Yes --> C[Proceeds <= Proceeds Goal?]
B -- No --> D[Proceeds > Proceeds Goal]
C -- Yes --> E[Proportional Distribution]
C -- No --> F[Allocate Based on Goals]
D --> G[Calculate Collateral Multiplier]
G --> H[Handle Cases with Limits]
H --> I[Proportional Allocation with Limits]
H --> J[Distribute Proportional Shares]
I --> J
J --> K[Reserve Transfer]
K --> L{Collateral Left?}
L -- Yes --> M[Collateral to Reserve]
L -- No --> N[Proceeds to Reserve]

Loading

Shortfall sent to Reserve (d*)

The shortfall sent to Reserve can be retrieved at the vaultsManager when the liquidateVaults method is executed via the shortfallToReserve attribute of plan, an object returned by the calculateDistributionPlan method of proceeds.js.
In more detail bellow-

@Jorge-Lopes
Copy link
Owner Author

Jorge-Lopes commented Jan 4, 2024

Connect all data

Story

The liquidationWaker is provided to the setWakeupsForNextAuction method at the vaultDirector.

At the vaultDirector, the liquidationWaker is provided as an argument for the setWakeupsForNextAuction method of liquidations.js which will set the respective wakeup.

ToDo: follow the nextAuctionSchedule and nominalStart thread...

Now the nominalStart time is reached and the wakeup is triggered. The vaultDirector will call the liquidateVaults method of all vaultManager registered.

The vaultManager will get the totalDebt, totalCollateral, vaultData, liqSeat from the Liquidatable Vaults and make a deposit on the auctioneer, providing the totalCollateral to be sold and the totalDebt as the goal.

At this moment, we need to know:

  • which vaults got liquidated and added to an auction
  • the vaultManager id of those vaults
  • the auction id (bookID) that each vault was added

From here we can used the book storage node (published.auction.bookID) to query the details of each auction run.
As well as the details of the liquidated vaults from the vault storage node (published.vaultFactory.managers.managerID.vaults.vaultID)

When the auction is finalised:

  • the auctioneer will call the distributeProceeds method that will calculate the post-auction details, specifically the Collateral sent to Reserve and Collateral and Debt returned to vault holders
  • the vaultManager will get the plan from the proceeds.js calculateDistributionPlan method, which will indicate the shortfall sent to Reserve.

The post-auction data detailed above cannot currently be found on the Vstorage.

Connect data

The challenge is to connect

  • manager <--> vaults
  • vaults <--> auctions
  • auction <--> transfers
  • auction <--> plan

manager <--> vaults

  • Manager to vaults can be achieved by retrieving all the child nodes under manager.vaults
  • Vaults to manager can be achieved by retrieving by getting the vault collateral brand and use the manager metrics totalCollateral attribute to find a match

vaults <--> auctions

  • Vaults to auctions can be achieved by retrieving by getting the vault collateral brand and use the auctions startCollateral attribute to find a match
  • Auctions to vaults can be achieved by retrieving by getting the auction collateral brand, select the respective vaultManager, and verify which vaults has the phase LIQUIDATING. This can be verified by the vaultsManager metrics numLiquidatingVaults attribute

auction <--> transfers

@anilhelvaci
Copy link

anilhelvaci commented Jan 5, 2024

Liquidation Visibility Analysis [DRAFT]

Problem Definition

Currently when there are liquidatable vaults present in one of the vaultManagers, it's collateral is deposited into the auctioneer and a dutch auction is run. The problem is that too little information is exposed to vstorage for running financial analysis and being transparent overall.

Solution Requested

Agoric OpCo(maybe tag rowland here) requested below information to be exposed to vstorage as well;

  1. Which vaults got liquidated and added to an auction. For each vault
    1. Vault manager
    2. Collateral at time of liquidation
    3. Debt at time of liquidation
    4. Time of liquidation
  2. Auction details. For each auction run:
    1. Auction start time
    2. Auction end time
    3. Auction identifier
      1. Collateral offered
      2. IST target
    4. Ending state
      1. Collateral sold
      2. Collateral remaining
      3. IST raised
      4. IST target remaining
  3. Post-auction funds distribution:
    1. Collateral asset for liquidation penalty sent to Reserve
    2. Collateral asset returned to vault holders
    3. Debt returned to vault holders (only happens in certain circumstances)
    4. Shortfall value sent to Reserve

Solution Analysis

During our analysis, below are what we've found;

Auction Details

Data Path Derived
Auction identifier published.auctioneer.{bookID} no
Auction start published.auctioneer.schedule.activeStartTime no
Collateral offered published.auctioneer.{bookID}.startCollateral no
IST Target published.auctioneer.{bookID}.startProceedsGoal no
Collateral remaining published.auctioneer.{bookID}.collateralAvailable no
Collateral sold - (Collateral offered) - (Collateral remaining)
IST target remaining published.auctioneer.{bookID}. remainingProceedsGoal no
IST raised - (IST target )- (IST target remaining)

Information Per Vault

Data Path Derived
Vault Manager - Correlate collateral sold in auction and accepted in VM
Collateral at time of liquidation - no
Debt at time of liquidation - no
Time of liquidation - Auction start time

No data correlating individual vaults to liquidations can be found in vstorage. This raises a requirement which we have to put new data to vstorage. When going over vaultManager code, we've found this in liquidateVauıtls method

 const { totalDebt, totalCollateral, vaultData, liqSeat } =
            getLiquidatableVaults(
              zcf,
              {
                quote: lockedQuote,
                interest: compoundedInterest,
                margin: liqMargin,
              },
              prioritizedVaults,
              liquidatingVaults,
              debtBrand,
              collateralBrand,
            );

Where vaultData has the following type definition;

/**
   * @type {MapStore<
   *   Vault,
   *   { collateralAmount: Amount<'nat'>; debtAmount: Amount<'nat'> }
   * >}
   */

This means vaultData will have the exact amounts of collateral and debt going into the liquidation which is what we want and can expose this to vstorage.

For the time of liquidation data point, we've traced that liquidations are scheduled to take place when auctions are starting. This means time of liquidation = auctioneer.schedule.activeStartTime but not sure to include this here since this will be the same for multiple vaults going into the same auction.

Post Auction Details

Data Path Derived
Collateral sent to reserve - no
Collateral returned to vault - no
Debt returned to vault - no
Shortfall sent to reserve - no

First of all, we want to clarify how we interpreted the phrase Post Auction Details. To us, Post Auction Details means that the auction is completed AND all unsold collateral along with raised IST is returned to the respective vault managers. We feel the need to make this clarification because the auctioneer itself does a distribution too. Which handles sending the unsold collateral and the raised Bid to depositors then send any uneven Bid to reserve, if any.

Having said all of above, currently vstorage does not have any data in it in the sense that we described above. See what vstroage has to offer here.

However, we've located where this data is in the vaultManager;

for (const [vault, balances] of bestToWorst) {
            vaultsInPlan.push(vault);
            vaultsBalances.push({
              collateral: balances.collateralAmount,
              // if interest accrued during sale, the current debt will be higher
              presaleDebt: balances.debtAmount,
              currentDebt: vault.getCurrentDebt(),
            });
          }
          harden(vaultsInPlan);
          harden(vaultsBalances);

          const plan = calculateDistributionPlan({
            proceeds,
            totalDebt,
            totalCollateral,
            oraclePriceAtStart: oraclePriceAtStart.quoteAmount.value[0],
            vaultsBalances,
            penaltyRate,
          });
          return { plan, vaultsInPlan };

Notice that we know what vaults are reinstated or marked as liquidated along with their respective balances. A plan object is built progressively by iterating over these vaults. Which has the type definition;

/**
 * @typedef {{
 *   overage: Amount<'nat'>,
 *   shortfallToReserve: Amount<'nat'>,
 *   collateralForReserve: Amount<'nat'>,
 *   actualCollateralSold: Amount<'nat'>,
 *   collateralSold: Amount<'nat'>,
 *   collatRemaining: Amount<'nat'>,
 *   debtToBurn: Amount<'nat'>,
 *   mintedForReserve: Amount<'nat'>,
 *   mintedProceeds: Amount<'nat'>,
 *   phantomDebt: Amount<'nat'>,
 *   totalPenalty: Amount<'nat'>,
 *   transfersToVault: Array<[number, AmountKeywordRecord]>,
 *   vaultsToReinstate: Array<number>
 * }} DistributionPlan

This plan object satisfies our needs in Post Auction Details more than enough. We can cherry pick which ones to expose in vstorage.

  • For "Collateral returned to vault" , do we need to expose collateral returned in per vault basis for every individual vault?
  • For "Debt returned to vault", what exactly do we mean here?

Deliverables

We'll use this section what to deliver exactly. We aim to clarify the work scope here.

Will Deliver

  • Make the necessary updates to what's written to vstorage. Work on on-chain code.

Might Deliver

  • A library for off-chain programs to query what's going on in inter-protocol liquidations. This deliverable depends on these variables;
    • The logic we use to derive some of the data gets so complex that it's better to wrap it up in a separate library.
    • Agoric OpCo requests such a feature
  • Upgrade the dashboard in analytics page. This deliverable depends on these variables;
    • Agoric OpCo requests this front-end work to be implemented by BytePitch.

Considerations

  • Should be careful when adding new nodes to vstorage from a resource consumption and performance standpoint.
  • It's probably better to initiate the UX work for analytics page as that might end up affecting how we structure data when writing to vstorage.

cc for review @Jorge-Lopes

@Jorge-Lopes
Copy link
Owner Author

Liquidation Visibility Analysis [DRAFT]

Review of Solution Analysis

Auction details

Aligned with my understanding

Vaults details

It seems I misunderstood the requested Collateral and Debt at moment of liquidation.
The solution you proposed to use the vaultData map returned by the getLiquidatableVaults function would be perfect for to retrieve this data.

Post Auction Details

Regarding the post auctions details, if it is true that we should discard the distribution made by the auctioneer, I agree that the plan would solve most of our problems.
If we need the Collateral and Debt returned to vault, we could maybe used the plan attribute transfersToVault. But I am still exploring if it is possible to mach the vault indexes provided in that list with the vaultID

In case we need to consider the distribution made by the auctioneer we should rethink our strategy with something similar to this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

2 participants