# Lido Treasury Diversification: tech proposal
## Background
[A proposal has been submitted] to the Lido Governance forum for selling 10% of the LDO token supply for 21,600 ETH. The received ETH should be transferred to the DAO treasury, and tokens should be assigned in a vested state from the DAO treasury, too.
If this proposal is to be accepted by the DAO, the DAO will need a mechanism for performing the exchange. Below we'll discuss several approaches to building such a mechanism.
[A proposal has been submitted]: https://research.lido.fi/t/proposal-ldo-treasury-diversification/458
## Requirements
* The amount of the purchase, the exchange rate, the vesting parameters, and token recipients are known in beforehand.
* As soon as the ETH funds are received by the DAO in full, the vesting recipients should be able to get the vested tokens.
* Until the ETH funds are received by the DAO in full, the vesting recipients should be unable to get any tokens.
## Approach 1 (trust-based)
The most trivial option is to execute the exchange as follows:
1. LDO recipients deposit their ETH to the DAO treasury address (the `Agent` smart contract).
2. As soon as the treasury receives the total amount of the purchase, 21,600 ETH, the DAO starts the vote (or multiple votes) for transferring vested LDO tokens to the recipients.
3. As votes pass, the recipients get their vested tokens.
The DAO can transfer vested LDO tokens to multiple recipients within the same vote by encoding multiple [`TokenManager.assignVested`] calls in a single EVM script. All transfers might not fit into a single vote due to the block gas limit, in which case several votes will be required.
This approach has an obvious downside of requiring LDO recipients to trust the DAO.
The upside is that it's quite simple to do and will take a couple of days to test and put to a vote.
## Approach 2 (trustless, one-by-one)
Each participants can execute their part of the deal individually with individually assigned lock/vesting times based on time of the purchase. The opprtunity to participate expires in one month.
A `PurchaseExecutor` smart contract is deployed, having the following interface:
* `__init__(eth_to_ldo_rate: uint256, vesting_cliff_delay: uint256, vesting_end_delay: uint256, offer_expiration_delay: uint256, ldo_recipients: address[], ldo_allocations: uint256[])` initializes the contract and sets the immutable offer parameters.
* `execute_purchase_for(recipient: address): payable` will, if there's enough ETH for a purchase allocated to the `recipient` address, assign vested tokens to the recipient by calling the [`TokenManager.assignVested`] function, and send any ETH change back to the `msg.sender`. The vesting start is set to the timestamp of the block the transaction is included to. Reverts unless the `recipient` is a valid LDO recipient, the amount of ETH sent with the call is enough to purchase the whole amount of LDO allocated to the recipient, and the offer is still valid.
* `__default__(): payable` does `execute_purchase_for(msg.sender)`.
The process is the following:
1. The DAO votes for granging the [`ASSIGN_ROLE`] to the `PurchaseExecutor` smart contract. This allows the contract to transfer tokens from the DAO treasury to any address.
2. Each purchasers call the `PurchaseExecutor.execute_purchase_for` function, sending ETH and receiving the vested LDO tokens. The list of purchasers and their allocated amounts are set during the `PurchaseExecutor` contract deployment.
3. After the offer expires (in one month), `PurchaseExecutor.execute_purchase_for` always reverts. No recovery of unsold LDO tokens is needed since unsold tokens remain in the DAO treasury.
### Pros
* Requires no coordination between purchasers.
* Can be done in parts.
* Fairly easy to implement and audit.
* Doesn't even need a web interface.
* Can be implemented and tested in 3-5 days.
### Cons
* Allows a purchaser to get their part of the total allocation even if the total allocation is not sold in full.
* Lock/vesting time will be different for different purchasers.
## Approach 3 (trustless, bulk)
The next two approaches differ from the previous one in that they only close the deal if the whole LDO allocation is sold.
A `PurchaseExecutor` smart contract is deployed, having three main interface functions:
* `__init__(eth_to_ldo_rate: uint256, vesting_cliff_delay: uint256, vesting_end_delay: uint256, ldo_recipients: address[], ldo_allocations: uint256[])` initializes the contract and sets the immutable transaction parameters.
* `execute_purchase(): payable` receives the preconfigured amount of 21,600 ETH and locks vesting start, cliff and end dates. Reverts unless the amount is received in full. Transfers any excess ETH back to the caller. Can be called by anyone.
* `claim_vesting(to: address): nonpayable` assigns vested tokens to the specified recipient (defaults to the caller) by calling the [`TokenManager.assignVested`] function.
* Direct ETH transfers to the contract are not possible.
The contract has two states:
* `PENDING` No ETH is received, vestings are not claimable.
* `EXECUTED` ETH is received in full, no further deposits are possible. Vestings are claimable.
The transition is only possible from the `PENDING` state to the `EXECUTED` state, not the other way, i.e. `EXECUTED` state is the final one:
```
execute_purchase
|
-> PENDING ----> EXECUTED.
```
The process is the following:
1. The DAO votes for granging the [`ASSIGN_ROLE`] to the `PurchaseExecutor` smart contract. This allows the contract to transfer tokens from the DAO treasury to any address.
2. The buyers coordinate, either offchain or using a smart contract, and deposit the ETH funds using the `PurchaseExecutor.execute_purchase` function. The function call succeeds iff:
* the full amount of ETH is received; any excess ETH is transferred back to the caller;
* the contract actually has the rights to assign the tokens.
4. After the previous step succeeds, the `PurchaseExecutor` transitions to the second state where one can claim vested LDO tokens to any preconfigured address. The vesting start is the same for all addresses and is set to the moment the `PurchaseExecutor.execute_purchase` call succeeded.
5. LDO recipients call `PurchaseExecutor.claim_vesting` function. Upon the call, `PurchaseExecutor` assigns vested tokens to the vesting recipient (the `to` argument). Only one `claim_vesting` call is allowed per recipient.
### Pros
* Ensures that the complete LDO allocation is sold.
* Vesting start/cliff/end dates are the same for all purchasers.
### Cons
* Requires coordination between purchasers.
* More complex than previous two approaches.
* Requires a UI.
* Takes a week to implement and test (incl. the UI).
## Approach 3.1
The previous approach has a downside of the need for coordination between vested token purchasers. This can be improved by deploying a second smart contract, `PurchaseCoordinator`, that will do this coordination trustlessly. The contract is deployed in addition to the `PurchaseExecutor` contract and has the following interface:
* `__init__(purchase_executor: address)` initializes the contract and sets the `PurchaseExecutor` contract address.
* `deposit_eth(vesting_recipient: address): payable` receives the ETH that will be later exchanged for the vested LDO assigned to the specified address. The call succeeds iff:
1. the `vesting_recipient` is a valid LDO recipient configured during the `PurchaseExecutor` deployment;
2. no ETH is currently deposited for `vesting_recipient`;
3. the amount of the received ETH is not less than the amount of LDO tokens to be sent to `vesting_recipient`, multiplied by the exchange rate. Any excess ETH is sent back to the caller.
* `execute_purchase(): nonpayable` performs the actual purchase, sending the whole ETH balance of the contract to the `PurchaseExecutor.execute_purchase` function. Can be called by anyone; the call succeeds iff the contract balance is exactly equal to the total purchase amount configured in the `PurchaseExecutor` contract (the `ethAmount` constructor parameter).
* `withdraw_eth(): nonpayable` allows the prior caller of the `deposit_eth` function to withdraw their ETH back, given that the purchase is not performed yet.
* Direct ETH transfers to the contract are not possible.
The contract has three states:
* `COLLECTING` The total ETH amount is not received yet:
* calling `deposit_eth` is possible for valid LDO recipients;
* the depositors are able to call `withdraw_eth`;
* calling `execute_purchase` is not possible.
* `PURCHASE_PENDING` The total ETH amount is received, purchase not performed yet:
* calling `deposit_eth` is not possible;
* the depositors are able to call `withdraw_eth` (this will transition the contract back to the `COLLECTING` state);
* calling `execute_purchase` is possible, transitioning the contract to the `PURCHASE_EXECUTED` state.
* `PURCHASE_EXECUTED` The purchase has been successfully performed, all ETH funds are forwarded to the DAO treasury by the `PurchaseExecutor` contract. This is the final state, no further actions are possible, any mutating call will revert.
The state diagram:
```
deposit_eth
withdraw_eth execute_purchase
| |
-> COLLECTING <-----> READY ----> PURCHASE_EXECUTED.
```
The process is the same as in the previous approach except the second step:
2. The buyers coordinate using the `PurchaseCoordinator` contract:
1. The individual purchasers deposit their ETH by calling `PurchaseCoordinator.deposit_eth` function.
2. As soon as all purchasers performed their deposits, someone calls `PurchaseCoordinator.execute_purchase` function which, in turn, calls `PurchaseExecutor.execute_purchase` and finalizes the purchase.
### Pros
* Ensures that the complete LDO allocation is sold.
* Vesting start/cliff/end dates are the same for all purchasers.
* Requires no trust between token purchasers.
### Cons
* The most complex approach, will definitely require an audit.
* Requires a UI.
* Takes a week to implement and test (incl. the UI).
[`TokenManager.assignVested`]: https://github.com/aragon/aragon-apps/blob/631048d/apps/token-manager/contracts/TokenManager.sol#L145
[`ASSIGN_ROLE`]: https://github.com/aragon/aragon-apps/blob/631048d/apps/token-manager/contracts/TokenManager.sol#L23