Problem statement
The Treasury Management Committee (TMC) was introduced last April as a result of Snapshot vote. The committee’s purpose is to develop constrained strategies, execute actions within Treasury Management Principles, and eventually automate itself away.
Key aspects include keeping ETH as the primary unit of account, supporting the Lido protocols’ integrity, growth, and robustness, minimizing risk of loss, and ensuring all treasury ETH is staked using Lido.
Its capabilities are presently confined to proposing votes via Aragon, such as TMC-0 (vote #161), highlighting the need for more flexible tools to enhance treasury agility and rebalancing. TMC-1 introduces the notion of an automated swapper triggered through an optimistic governance (like Easy Track).
A treasury swapper is an example of on-chain tooling that can implement TMC-1 and execute constrained actions, with optimistic purview by Lido DAO token holders. The swapper should adhere to the following requirements:
General requirements
- proposed solution should be able to be used by the TMC multisig
- Lido DAO token holders should always be able to veto any actions
- funds should only be available for a limited set of transactions, pre-approved by Lido DAO token holders;
- proposed solution should have restricted access to the Lido DAO treasury.
Technical requirements
- every trade should be MEV-protected;
- every trade should be “price-guaranteed” (the trade is expected to be cancelled if the minimum exchange amount is not received);
- beneficiary of every trade should be Lido DAO (treasury);
- variety of tokens for trade (both input and output) should be strictly limited by DAO;
- TMC should has the ability to operate trades (move funds from treasury, place orders, move funds back to treasury in case of unsuccessful trade);
- TMC should never take custody of Aragon funds;
Primary use-cases
- selling stETHs to stables (DAI/USDC/USDT)
- rebalancing the amounts of different stables (DAI/USDC/USDT)
Considering mentioned requirements it is proposed to develop a new technical solution Stonks, which will contain a fixed set of necessary operations for exchanging funds and managing the treasury.
Solution components
- Easy Track motion or Aragon vote (in case of emergency) for token transfering. These contracts will be used as is.
- Stonks - a set of contracts which act as a receiver of tokens from the first step and a container of swap operations set by Lido DAO;
- Cow Protocol - a fully permissionless trading protocol that enables batch auctions to maximize liquidity via Coincidence of Wants (CoWs) in addition to tapping all available on-chain liquidity;
- ChainLink - a decentralized oracle network that provides the USD price of Ethereum’s native cryptocurrency and tokens.
There are two directions of tokens movement between components:
- Swap (happy path) - when tokens are transferred from the DAO Treasury to Stonks for swapping using CoW Protocol. Swap proceeds are sent directly to the DAO Treasury (CoW Protocol API allows it).
- Recovery (contingency) - if the swap doesn’t execute for any reason, tokens must be returned to the DAO Treasury.
Happy path illustration
Step 0. A new Stonks instance is created with specific params and the DAO is proposed to add that contract to the Easy Track allowed recipients.
Step 1. Tokens are transferred from the DAO Treasury to the Stonks instance via Easy Track top up factories or Aragon vote.
Step 2. Order placement. TMC requests Stonks to deploy a new Order contract via placeOrder function and it automatically sends all available assets there. After deployment the Order emits an event about it’s creation, sets an allowance to the CoW vault relayer contract and waits until this order is completed.
Step 3. Order creation. Easy Track UI checks for Order Created
events and if it detects one, allows TMC to create an offchain order on Cow Protocol. This UI is trustless and can be used by anyone. The order price is checked against price tolerance parameters to simultaneously provide some margin to cover CoW Protocol fees + minor price fluctuations, but restrict from executing significantly unfavourable swaps (more details are in the specification).
Step 4. The Swap. At the moment of order fulfilment CoW Protocol debits funds from Order contract & sends all exchanged assets to the DAO Treasury.
Recovery (contingency) flow illustration
If for any reason swap isn’t going through, there must be a method to recover the tokens. Tokens can be at Stonks (before the swap is requested) or at Order (once the swap is requested on Stonks).
Step 4. The swap hasn’t happened and the order has expired. Anyone can return all funds to the Stonks contract for further actions in a permissionless manner. From this moment Order contract becomes inactive.
Step 5. At the moment, in case of market turbulence the DAO can decide what to do next:
- send all funds to the DAO Treasury back;
- create a new order again.
For more details about each step please read the specification.
Voting actions
Lido DAO is proposed to add following contracts to the set of Lido on Ethereum protocol in case of successful Aragon vote:
new TopUpAllowedRecipients ET factory addresses
These factories are used to request tokens from the Treasury, to be swapped in Stonks orders (TMC multisig selects a factory according to the swap direction, for example stETH → DAI, and enters the amount to be swapped).
TopUpAllowedRecipients for STETH → DAI / USDC / USDT (ADDRESS_TBA)
parameters:
- AllowedRecipientsRegistry (0x1a7cFA9EFB4D5BfFDE87B0FaEb1fC65d653868C0);
- TopUpAllowedRecipients (0x6e04aED774B7c89BB43721AcDD7D03C872a51B69);
- Top up limit: 9_000 * 10 **18 ($30M equivalent using 30d TWAP rounded to the nearest thousand);
- Limit refresh frequency: every six months
TopUpAllowedRecipients for DAI /USDC / USDT → USDC / USDT / DAI (ADDRESS_TBA)
parameters:
- AllowedRecipientsRegistry (0x3f0534CCcFb952470775C516DC2eff8396B8A368)
- TopUpAllowedRecipients (0x0d2aefA542aFa8d9D1Ec35376068B88042FEF5f6)
- Top up limit: 10_000_000 * 10 **18 ($10M);
- Limit refresh frequency: every three months
Stonks contracts
These contracts create order instances initiating CoW swap orders using tokens and parameters received from TopUpAllowedRecipients and AmountConverter. CoW orders are created via ET UI, following a preliminary double check of the minimal amount that the treasury should receive in case of success.
- STETH→DAI (0x3e2D251275A92a8169A3B17A2C49016e2de492a7)
- STETH→USDC (0xf4F6A03E3dbf0aA22083be80fDD340943d275Ea5)
- STETH→USDT (0x7C2a1E25cA6D778eCaEBC8549371062487846aAF)
- DAI→USDC (0x79f5E20996abE9f6a48AF6f9b13f1E55AED6f06D)
- DAI→USDT (0x8Ba6D367D15Ebc52f3eBBdb4a8710948C0918d42)
- USDT→USDC (0x281e6BB6F26A94250aCEb24396a8E4190726C97e)
- USDT→DAI (0x64B6aF9A108dCdF470E48e4c0147127F26221A7C)
- USDC→USDT (0x278f7B6CBB3Cc37374e6a40bDFEBfff08f65A5C7)
- USDC→DAI (0x2B5a3944A654439379B206DE999639508bA2e850)
In addition to mentioned ET factories and Stonks the AmountConverter contract is to be deployed.
AmountConverter (0x12cc60eea45F705f069B43095FbF2Fb3c7f874c1)
This contract provides functionality to retrieve expected token conversion rates based on the Chainlink Price Feed. It also sets immutable variables:
- FEED_REGISTRY = 0x47Fb2585D2C56Fe188D0E6ec628a38b74fCeeeDf (ChainLink price feed registry)
- CONVERSION_TARGET = 0x0000000000000000000000000000000000000348 - USD (‘840’ in decimal notation)
- ALLOWED_TOKENS_TO_SELL = [STETH, DAI, USDC, USDT]
- ALLOWED_TOKENS_TO_BUY = [DAI, USDC, USDT] (same addresses as in previous bullet point)
- PRICE_FEEDS_HEARTBEAT_TIMEOUTS = [
- 3600n + 15n * 60n, - 1 hour 15 minutes for the stETH/USD feed
- 3600n + 15n * 60n, - 1 hour 15 minutes for the DAI/USD feed
- 24n * 3600n + 30n * 60n, - 24 hours 30 minutes for the USDC/USD feed
- 24n * 3600n + 30n * 60n, - 24 hours 30 minutes for the USDT/USD feed]
- Here 15/30 minutes are added to the default heartbeat values to compensate for non-critical delays of the updates (more details here)
Audit of Stonks contracts is being finalized and the report to be provided in the comments to this post before the Aragon vote starts.
UPDATE Added addresses and corrected limit for TopUpAllowedRecipients stables factory to $10M quarterly.