Cove RFC
Cove is an asset management protocol designed to maximize returns through intelligent automation. Cove simplifies complex strategies into accessible, yield-bearing products.
Background
Traditional automated market makers (AMMs) are not well suited for portfolio or index construction because they suffer from loss-versus-rebalancing (LVR) due to toxic order flow (i.e. all trades execute at worse-than-market prices).
”We found that for the 17 pools we analyzed, covering 43% of TVL and chosen by size, composite tokens and data availability, total fees earned since inception until the cut-off date was $199.3m. We also found that the total IL suffered by LPs during this period was USD 260.1m, meaning that in aggregate those LPs would have been better off by USD 60.8m had they simply HODLd.”
Stefan Loesch, Nate Hindman, Mark B Richardson, Nicholas Welch: “Impermanent Loss in Uniswap v3”, 2021; arXiv:2111.09192.
“Thus, providing liquidity has become a game reserved for sophisticated players with the introduction of Uniswap V3, where retail traders do not stand a chance.”
Lioba Heimbach, Eric Schertenleib, Roger Wattenhofer: “Risks and Returns of Uniswap V3 Liquidity Providers”, 2022; arXiv:2205.08904. DOI: 10.1145/3558535.3559772.
We propose an alternative solution that eliminates LVR and guarantees general intent level execution as a lower bound (e.g. CoW Swap).
This document introduces the BasketManager contract, which handles protocol deposits/withdrawals/LP tokens, custodying user assets, and managing the rebalance lifecycle. Storing user assets in one contract provides two major advantages from economies of scale: optimized/cheaper rebalancing (at the protocol level vs. for each basket) and increased value capture from matching trades internally.
Summary
Cove functions as an intent aggregator for liquidity providers (LPs). Users express their intent by choosing a weight strategy and a combination of ERC-20/4626 tokens (represented as an LP token). These choices form a basket, such as Gauntlet-optimized yield strategy with sDAI/sFRAX, a market cap weighted index strategy with stETH/sfrxETH/yETH, or custom weights. This approach limits the number of options while allowing the protocol to aggregate deposits efficiently. The weight strategy determines the weights of each token in a basket. When the protocol rebalances, there is the potential for coincidence of wants (CoW) to exist between baskets, like ring trades in CoW Swap’s batch auctions. Approximately maximizing the volume of CoWs given the current/target protocol level allocations is solvable off-chain with linear programming. When there are matches, trades execute without leaking value (i.e. no price impact/slippage/fees/MEV from accessing exogenous liquidity). External trades are routed through CoW Swap, which now supports programmatic orders including TWAP via their Programatic Order Framework, to ensure the best execution and capture positive slippage. The protocol includes configurable/switchable fees for management and internal trades.
To ensure safe rebalancing, BasketManager follows a sequence of proposing, executing, and finalizing a rebalance while temporarily pausing deposits and withdrawals for the affected baskets. As BasketManager pools assets for all Baskets, the rebalancing process considers each Basket’s asset preferences and weight strategy. For example, when a new deposit arrives for a Basket with a limited token selection and a market cap weighted strategy, BasketManager deploys funds according to the respective weights, crediting the Basket for the new deposit without impacting other Baskets. Once the rebalance is complete, the tracked balances within BM are updated to reflect any changes resulting from swaps since the previous rebalance.
Terminology
📄 Contract
👮 Permissioned Actor
🧑 Permissionless Actor
- Smart Contract - BasketManager 📄
- The BasketManager contract is the core component of the Cove protocol, responsible for managing user deposits, withdrawals, and LP tokens. It also handles the rebalancing process, custody of user assets, and execution of trades. By pooling assets from all baskets, BasketManager enables efficient rebalancing and increased value capture through internal trade matching.
- Smart Contract - BasketToken 📄
- Token representing BasketManager’s user deposits. Each BasketToken is defined and created with immutable selection of assets that will be considered for allocating and a weight strategy which determines the weights of the selected assets. The maximum number of selected assets will be capped and configurable (e.g. start at 4 with a maximum of 8).
- Smart Contract - WeightStrategy 📄
- Contract that each Basket is linked to for determining asset weights. Each weight strategy contract is associated with a single weight proposer who controls weight updates. Weights define the proportions of assets within a basket. Multiple baskets with varying asset selections can use the same WeightStrategy, provided the WeightStrategy supports all assets chosen by each basket.
- When a Basket is created, an WeightStrategy address must be supplied.
- Smart Contract - SwapAdapter 📄
- Adapters are modular contracts that are written to execute trades (e.g. via an aggregator or auction). BM can support both intent based aggregators and on-chain execution aggregators as long as their parameters/states are verifiable by BM.
- Intent based aggregators that support smart contract signing via ERC-1271 are supported, such as CoW Swap.
- A generic dutch auction adapter with a flash loan-like mechanism could be implemented such that any on-chain user-executable aggregator is compatible. This also allows for easy deployment to other chains or L2s.
- Another adapter could be used to swap more efficiently between 4626 tokens with the same underlying assets, or for lossless conversions between assets like USDC<>DAI using MakerDAO’s PSM.
- Actor - Weight Proposer 👮/📄
- Actors responsible for updating their associated WeightStrategy contracts with the new weights for all baskets subscribed to it.
- Actor - Rebalance Proposer 👮/📄
- Role that initiates the rebalance lifecycle.
- Actor - TokenSwap Proposer 👮
- Calculates an optimized set of internal and external trades to execute in this rebalance and submits them to BasketManager.
- Actor - TokenSwap Executor 👮
- Role that submits the calldata required to execute the submitted external trades.
- If the Basket’s AggregatorAdapter is synchronous, TokenSwapProposer also executes the swaps.
- Actor - Rebalance Executor 🧑
- Permissionless role that completes an ongoing rebalance and advances the epoch. Checks on the progress of the current rebalance are made.
External Assumptions
- Dependent oracles do not report incorrect prices.
- BasketManager does not have on-chain access to live quotes from aggregators.
- We assume the confidence intervals given by Pyth Oracles are sufficient to use. An oracle registry will be managed by the DAO and individual Oracle wrappers can be deployed to support additional operations (aggregating, applying 4626’s price per share, etc).
- TokenSwapProposer uses the ideal exchange rates derived from on-chain oracles instead of live quotes when generating the list of internal and external trades.
- RebalanceProposer will submit a set of internal and external trades that describes the changes in ALL baskets.
- Assets in BM are high quality (e.g. USDT, USDC, sDAI, sFRAX, stETH, cbETH) - liquid with low volatility and minimal instances of depegging.
- BasketManager is designed for standard ERC-20/4626 tokens, and is not concerned with the potential effects of rebasing or non-standard ERC-20/4626 implementations such as fee-on-transfer tokens.
Entity–relationship model
Rebalance lifecycle flowchart
User actions flowchart
Overview of the rebalancing sequence
- Each Weight Proposer asynchronously updates any WeightStrategy instances that they manage.
- Updates can be proposed until the rebalance starts. For resolvers that have not been updated since the last rebalance, the prior target weights will be reused.
- If a Weight Strategy’s methodology is difficult to prove or implement on-chain, the Weight Proposer for the strategy should be permissioned.
- Rebalance Proposer (DAO) calls
BasketManager.proposeRebalance()
.- Calculate the new total target token amounts based on each basket’s target weight.
- TokenSwap Proposer (DAO) calculates the optimal swap pairs to reach the target amounts of each basket and calls
BasketManager.proposeTokenSwap(Trade[] internalTrades, Trade[] externalTrades)
to submit all necessary swap amounts and associated basket information. BasketManager performs minimal on-chain validation to ensure the inputs align with each basket’s target weights. If more advanced validation is needed, the role can be assigned to a separate contract using Axiom, transforming the process from a variable to a fixed cost.
- TokenSwap Executor uses quotes from the relevant API and then calls
BasketManager.executeTokenSwap(externalTrades, bytes data)
where the data is optional information required for the current token swap adapter.
- If required, TokenSwap Executor submits orders to the off-chain API for execution (e.g. CoW Swap API).
- Responsible for reading the stored orders from BM and submit them using off-chain API.
- CoW Swap orders have unique IDs so once a trade is executed it can no longer be filled so replays or duplicate submissions are not problematic.
- Responsible for reading the stored orders from BM and submit them using off-chain API.
- Rebalance Executors calls
BasketManager.completeRebalance
, executing any checks and validations needed to complete the rebalance and advance the epoch.- Responsible for completing rebalances and advancing the epoch.
Window timings and retries
Governance can adjust window times and other parameters.
proposeRebalance
- Charges management fee and processes deposits for rebalancing baskets.
- Determines target weights.
- Opens a 15-minute window for
proposeTokenSwap
. - If proposer role doesn’t call within the window, anyone can call
completeRebalance
.
proposeTokenSwap
- Settles internal trades and checks minimum amounts for external trades against on-chain oracle values.
- Opens a 15-minute window for
executeTokenSwap
. - If not called within the window, anyone can call
completeRebalance
.
executeTokenSwap
- Executes token swap with extra data if necessary.
- After 15 minutes, anyone can call
completeRebalance
.
completeRebalance
- Checks if close to target weight.
- If not, retries up to 3 times, resetting the state as if
proposeRebalance
was just called.
- If not, retries up to 3 times, resetting the state as if
- If pending redemptions can’t be fulfilled (not enough base asset), basket tokens are notified of the failed rebalance and users must claim shares back.
- Checks if close to target weight.
Sequence diagrams
CoW Swap adapter
Generic dutch auction adapter
Basket actions
Creating a new Basket
Baskets are specified by their:
- Combination of assets to be considered in the allocation.
- Address of the weight strategy contract
The Basket is then deployed using keccak256(uint256 selectionBitFlag, address weightStrategy)
as the salt, preventing duplicate deployments of the same config. Creation of new basket is permissioned.
Deposit/Mint
Deposits will be asynchronous and will be fulfilled at rebalance. Users can claim their shares after the next rebalance is finished.
- Users requests a deposit and transfers in the base asset.
- Protocol processes deposits at the next rebalance proposal.
- After the rebalance is finished, users who requested deposit can now claim their shares.
Redeem
Redeem will also be asynchronous if the user desires to receive the base asset only. Alternatively, we offer users to the ability to redeem basket tokens at any time for the their pro-rata allocation of the underlying tokens.
- Synchronously
- Users call
proRataRedeem
- Burn basket token from the user.
- Transfer the currently allocated assets of the basket the user calculated pro-rata.
- where is the total quantity of the i-th token in the basket, and is the total supply of basket tokens.
- Users receive a set of tokens instead of USDT.
- Users call
- Asynchronously
- Users request to redeem and transfers in the basket token shares.
- Process redeems at the next rebalance.
- If the rebalance is successful, users who previously requested redeem can claim USDT and BM will burn the basket token shares.
LP tokens
Cove will use the ERC-7540 standard for LP tokens and include permit functionality using Uniswap’s Permit2 (EIP-2612).
Pricing LP tokens
Problems
- It is difficult to know the price of each Basket’s tokens in USDT due to the portfolio aspect of Baskets, exchanging USDT to/from different assets every epoch.
Solutions
-
Use Chainlink oracle prices of base asset combined with 4626’s price per share:
- = Number of different ERC-4626 tokens in the basket
- = Quantity of the j*-*th ERC-4626 token in the basket
- = Oracle price of the underlying asset of the j*-*th ERC-4626 token
- = Price per share of the j*-*th ERC-4626 token in terms of the underlying asset
- = Total supply of basket i
- Does not reflect real value of basket tokens unless withdrawn pro-rata.
Risks
- This pricing will not account for execution costs for acquiring the assets thus relying on this could penalize depositors.
- This value is generated at the time user views the call.
Rebalancing optimization with linear programming
Problem
As number of baskets and available assets grows, it becomes not trivial to match intents. We will use linear programming to maximize the volume of internal trades. This minimizes losses (price impact/slippage) from accessing exogenous liquidity.
Variables
Let:
- be the set of baskets
- be the set of assets
- be USDT
- be the set of all possible internal trades between two different baskets
- be the set of all possible external trades
- be the current holdings of asset in basket
- be the ideal target holdings of asset in basket
- be the internal trade variable representing the amount of asset to trade from basket for asset from basket
- be the external trade variable representing the amount of asset to trade from basket to an external party for asset
Determining Current Holdings
This would be the balances of each asset including USDT that each basket is currently holding. The amount of USDT is the new user deposits that have accrued since the last rebalance process.
Determining Target Holdings
In order to determine the target holdings of assets, we need to account for users’ deposit requests and redeem requests. First we calculate how much ideal amount of USDT will be withdrawn from the basket. (Note that this is only an estimation and would not account for fees/slippage. The real amounts we end up with will vary based on slippage of each trade pairs). Since we know the total supply of the basket token and the amount of basket tokens submitted by the users for redeeming, the below formulae would be used for the target holding amounts.
- Let
- USD value of basket i before rebalancing, including the pending USDT deposit.
For the USDT target amount:
For other assets’ target amounts:
Then after the rebalancing is successful, the resulting balance of USDT will be claimable by redeem requesters.
Objective Function
The objective function balances the cost of internal and external trades, heavily penalizing the latter to encourage internal matches when possible.
We aim to minimize the volume of external trades with a higher penalty:
where is a large penalty constant for external trades (e.g., ).
Constraints
-
No Negative Holdings After Internal Trades:
For each basket and each asset :
This ensures that no basket ends up with a negative quantity of any asset due to internal trades.
-
External Trades Limited by Post-Internal Trade Balances:
For each basket and each asset :
This ensures that any external trades are limited by the available balance of the asset in the basket after accounting for internal trades.
-
Target Holdings Met After All Trades:
For each basket and each asset
This requires that the final holdings in each basket must match the target holdings after all trades have been completed.
-
Trade Variables Non-Negative:
For all and :
This ensures that all trade variables are non-negative, which is a standard requirement in trade optimization problems to prevent nonsensical negative trades.
Calculating deposits and redeems
- Let
- USD value of basket i before rebalancing, including the pending USDT deposit.
- Let
- Ideal USD value of basket i, using ideal target balances for each token.
- Let
- Actual USD value of basket i after all trades are successfully executed. This will be less then the ideal target balances due to slippage/costs.
First, the USDT that should be allocated for the requested redeems is calculated using the ideal exchange rate of the assets. Then we attempt to satisfy that the ideal USDT amount that should leave the basket.
Then after the swaps are executed, for each basket, the amount to mint for deposits are determined by measuring the change of value of the basket
-
- In case any of the trades associated with redemption fails, we replace the withdrawn value with the sum of value of the assets that will be withdrawn via
proRataRedeem
method.
- In case any of the trades associated with redemption fails, we replace the withdrawn value with the sum of value of the assets that will be withdrawn via
During a rebalance period where there are new deposits, the slippage associated with rebalancing will be paid by the depositors not the existing basket users.
In the ideal scenario, the prices at the end of the rebalance will not deviate greatly from the the beginning of the rebalance. In the case that it does, we use the latest oracle price to determine the depositors value.
Fees
The protocol incorporates a configurable management fee for each basket and a global internal trade fee, both measured in basis points (BPS).
The management fee is calculated based on the time elapsed since its last computation, occurring at the start of every rebalance cycle and when users exit via pro-rata redeem. This fee is then divided between the protocol treasury and the strategy sponsor according to the protocol’s configuration in FeeCollector contract.
The internal trade fee, set on a global level, is charged as both buy and sell token upon every internal trade settlement. The protocol admin can claim this fee, which sends the underlying token to the protocol treasury without any further division.
Oracles
Cove uses oracles to value baskets, settle internal trades, and set minAmounts for rebalance swaps. Cove will support pluggable oracle implementations. The initial implementation will first sanity check each of the responses (e.g. stablecoins should be between 1.10 or ETH LSTs should be between 0.9 and 1.1 ETH) and then validate that primary response is within some threshold of the secondary/tertiary responses. If any checks fail, the associated baskets will be paused until resumed by the operational multisig or governance.
- Primary: Live or near live price oracle
- Pyth (preferable for settling internal trades since real-time prices can be pulled for accuracy, cost permitting)
- https://pyth.network/pyth-vs-chainlink
- https://pyth.network/blog/pyth-launches-price-oracles-on-ethereum-and-optimism
- Pyth does not automatically submit the prices to the EVM networks: protocols and users are responsible for updating the on-chain Pyth price before using it. You may find a thorough explainer on the On-Demand Model in our docs.
- DEX TWAP or EMA oracles
- Pyth (preferable for settling internal trades since real-time prices can be pulled for accuracy, cost permitting)
- Secondary: Update threshold based oracle
- Chainlink
- Chronicle protocol
- Similar update cadence and limits as Chainlink
- Tertiary: Uniswap V3 TWAP
- Other potential options include:
- Gyroscope’s Consolidated Price Feed (CPF)
- Includes a series of circuit breakers to protect against faulty oracles.
- Gyroscope’s Consolidated Price Feed (CPF)
Supporting points programs
In order to support points programs that are currently popular in the meta, BasketManager will include the following functionality:
- An exec function that is scoped to allowlisted function signatures, which can only be added behind a timelock (e.g. to claim airdrops).
- A rescue function that is scoped to assets not in the protocol’s asset universe (e.g. to send the claimed airdrops for distribution).
Risks
- Allocation proposers gas payment increases as the number of baskets grow.
- Large amount of Baskets could be created to DDOS the system.
- Incorrect price calculation of a single asset could lead to incorrect accounting of the baskets.
- If Cove is unable to complete swaps to facilitate withdrawals users will be required to withdraw their assets in-kind.
Alternatives considered
- Rebalancing methods
- Auctions
- The problem with auction mechanisms for frequent rebalancing is as follows (from Aera V1 Weight Update):
- Auctions are overly sensitive to current market price,
- Auctions restrict participation to larger pools of capital and infrastructure to identify and participate in the auction.
- The main advantage from using an auction mechanism like Kuiper is removing the oracle dependency. However, that dependency is already implicit since oracles are used to value basket tokens. In theory, CoW Swap should offer better execution due to ring trades, and by outsourcing this functionality we can reduce the complexity of the protocol and tap into CoW Swap’s existing network of solvers. Cove will also support using dutch auctions to rebalance via an adapter as a fallback.
- Existing work
- Kuiper (oracle-less) - https://kuiper.gitbook.io/kuiper-docs/protocol/rebalancing-auctions
- Set (uses MakerDAO price feeds) - https://help.tokensets.com/en/articles/3399436-how-to-participate-in-set-protocol-rebalance-auctions
- The problem with auction mechanisms for frequent rebalancing is as follows (from Aera V1 Weight Update):
- Balancer’s Managed Pools (Aera V1)
- While it retains some benefits of TWAP’ing, still suffers from loss-versus-rebalancing.
- Auctions
- Omitting contract events and using shadow events for gas optimization (Shadow)
- Seems like a premature optimization given how much tooling relies on events.
Related work
Acknowledgements
Special thanks to Joey Santoro (Variant), Tarun Chitra (Gauntlet), Ken Deeter (Electric), Saneel Srini (Accomplice), Peteris Erins (Auditless/Aera), Elliot Friedman (Solidity Labs), Storm Slivkoff (Paradigm), and Will Price for their feedback after reviewing drafts.