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

Vault Protocol #53

Merged
merged 88 commits into from
Sep 24, 2024
Merged

Vault Protocol #53

merged 88 commits into from
Sep 24, 2024

Conversation

0xBreath
Copy link
Contributor

‼️ Major Update ‼️

Purpose

Support a third party "protocol" that represents the entity, company, or product surrounding one or more vaults. The use case is for my company which highlights multiple vaults allowing users to arbitrage between the best vaults; this is analogous to shopping for hedge funds on Wall Street.

My company, or any company that uses this feature, is the protocol which can take a fee or profit share in order to drive revenue. The manager of the vault (the trader) maintains their profit share and fee just as before.

The need for this exists because previously only the manager of the vault could take a cut, but that is only a two-party system with the investors and the manager/trader. If a company is maintaining a product surrounding these vaults and the managers/traders are the party that collects the fees, then the company/protocol needs a way to mechanism to generate revenue. This update makes the vaults a three-party system where the protocol, too, can take a fee.
That is what this VaultProtocol feature achieves.

Changes

Adds a vault_protocol field in the Vault account which is a PDA for a VaultProtocol account if defined and not the system program.
This vault protocol can take an optional profit share or annual fee just as the manager, expect only >= 0 whereas the manager can be negative.

In programs/drift_vaults/src/tests.rs under the vault_v1_fcn test module, there are tests that incorporates protocol fees alongside and standalone to the manager fees.
The main case change in the apply_fee where it applies the manager or protocol annual fee and profit share, if any.
These tests handle just a manager fee, both a manager and protocol fee, and just a protocol fee. All legacy tests without a protocol have been untouched and still pass, which is the main piece to review here: backwards compatibility.

In ts/tests/driftProtocolVault.ts there are tests that run through the following steps:

  1. Init a Vault with a VaultProtocol with a manager profit share of zero and a protocol profit share of 10%. A distinct keypair for the manager is used to test signing permissions.
  2. Delegate the vault's user to a "delegate" keypair to test allowing someone to manage the vault assets.
  3. Init a VaultDepositor and deposits $300 USDC.
  4. The delegate longs SOL-PERP using the vault assets.
  5. The admin client increase the price of SOL-PERP 10%
  6. The delegate shorts (exits the long) using the vault assets.
  7. The vault depositor settles PNL for a ~10% gain on deposits for a total of ~$310.
  8. The vault depositor withdraw all equity, or ~$310, minus a 10% profit share on ~$10 in profit, or $1, for a total of ~$309.
  9. The protocol withdraw its profit share of ~$1.

The VaultProtocol is passed in as the last remaining account in any instruction that needs it post-initialization.
If the last rem acct deserializes into an AccountLoader<VaultProtocol> then it is a protocol, and if it does not then the vault protocol has not been passed in and is a legacy vault.
This is backwards compatible because legacy clients won't pass this into remaining accounts, and it will fail to deserialize and return None which the instructions handle as being a legacy client. This logic can be found in programs/drift_vaults/src/state/vault_protocol.rs
You will find these two lines in any instruction that needs the VaultProtocol:

    // returns Option<AccountLoader<VaultProtocol>>>, None if legacy vault that doesn't have a protocol.
    let mut vp = ctx.vault_protocol();
    // gets a RefMut<VaultProtocol> from behind the option without consuming self (borrowed instead of owned).
    let vp = vp.as_mut().map(|vp| vp.load_mut()).transpose()?;

Side Note

Added steps to the README for Apple Silicon (Mac M1) users since I had trouble getting things to compile.

programs/drift_vaults/src/state/vault.rs Show resolved Hide resolved
.cast::<i128>()?
.safe_sub(self.total_shares.cast()?)?;

protocol_fee_shares = self
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think it might be better to do new_total_shares - management_fee_shares - vault.total_shares here so that we dont get weird numerical issues for rounding in multipication?

Copy link
Contributor Author

@0xBreath 0xBreath Aug 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should stay. switching both of your comments to that math breaks the tests.
in the first comment that protocol calc does:

let protocol_fee_payment = total_fee_payment
  .min(depositor_equity.saturating_sub(1))
  .safe_sub(management_fee_payment)?;

This is because if the shares are odd, then the applied fee to the manager and protocol can't divide, so one of them must lose one share. I make the protocol take the short end of the stick.

This second comment has to use the protocol_fee_shares_factor so that it has that subtraction factored in. It can't just do new_total - manager - old_total = protocol like you suggest.

I spent a lot of time wrestling with the rounding errors when I first made this PR.
The tests with manager shares had to stay as it was, and calculating the protocol as the total - manager never worked because of that rounding error on odd numbers.

If I make your change then the manager ends up with more shares than it should, because that rounding isn't accounted for.

So my system attempted to solve for this, so I don't think it should change as the tests would break.

programs/drift_vaults/src/state/vault.rs Show resolved Hide resolved
programs/drift_vaults/src/state/vault.rs Outdated Show resolved Hide resolved
programs/drift_vaults/src/state/vault.rs Outdated Show resolved Hide resolved
programs/drift_vaults/src/state/vault.rs Outdated Show resolved Hide resolved
programs/drift_vaults/src/state/vault.rs Show resolved Hide resolved
programs/drift_vaults/src/state/vault.rs Show resolved Hide resolved
@wphan wphan changed the base branch from master to merge/0xBreath/master September 24, 2024 14:39
@wphan wphan changed the base branch from merge/0xBreath/master to master September 24, 2024 14:40
@wphan wphan merged commit 8689e71 into drift-labs:master Sep 24, 2024
5 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants