Provide quotes/budget for Smart Treasury development

I would love to hear the rationale for whitelisting liquidity providers. Is it just so the protocol has full ownership of the treasury?

I’d like to summarise where this discussion has gone so far, by writing down concretely what the spec is that we want from this smart treasury.

Smart Treasury Candidate v0.1
The smart treasury will be built using a balancer smart pool which allows flexible parameter changes of the pool, as the needs of the protocol change over time.

  • Tokens: IDLE, ETH,
  • Weights (Initial): 99/1 (IDLE/ETH) [Gradually adjusted after bootstrapping]
  • WeightsAfter3Months: 90/10 (IDLE/ETH)
  • SwapFee: 0.5% (pending snapshot vote completion)
  • UseWhiteList: False
  • CanChangeSwapFee: True
  • CanChangeWeights: True
  • MinimumGradualUpdateDurationForSwapFee: 3 days
  • CanChangeTokens: True
  • AddTokenLockTime: 3 days
  • CanLimitBPTSupply: False

Bootstrapping Mechanism Candidate v0.1
The mechanism for initially funding the smart treasury using the ecosystem fund, and fee treasury.

  1. Withdraw 130,000 IDLE from ecosystem fund (representing 1% of supply)
  2. Convert all assets in FeeTreasury to ETH
  3. If ETH value != 1,300 IDLE (1% of 130,000), sell UP TO 1300 IDLE on the open market until ETH value is equivalent to 1% of IDLE deposit
  4. Deposit IDLE & ETH into the pool, bootstrapping the smart treasury.

This will probably need to be written as a smart contract too.
There will need to be an IDLE and ETH oracle in order to do this calculation. I suggest a 7-day average of uniswap price.

Fee Collection Candidate v0.1
All idletokens (ie IdleUSDCYield, IdleDAIRisk …) have the fee address pointing to FeeCollectionContract.

This is the minimum interface which the contract needs to implement.

interface FeeCollectionContract {
   function deposit () {}
   function setSplitRatio(uint ratio) {} // ratio of fees sent SmartTreasury vs FeeTreasury
   function setFeeTreasuryAddress(address newFeeTreasuryAddress) {}
   function setSmartTreasuryAddress(address newSmartTreasuryAddress) {}

   function addAddressToWhiteList(address addressToAdd) {}
   function removeAddressFromWhiteList(address addressToRemove) {}

   function addTokenToDepositList(address tokenAddress){}
   function removeTokenFromDepositList(address tokenAddress) {}

   function setAdmin(address newAdmin) {}
}

This contract will keep track of tokens which IDLE can collect fees in, such as DAI, USDC, COMP, WBTC.

  • deposit() should only be called by a whitelisted address, according to the split ratio, a portion will be directly sent to the Fee Treasury, the other portion will be converted to ETH, and added to the balancer SmartTreasury.
  • SetSplitRatio() Set what portion of fees as sent to SmartTreasury vs the regular FeeTreasury.

I think initially the admin should be set to a dev multisig account, in case there is anything that they need to do to get this contract ready, and after that it’s ownership can be transferred to governance.

I think the initial split ratio should be 80%, this, of course, can be adjusted over time by future governance.

Where to go from here
As a community, we should debate these parameters, and specs until we are happy with the requirements, then we can move onto the next phase.

5 Likes

I like the idea, just some questions:

  1. What would be the best percentage for the pool in order to minimize IL and Slippage? 90/10 - 80/20 - 66/33 or what else? Maybe @Teo and @william can be more specific.

  2. How about to insert into a pool a small percentage of $BAL? So we will increase the rewards from the Balancer pool, and we need to ask to put $IDLE into a whitelist too… The $BAL can be converted into ETH and accumulated into the Fee Treasury for later uses.

  3. The idle protocol fees are a little low, near $1000 a weeks (as shown by @simoneconti’s analysis), maybe it would be better to wait at least 2 or 3 weeks to be able to put the fees in the Smart Treasury, isn’t?

  4. How to create a staking smart contract for the tokenholders? They can put in stake their $IDLE with a cooldown of 7 days, they receive rewards and the $IDLE in staking will be put into the Smart Treasury? Maybe we can use the idle protocol fees for incentivize the staking.

  5. How much is the Ecosystem Fund? How much 130.000 $IDLE will affect the fund for future uses?

3 Likes
  1. The optimal percentage in regards to slippage is 50/50.
  2. Perhaps this can be done in a future vote, we need to get $IDLE whitelisted to be eligible for rewards.
  3. I’m not sure if you’re referring to the bootstrapping, or for the fee collection contract, but I’ll answer both.
    • Bootstrapping: If there are not enough fee’s collected to match the deposit, then we would need to do a 1 time market sell in order to raise the funds.
    • FeeCollection: A whitelisted address should only call the deposit() function, when there are enough fee’s accrued, and to minimising the % of gas costs, as @simoneconti suggested, using some threshold, ~$1000, however this is for a single asset deposit, this approach deposits all tokens from the contracts internal whitelisted token list at once, so maybe this threshold needs to be a bit higher. In anycase this is just an optimisation, and can be determined once this is developed further.
  4. I assume that we could reuse the multitude of staking contracts which have been developed recently, and adjust it as we need, if we were to implement a staking program.
  5. The ecosystem fund has 1,950,000 $IDLE in it, representing 15% of total supply.
1 Like

:smiley: :+1:t2: Thanks you @8bitporkchop

So, maybe a middle way can be 70/30 or at most 80/20

yes, true, mine was meant to be a suggestion, mybe for the future of the pool.

ok, so the $130K for the bootstrap of the pool it’s just a little part of the ecosystem fund, near the 6%.

yes, i’m referring to the fee collection of the platform, the 10% of the interest earned. Ok, it’s right, we can think about the threshold further.

1 Like

The initial weights will be 99/1 in order to bootstrap the treasury. This will be gradually changed to 90/10 over 3 months starting from when the pool is created. If there is community consensus this could be adjusted to 80/20 over 3 months.

The discussion so far has been for a 90/10 initial weighting, with the possibility to change this in the near future, based on how the smart treasury performs.

4 Likes

Thanks for the extensive summary @8bitporkchop I think we are on the right track!

I think there should be an intermediate step where fees are transferred from the current FeeTreasury to the contract needed for the conversion which is needed imo (for steps 2, 3 and 4)

Details for the FeeCollection contract looks good to me.

I just want to clarify some points of the processes for the on-chain proposals. Currently there is a maximum of 10 actions that can be executed per proposal see here.

This is considered an action

These are multiple actions

The change of fee address for all tokens are 9 actions (6 tokens for the best yield and 3 for the risk adjusted)

so the proposal may need to be splitted in 2 (eg first upgrade the Best Yield strategy tokens and then the Risk adjusted ones with another proposal) or better the use of a contract for batched delegatecalls may be needed (something similar to this I think but with delegatecalls)

3 Likes

For 1. I don’t think that there is a correct answer that can both minimize slippage AND Impermanent Loss for LPs but in the vote for the weights of the Smart Treasury the 90/10 was the most voted.

Regarding point 2. and 4. I think that those may be a good start for separate posts

3 Likes

Incorporating your recommendations to the bootstrapping candidate. And including a step to manage the pool tokens, and migrate control of the pool to governance.

Bootstrapping Mechanism Candidate v0.2
The mechanism for initially funding the smart treasury using the ecosystem fund, and fee treasury.

  1. Deploy Smart Treasury bootstrapping smart contract (BSC)
  2. Withdraw 130,000 IDLE from ecosystem fund (representing 1% of supply) and deposit to BSC
  3. Withdraw all assets from FeeTreasury to BSC
  4. Convert FeeTreasury assets to ETH in BSC
  5. If ETH value != 1,300 IDLE (1% of 130,000), sell UP TO 1300 IDLE on the open market until ETH value is equivalent to 1% of IDLE deposit
  6. Convert ETH to WETH (Balancer only supports WETH)
  7. Deposit IDLE & WETH into the pool, bootstrapping the smart treasury.
  8. Send BalancerPoolToken to FeeTreasury. (Token representing stake in pool).
  9. Set pool controller as an address governance controls. (calling setController())

There will need to be an IDLE and ETH oracle in order to do this calculation. I suggest a 7-day average of uniswap price.

Regarding on chain, I got your point about 10 actions. Perhaps we should roll this out in phases, Phase 1 being setting up the pool, using the ecosystem fund to bootstrap. Phase 2 being configuring the strategy tokens to use the FeeCollectionContract, where we can use the batched delegatecalls.

4 Likes

:blush: :blush: Ok thanks…
Maybe when the pool is started we can discuss about adding $BAL and if create a retribuited smart contract for tokenholders.

3 Likes

This is referred to step 4. right? Maybe the spot price is enough to convert at current value if needed (with an oracle)

I think those should be sent to the FeeCollector contract and this contract should also have a withdraw method in case some of the liquidity needs to be partially removed at some point from the Treasury.

It could be an idea even though this would require more time (ie pass and prepare 2 proposals instead of one)

1 Like

You have to request the token addition in our discord on channel #token-requests =)

5 Likes

I agree with these changes, here’s the final spec that we can go with.

Fee Collection Candidate v0.2

interface FeeCollectionContract {
   function deposit () {} // called by whitelisted address
   function setSplitRatio(uint ratio) {} // ratio of fees sent SmartTreasury vs FeeTreasury
   function setFeeTreasuryAddress(address newFeeTreasuryAddress) {} // called by admin
   function setSmartTreasuryAddress(address newSmartTreasuryAddress) {} // If for any reason the pool needs to be migrated, call this function. Called by admin

   function addAddressToWhiteList(address addressToAdd) {} // Whitelist address. Called by admin
   function removeAddressFromWhiteList(address addressToRemove) {} // Remove from whitelist. Called by admin

   function addTokenToDepositList(address tokenAddress){} // Register a token which can converted to ETH and deposited to smart treasury. Called by admin
   function removeTokenFromDepositList(address tokenAddress) {} // Unregister a token. Called by admin

   function withdraw(address toAddress) {} // withdraw balancer liquidity token to address. Called by admin

   function setAdmin(address newAdmin) {} // called by admin
}

withdraw() method is used incase liquidity needs to be partially removed from treasury, as @william described.

This also means that the bootstrapping mechanism is slightly different. And also I’ve included the recommendation to use spot price with an oracle.

Bootstrapping Mechanism Candidate v0.3
The mechanism for initially funding the smart treasury using the ecosystem fund, and fee treasury.

  1. Deploy Smart Treasury bootstrapping smart contract (BSC)
  2. Withdraw 130,000 IDLE from ecosystem fund (representing 1% of supply) and deposit to BSC
  3. Withdraw all assets from FeeTreasury to BSC
  4. Convert FeeTreasury assets to ETH in BSC
  5. Retrieve IDLE/ETH conversion rate using an oracle.
  6. If ETH value != 1,300 IDLE (1% of 130,000), sell UP TO 1300 IDLE on the open market until ETH value is equivalent to 1% of IDLE deposit (as defined in step 4)
  7. Convert ETH to WETH (Balancer only supports WETH)
  8. Deposit IDLE & WETH into the pool, bootstrapping the smart treasury.
  9. Send BalancerPoolToken to FeeCollection Contract. (Token representing stake in pool).
  10. Set pool controller as an address governance controls. (calling setController() )

I will leave this spec open for a few days for feedback, but after this, I think we are ready for the next phase. Looking forward to guidance by @Teo and @william to support with this :smiley:.

4 Likes

In order for the smart treasury to work as a buyback machine in the future, there needs to be a whitelist where the protocol is the singular liquidity provider so that the tokens bought back are essentially taken out of circulation. In addition to this, adding making the having a public pool LPs can add liquidity in an unbalanced way, causing the pool to become unbalanced. This change is in line with the original placeholder VC article, and how it was implemented by pickle.finance.

Smart Treasury Candidate v0.2

  • Tokens: IDLE, ETH,
  • Weights (Initial): 99/1 (IDLE/ETH) [Gradually adjusted after bootstrapping]
  • WeightsAfter3Months: 90/10 (IDLE/ETH)
  • SwapFee: 0.5% (pending snapshot vote completion)
  • UseWhiteList: True
  • WhiteList: FeeCollectionContract address
  • CanChangeSwapFee: True
  • CanChangeWeights: True
  • MinimumGradualUpdateDurationForSwapFee: 3 days
  • CanChangeTokens: True
  • AddTokenLockTime: 3 days
  • CanLimitBPTSupply: False
2 Likes

thumbs up to this. I like the pickle jar mindset and absolutely agree with the vision of a “buyback machine”

3 Likes

Minor update to spec to include pool creation step, as defined by spec.

  1. Deploy Smart Treasury bootstrapping smart contract (BSC)
  2. Deploy a new Configurable Rights Pool (Smart Treasury) with parameters as defined in spec.
  3. (Governance) Withdraw 130,000 IDLE from ecosystem fund (representing 1% of supply) and deposit to BSC
  4. (Governance) Withdraw all assets from FeeTreasury to BSC,
  5. Retrieve IDLE/ETH conversion rate using an oracle.
  6. Convert FeeTreasury assets to ETH in BSC up to value of 1300 IDLE.
  7. If ETH value != 1,300 IDLE (1% of 130,000), sell UP TO 1300 IDLE on the open market until ETH value is equivalent to 1% of IDLE deposit (as defined in step 4).
  8. Convert ETH to WETH (Balancer only supports WETH)
  9. Deposit IDLE & WETH into the pool, bootstrapping the smart treasury.
  10. Send BalancerPoolToken to FeeCollection Contract. (Token representing stake in pool).
  11. Set pool controller as an address governance controls. (calling setController() )
  12. Transfer any remaining funds (eg ETH, or outstanding USDC, DAI etc) back to FeeTreasury.

This can be summarised in a basic interface, (reference only)

interface BootstrapSmartTreasuryContract {
   function initialise () {} // Create smart treasury pool, using parameters from spec
   function swap() {} // Exchange fees + IDLE if required for ETH
   function bootstrap() {} // fund the smart treasury pool, and call begin updating weights
   function relinquish() {} // transfer ownership to governance. 
}

The following sequence diagram can be helpful to visualise the process

4 Likes

@Fernando where to post this on the Balancer discord?

If I’m not mistaken Uniswap uses WETH, so maybe the conversion ETH → WETH is not necessary. We can directly sell $IDLE for $WETH.

2 Likes

I have a naive question and it doesnt apply specifically to this design but to all smart contract based “smart” treasuries. If it’s fairly clear and predictable what the contract will do, wont that enable bad actors to exploit the behavior of the contract? If I know what you’re going to do, cant I front run you in some way?

2 Likes

This can be mitigated by whitelisting access to the contract, such that only certain accounts can deposit into the smart treasury, making it harder to front run.

The plan is to swap using uniswap, but the amount that would be swapped at a given time is quite low, minimising slippage. Also reducing the risk of front running. These liquidity pools are massive.

Once we have swapped all the fees into ether, they can be deposited into the smart treasury. Since we are the only liquidity providers, we are essentially trading with ourselves, and there is reduced impact from front running.

2 Likes