Skip to content

Commit

Permalink
Merge pull request #192 from PeggyJV/zaki/usage_docs
Browse files Browse the repository at this point in the history
usage docs
  • Loading branch information
zmanian authored Jun 27, 2024
2 parents f7c90bd + 9f1d1f9 commit 20cc329
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

Cellar contracts for the Sommelier Network.


### Documentation

- [Contracts](./docs/cellar_ui.md) Building UI flows with Cellars
- [Permissions](./docs/permissions.md) Permissions in Cellars
- [Using Cellars](./docs/using_cellars.md) Using the Cellars Smart Contracts as a strategist
- [Fees](./docs/fees.md) How fees work for the strategist and the platform

### Development

**Getting Started**
Expand Down
39 changes: 39 additions & 0 deletions docs/cellar_ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# UI Flows and Sommelier Cellars


## Deposits

### Basic Deposits
All cellars possess a `deposit` and `mint` function. These functions are equivalent. This duplicate functionality is there to enable cellars to conform to the er4626 vault standard. The Cellar implementation overrides the default er4626.

We generally prefer deposit. Like other erc4626 vault, the deposit function takes a base asset stored in the asset variable and mints shares. The deposit functionality only supports this single asset.

The deposit function takes two arguments. The amount of the base asset and the address to recieve the share. The deposit function will mint the shares and send them to the reciever. The deposit function will also transfer the base asset from the sender to the cellar.

There is a also 'previewMint' function that allow a UI to simulate the deposit for the user. Note for any vault the uses the erc4626 price oracle fuctionality for share pricing will generally see the dollar redemeption value of the shares be lower than value of funds deposited. This functionality is reccomended because it results in dramatically lower gas costs for deposits and withdrawals.

### MultiAsset Deposits

The multiAssetDeposit function is an advanced and otional functionality that can be enabled when the cellar is constructed. This function allows the cellar to accept deposits of multiple assets. The multiAssetDeposit function takes three arguments. The type of asset being deposited, the amount of the base asset and the address to recieve the shares. The deposit function will covert the asset to base asset ad mint the shares and transfer them to the reciever.

The multiAssetDeposit functionality takes a fee on the deposit. This fee serves a couple of purposes but most importantly it prevents exploiting small price arbtrigages to get underpriced cellar shares.

The Strategist on a cellar can set all the supported asset with `setAlternativeAssetData`. The variable `alternativeAssetData` holds a map of ERC20 addresses to supported Alternative assets.

## Withdrawals

### Basic Withdrawls
All cellars posses a `redeem` and `withdraw` function. These functions are equivalent. This duplicate functionality is there to enable cellars to conform to the er4626 vault standard. The Cellar implementation overrides the default er4626 implentations. These functions are only able to withdraw liquidity from positions where the `_withdrawableFrom` value is greater than 0. Many adapters do not enable withdrawls. This has the effect of increasing security of the vault by reducing the surface for arbitrage attacks but it requires the strategist to interactively manage the vault for requests.

### The Withdrawal Queue

The withdrawal queue functionality is provided to enable user to place requests for withdrawl liquidity that are larger than current withdrawalable liquduity into a smart contract that the strategist can monitor and service requests from. The flow is a users who wishes to withdraw with call `maxRedeem` if the amount of shares they want to redeem is greater than `maxRedeem`. The user will called `updateWithdrawRequest` with the vault shares they want to redeem with appropiate metdata.

```solidity
struct WithdrawRequest {
uint64 deadline; // deadline to fulfill request
uint88 executionSharePrice; // In terms of asset decimals
uint96 sharesToWithdraw; // The amount of shares the user wants to redeem.
bool inSolve; // Inidicates whether this user is currently having their request fulfilled.
}
```
51 changes: 51 additions & 0 deletions docs/fees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Fees

Strategists use the Fees and Reserve system to manage the fees generated by the cellar. The fees are generated by the cellar's strategies and are denominated in the holding asset. The plaform cut is managed directly in the Cellar and is a fraction of the total fees generated by the Strategist.

## Reseve

The Reserve is an adapter the hold funds from the cellar. Generally the strategist moves funds into the reserve on a regular basis from profits. The reserve gives the stratgist optionality. Fees must be collected from the reserve but funds in the reserve can also be returned to the cellar if the strategist decides to do so.

## Fees

Fees are storied in the metadata strct of the fees and reserves module. The management fees are computed on NAV and stored as basis points. The peformance fees are computed by tracking a highwater mark of the share from from each fee collection and then computing the fees based on the growth of the share price. New LP shares are minted when the performance fee is collected and that effectively dilutes the existing LPs.

``` solidity
/**
* @notice Stores meta data needed to calculate a calling cellars earned fees.
* @dev Store calling Cellars meta data in this contract to help mitigate malicious external contracts
* attempting to break logic by illogically changing meta data values.
* @param reserveAsset ERC20 asset Cellar does all its accounting in
* @param managementFee Fee charged for managing a Cellar's assets
* - Based off basis points, so 100% would be 1e4
* @param timestamp The last time this cellar had it's fees calculated
* @param reserves The amount of `reserveAsset` a Cellar has available to it
* @param exactHighWatermark High Watermark normalized to 27 decimals
* @param totalAssets Stored total assets
* - When calculating fees this value is compared against the current Total Assets, and the minimum value is used
* @param feesOwed The amount of fees this cellar has accumulated from both performance and management fees
* @param cellarDecimals Number of decimals Cellar Shares have
* @param reserveAssetDecimals Number of decimals the `reserveAsset` has
* @param performanceFee Fee charged based off a cellar share price growth
* - Based off basis points, so 100% would be 1e4
*/
struct MetaData {
ERC20 reserveAsset;
uint32 managementFee;
uint64 timestamp;
uint256 reserves;
uint256 exactHighWatermark;
uint256 totalAssets;
uint256 feesOwed;
uint8 cellarDecimals;
uint8 reserveAssetDecimals;
uint32 performanceFee;
}
```

## Platform Fees

When the strategist withdraw fees from the Cellar, the top level platform fee takes a cut for the Sommelier protocol. These fees are sent to the Sommelier chain via the gravity bridge on Ethereum mainnet and the Axelar on other chains. The fees are then auctioned to generate staking rewards.

It's expected that Strategists will collect fees every two weeks and trigger auctions on the Somm chain periodically.
18 changes: 18 additions & 0 deletions docs/permissions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Permississons in Cellars.


## Cellars

The basic Cellar contract uses the Auth contract from solmate for permissioning. A typical deployment transfers authorization to either Axelar Proxy Contract or the Sommelier Gravity contract that then manages the permissions for the Cellar.

Sommelier chain has a complex permissioning system which can isolate calls to requiring either messagages from the strategist to the Sommelier validators which are typicallly executed in 1-5 min or calls that require a full 48-hour vote of the Sommelier token holders.

One key thing to note is that Cellars often hold complex defi positions that are non-trivial to unwind. Even if a Cellar is in the Shutdown state, there still needs to be a mechanism to execute trades that unwind the complex positions in an intelligent way.

## Registry

The Registry contract is typically managed by a multisig (Gnosis Safe). The registry is a contract that holds the addresses of all the adapters that the Cellar will use. Typically, adding a new adapter to the cellar requires both the multisig on the Registry and Sommelier governance to approve the change. The Registry makes it so the Cellar will trust the new adapter, and Sommelier governance makes the new adapter callable by the strategist.

## PriceRouter

Prices are a very sensitive security boundary in the Cellar. if the strategist tends keep funds in adapters that are not directly withdrawalable from then catastrophic pricing exploits are unlikely. Never the less, the Price Router is responsible for getting the price of assets supported by the Cellars so deposits and withdrawls can be priced correctly. This is typically also managed by the multisig. The current deployment of the PriceRouter is set up with a 7 day timelock on changes to decrease the risk of a sudden change to share pricing.
115 changes: 115 additions & 0 deletions docs/using_cellars.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
### Using Cellars

A Cellar is a contract that holds and deploys funds in DeFi as managed by a strategist.

A Strategist begins by deploying a Cellar contract. The Base cellars contract is located here `src/base/Cellar.sol`. Typiccally a strategists will choose to deploy one of the permutations of the base cellar contract located in `src/base/permutations`


### Deploying Cellars

Examples of past production deployments of Cellars can be found in the `scripts` directory.

Below we are going to go through a mock deployment of a cellars.

1. First we configure the core set of variables.

The `devOwner` is the intitial owner of the Cellar contract. The ownership will be transferred to the Sommelier gravity contract instance after the Cellar is fully configured. The stratgists should have control of this address.

The `deployer` is the instance of the deployer contract that will be used to deploy the Cellar. This is an optional process that uses a deployer contract to get determinsistic addresses for the Cellars.

The `registry` is the instance of the registry contract that will be used to register adapaters for the Cellar. The registry is a contract that holds the addresses of all the adapters that the Cellar will use and is typically managed by a timelock multisig. We will go into more detail about how to configure the registry in another section.

The `priceRouter` is the instance of the PriceRouter contract that will be used to get prices assets for the Cellar. The priceRouter is a contract that interfaces with multiple price oracles to get the price of assets.



``` solidity
address public devOwner = 0xeeF7b7205CAF2Bcd71437D9acDE3874C3388c138;
Deployer public deployer = Deployer(deployerAddress);
Registry public registry = Registry(0xEED68C267E9313a6ED6ee08de08c9F68dee44476);
PriceRouter public priceRouter = PriceRouter(0xA1A0bc3D59e4ee5840c9530e49Bdc2d1f88AaF92);
```

Pick a permutation of the cellar for your contract. `CellarWithOracleWithBalancerFlashLoansWithMultiAssetDepositWithNativeSupport` is the state of the art cellar that supports some advanced capabilities.

These capabilities include

1. Balancer Flash Loans for leveraged staking.
2. A erc4626 Price Oracle for pricing the shares. These oracles allow amoritizing the costs of pricing assets in the cellar through an oracle so that deposits and withdrawals can be quite a bit cheaper than using the price router to price shares on every deposit and withdrawal.
3. MultiAssetDeposit allows the cellar to accept multiple assets as deposit addresses. The base cellar architecture only supported a single deposit address.
4. NativeSupport allows the cellar to support eth transfers for deposits without requiring the user to wrap their eth in weth.


``` solidity
CellarWithOracleWithBalancerFlashLoansWithMultiAssetDepositWithNativeSupport public cellar;
```

2. More cellar specific important variables

The `cellarName` and `cellarSymbol` are the name and symbol of the cellar. These are used to create the ERC20 token that represents the cellar shares.

The `holdingAsset` is the address of the asset that deposits and withdrawals are processed in.

The `holdingPosition` is the position id indicating the adapter that the cellar will use to manage the holding asset.

The `holdingPositionConfig` is the configuration for the holding position. This is a bytes array that is used to configure the holding position. The format of this bytes array is specific to the adapter that is being used.

The `initialDeposit` is the amount of the holding asset that the cellar will deposit into the holding position when the cellar is deployed.

The `platformCut` is the fraction of the fees generated by the cellar that will be sent to the Sommelier protocol via the gravity contract. This denominated in the holding asset.

``` solidity
string memory cellarName,
string memory cellarSymbol,
ERC20 holdingAsset,
uint32 holdingPosition,
bytes memory holdingPositionConfig,
uint256 initialDeposit,
uint64 platformCut
```

### Deploying the Registry

The Registry is a critical trust boundary in the security of the Cellar architecture. It enables a multsig to manage the adapters and positions that the cellar will use. This acts as a check and balance on Sommelier governance.

``` solidity
creationCode = type(Registry).creationCode;
constructorArgs = abi.encode(dev0Address, dev0Address, address(0), address(0));
registry = Registry(deployer.deployContract(registryName, creationCode, constructorArgs, 0));
```

Then deploy an adapter.

``` solidity
// Deploy SwapWithUniswapAdaptor.
creationCode = type(SwapWithUniswapAdaptor).creationCode;
constructorArgs = abi.encode(uniV2Router, uniV3Router);
swapWithUniswapAdaptor = deployer.deployContract(swapWithUniswapAdaptorName, creationCode, constructorArgs, 0);
```

Then register the adapter with the registry.

``` solidity
registry.trustAdaptor(swapWithUniswapAdaptor);
```


### Configuring the Price Router

The price router needs to be configured with the price oracles that the cellar will use to price assets. The price router is a contract that is generally managed by a timelock multisig and is used to get the price of assets in the cellar. The price router is used to price the shares of the cellar when deposits and withdrawals are made. The one instance of the price router can be shared by multiple cellars operated by the same strategist.

The price router should be deployed on any chain where the cellar will operate. If deploying on an L1 chain, the base price router should be deployed. If deploying on an L2 chain, the Sequencer Price Router should be deployed. The Sequencer Price Router is aware of liveness failures in the sequencer and can operate accordinly in the case of a sequencer halt.





###

0 comments on commit 20cc329

Please sign in to comment.