Open anilhelvaci opened 1 month ago
sequenceDiagram
participant user as institutionalUser
participant int as dapp-inter
participant vs as vStorage
participant vf as vaultFactory
user ->> int: connect keplr
int ->> vs: query = list of institutional managers and the white-list
vs -->> int: [manager0, manager1]
Note over int, int: find which managers the user is whitelisted
int ->> int: display retail AND institutional managers
int ->> int: display all vaults belonging to the user
int -->> user: page loaded
user ->> int: Click "open vault" for institutional manager
int ->> int: calculate prevOfferId
Note over int,int: Inst users can send offers using continuing invitations
Note over int,int: e.g {address}-manager-{managerIndex}-{?iteration}
int ->> vf: Send vault offer
Note over int, vf: offer = {invitationSpec = {source = continuing, prevOfferId, makeVaultInvitation}}
vf -->> int: success
After giving some deeper thoughts, it is clear that we need identifiers for both retails and institutional VMs. Which leads me to think that we can leverage these identifiers to store our VMs. Below is a high level illustration of how I think we can store the ever increasing VMs.
flowchart LR
A(EC) --> |Add new VM| D(VaultDirector)
D(VaultDirector) --> C{Retail or Institutional}
C -->|Retail| F(Retail VMs Store)
--> |collateralBrand| E(Get VM store for a Col Type)
--> |VM identifier| K(Init new retail VM)
C -->|Institutional| G(Institutional VM store)
--> |collateralBrand| N(Get VM store for a Col Type)
--> |VM identifier| L(Init new retail VM)
However, before we come to any conclusion we must answer more questions. Preferably with diagrams. Here are some of those questions.
retail
and institutional
VMs?sequenceDiagram
actor mem as Member
participant ec as EC UI
participant vs as vStorage
participant sw as Member SmartWallet
participant ch as EconCharter
participant gv as VF Governor
participant vf as VaultFactory
mem ->> ec: Raise DebtLimit for ATOM-A
ec ->> vs: Query vaultManager specifiers
vs -->> ec: { collateralBrand, managerIdentifier }
ec ->> ec: Build offerSpec
ec ->> sw: Initiate election
Note over ec, sw: offerSpec = {..., offerArgs = {..., path = {paramPath = {key = {collateralBrand, managerIdentifier}}}}}
sw ->> ch: Forward offer
ch ->> ch: Lookup relevant governor
ch ->> gv: voteOnParamChanges(..., {...path})
gv ->> vf: Lookup path
vf -->> gv: ATOM-A paramManager
gv ->> gv: Election result = Positive
gv ->> gv: Update params
gv -->> sw: Offer succesful
sw -->> vs: Offer succesful
vf ->> vs: Params updated
ec ->> vs: Lookup params
ec ->> ec: Display updated params
We expect this process to be the same for both retail
and institutional
managers as institutional
manager will also have their own identifier in vstorage
.
vstorage
look like? What will be the form of the VM identifiers?Here's a snapshot of how vstorage
looks for a vault manager;
published
│__vaultFactory
│ │__managers
│ │__manager0
│ │__governance
│ │__metrics
│ │__quotes
│ │__vaults
│ │__manager1
│ │ ...
I suggest we introduce a new child node under manager{manager-index}
called metadata
. This metadata
child can have a data model similar to below;
Note that data shown below is just a thought and not the final data model
const metadata = {
identifier,
title: "ATOM-A",
whitelist: ["agoric1....4w", "agoric1....2q", ...], // Means this is an institutional VM
}
So vstorage
after the upgrade will look like below;
published
│__vaultFactory
│ │__managers
│ │__manager0
│ │__governance
│ │__metrics
│ │__quotes
│ │__vaults
│ │__metadata ***
│ │__manager1
│ │ ...
VM identifiers are intended to serve as the key in vmStore
. Here are main parameters I use to decide;
See discussion in agoric-sdk
=> https://github.com/Agoric/agoric-sdk/discussions/9663
sequenceDiagram
participant sw as User SmartWallet
participant vd as VaultDirector
participant vm as VaultManager
Note over sw: user selects one of the vaultManagers displayed on dapp-inter
sw ->> vd: getCollateralManager(brandIn, managerIdentifier)
vd ->> vd: retailVMsStore.get(brandIn)
vd ->> vd: ${brandIn}Store.get(managerIndex)
vd -->> sw : vaultManagerPublicFacet
sw ->> vm : makeVaultInvitation()
vm -->> sw : invitation
sw ->> vm : makeVaultKit()
vm -->> sw : vaultKit
sequenceDiagram
participant vd as VaultDirector
participant vf as VaultFactory
participant hl as VM Holder
participant sw as User SmartWallet
vd ->> vd: addNewManager(collateralBrand, initialParamValues)
vd ->> vf: makeVaultManagerKit()
vf -->> vd: kit
vd ->> hl: makeVmHolder(kit.self.getPublicFacet(), getIterationCount)
hl ->> hl: currentIteration = getIterationCount()
Note over hl: currentIteration enables revoking access
hl -->> vd: holder
Note over vd, hl: holder = {invitationMakers = {MakeVault = pf.makeVaultInvitation}, holder = { getQuotes, getCompoundedInterest, getPublicTopics }}
vd ->> sw: holder
Using a holder
pattern similar to vaultHolder.js here. The main reason using a holder instead of a raw invitationMakers
object is that vaultManager's publicFacet does not only include methods for making new invitations but also has some utility read only methods like getQuotes
. So in order not to leave those out a holder pattern is probably useful.
In addition to what's written above we can use a method like isActive
to check current iteration against the holder's iteration count in order decide if this user is revoked or not. See owned
method in vaultHolderi.
sequenceDiagram
actor us as Institutional User
participant ui as dapp-inter
participant sw as User SmartWallet
participant ivm as Institutional VM
us ->> ui: select institutional VM
us ->> ui: enter vault parameters
us ->> ui: click open vault
ui ->> ui: build offer spec
note over ui: {invitationSpec= {previousOffer= "{$address-manager-$managerIndex}"}, proposal, offerArgs}
ui ->> sw: offer sent
sw ->> sw: find continuing invitation for {$address-manager-$managerIndex}
sw ->> sw: invitationMakers.makeVaultInvitation()
sw ->> ivm: forward offer
ivm ->> ivm: makeVatultKit()
ivm -->> sw: vaultHolder
sw -->> ui: success
A Retail Vault Manager
is a vm that its public facing methods (collateral
facet) are accessible to anybody. Anyone wishing to open a vault in these VMs can do so.
An Institutional Vault Manager
is a vm that only a set whitelisted account can access its public facing methods (collateral
facet). EC is in charge of determining the whitelisted accounts. If manager{managerIndex}
storage node's child node metadata
has a non-empty whitelist
array, this means that vault manager is for institutional purposes and only those accounts listed under metadata.whitelist
can send transactions to interact with that VM.
As far as I can see, vault lifecycle is handled separately via methods of vaultHolder
. As a result for an institutional user having vaults from both institutional and retails VMs, all those vaults show up under wallet.{address}.current.offerToPublicSubscriberPaths
with their own offerId
s as the key value. As a result an institutional user's smart wallet will have lifecycle methods for both institutional and retail vaults. Therefor, I don't expect any changes to the process as well.
New Approach to Retail VMs
Just to clarify if I understood your approach, the structure of VaultManagers stores will be similar to the following?
Retail or Institutional VMs Store | Key (collateralBrand) | Value (VM store) |
---|---|---|
stAtom | stAtomStore | |
stOsmo | stOsmoStore |
stAtomStore | Key (VM identifier) | Value (VM) |
---|---|---|
manager101 | vaultManager | |
manager102 | vaultManager |
The current structure of vStorage is:
published
│__vaultFactory
│ │__managers
│ │__manager0
│ │__vaults
│ │__vault0
│ │__vault1
│ │__manager1
│ │ ...
Lets assume that we wish to implement the minimum changes possible to keep back compatibility, but still be able to open and query a vaultManager that has the same collateral as other vaultManagers.
If we record on vstorage the Retail and Institutional VMs Store, from which we can fetch the manager index, we could keep the same vstorage structure by simply add a new step on the query process.
Diagram in progress ...
Assumptions:
addVaultManager
was included based on the new method proposed above that will serve a similar purpose of addVaultType
sequenceDiagram
actor I as Institution
participant EC as EconCommittee
participant ECC as EconCommitteeCharter
participant VD as VaultDirector
participant PS as PostalService
I ->> EC: provide account addresses to be whitelisted
I ->> EC: provide desired collateral and initialParamValues
Note over I, EC: Should the communication above happen off-chain?
EC ->> ECC: makeCharterMemberInvitation
ECC -->> EC: invitationMakers
note over EC: invitationMakerName: 'VoteOnApiCall'
note over EC: invitationArgs: [VaultDirector, addNewManager, [collateralIssuer, initialParamValues]]
EC ->> VD : executeOffer()
create participant VM as VaultManager
VD ->> VM: makeVaultManagerKit()
VM -->> VD: VaultManagerKit
VD ->> VD: Build invitationMaker for the institution
Note over VD: { invitationMakers = { GetVmPF = kit.self.getPublicFacet } }
loop Whitelisted addresses
VD ->> PS: sendTo(addr, payment)
end
PS -->> I: Invitation
I ->> I: Exercise invitation to fetch VaultManager publicFacet
I ->> VM: makeVaultInvitation()
VM -->> I: vaultKit
VaultManager PublicFacet
(collateral interface):
collateral: M.interface('collateral', {
makeVaultInvitation: M.call().returns(M.promise()),
getPublicTopics: M.call().returns(TopicsRecordShape),
getQuotes: M.call().returns(NotifierShape),
getCompoundedInterest: M.call().returns(RatioShape),
}),
The difference between this approach and the existing one is that the offer to retrieve the expected VaultManager
has to provide, in addition to the brandIn
, the managerIndex
. If the vaultManager
selected on the dapp-inter
UI is an Institutional one, it will be also required to passed the parameter institutional
as true
.
These additional parameters will be used to fetch the VaultManager from its respective durable storage.
sequenceDiagram
participant sw as User SmartWallet
participant vd as VaultDirector
participant vm as VaultManager
Note over sw: user selects one of the vaultManagers displayed on dapp-inter
sw ->> vd: getCollateralManager(brandIn, managerIndex, institutional: false)
alt intitutional === false
vd ->> vd: retailVMsStore.get(brandIn)
vd ->> vd: ${brandIn}Store.get(managerIndex)
else institutional === true
vd ->> vd: institutionalVMsStore.get(brandIn)
vd ->> vd: ${brandIn}Store.get(managerIndex)
note over vd: Not sure if next step is required considering that dapp-inter will only display whitelisted VMs
vd ->> vd: validate if address is whitelisted
end
vd -->> sw : vaultManagerPublicFacet
sw ->> vm : makeVaultInvitation()
vm -->> sw : invitation
Note over sw, vm: user exercises the invitation
sw ->> vm : makeVaultKit()
vm -->> sw : vaultKit
In the collapsable section bellow, it is displayed the VaultManager structure and data currently present on Vstorage. This should be kept as is for both retail and institutional VMs since it exposes the necessary data for the inter-protocol
logic and transparency.
Note: this assume as premisse that the institutional VMs will have the same level of transparency as the retail ones.
One of the main structural difference that we anticipate between retail
and institutional
VaultManagers is the addresses whitelist
that defines the identifies the accounts that should be able to visualize the respective VaultManagers on the inter-protocol app.
As described above, one possible solution for is to add an additional child node under manager{manager-index} called metadata.
If the whitelist
attribute is set with a list of addresses, that would mean we are interacting with an institutional VaultManager.
const metadata = {
identifier,
title: "ATOM-A",
whitelist: ["agoric1....4w", "agoric1....2q", ...], // Means this is an institutional VM
}
Other structural difference is the process required to create a new VaultManager. The retail VMs will continue to follow the current flow where a core-eval proposal is submitted and voted by the BLD stakers. Regarding the institutional VMs, they will be created via the EconComittee as it is described on this diagram https://github.com/anilhelvaci/agoric-sdk/issues/1#issuecomment-2208664281
The retail VMs will continue to follow the current flow where a core-eval proposal is submitted and voted by the BLD stakers. Regarding the institutional VMs, they will be created via the EconComittee as it is described on this diagram https://github.com/anilhelvaci/agoric-sdk/issues/1#issuecomment-2208664281
@Jorge-Lopes @anilhelvaci - Can you clarify this statement? If a collateral type has already been approved by the BLD stakers - then it's over to the EC to add additional vault types of that already supported collateral type. i.e. new vault types of already supported collateral type shouldn't need to be voted in by BLD stakers.
Similarly for those institutional vaults - if the collateral type has already been added via BLD stakers - i.e. ATOM then the EC should be able to create an institutional ATOM vault from the Econ Gov UI - rather than it needing to go back to BLD stakers. Does both of these flows aligns with what you had in mind?
@Jorge-Lopes @anilhelvaci - Can you clarify this statement? If a collateral type has already been approved by the BLD stakers - then it's over to the EC to add additional vault types of that already supported collateral type. i.e. new vault types of already supported collateral type shouldn't need to be voted in by BLD stakers.
Similarly for those institutional vaults - if the collateral type has already been added via BLD stakers - i.e. ATOM then the EC should be able to create an institutional ATOM vault from the Econ Gov UI - rather than it needing to go back to BLD stakers. Does both of these flows aligns with what you had in mind?
Yes, the flow you described aligns with our design. I apologize if the previous comment was unclear; it was a draft meant to initiate discussion.
We are currently preparing a new issue as Analysis Milestone: 2. The goal is to provide a clear description of the intended design, supported by necessary diagrams for comprehensive visualization.
This issue will take into account your responses from Analysis Milestone: 1, as well as the recent feedback you provided.
Context
See https://github.com/Agoric/agoric-sdk/issues/6518
Development Analysis
Components Expected to be Affected
addNewManager
. This method;initialParamValues
are checked just like inaddVaultType
paramManager
s CurrentlyparamManager
s are tracked for every collateral manager according to their collateral brand. Having multiple collateral managers for one collateral type will break this implementation. We must come up with a new way to track collateral managers. I suggest looking into durable handlers.Testing Considerations
Open Questions
Is it possible to have more than one retail vault manager?
In other words, for a given and already accepted collateral type, is it safe to assume that all incoming new vault managers will be of type
institutional
or can EC choose to create a newretail
vault manager?I believe the answer to this question is most likely "yes". See https://github.com/anilhelvaci/agoric-sdk/issues/3
Can EC add new accounts to the whitelist for a particular vault manager?
Can EC remove accounts from the whitelist for a particular vault manager?
Regression Considerations
Changing the way we index vault managers would probably end up changing a lot of the existing tooling around vaultFactory.
Deliverables
All the iterations will come with their corresponding FE implementations.
Iteration One: Enable only institutional vault managers first
Iteration Two: Enable retails vault managers