Governance owned cUSD-USDC Liquidity
Introduction
As an interim solution to offer cUSD-USDC liquidity short term, before GigaMento (see GigaMento Forum Discussion), we suggest deploying a constant sum market maker (“CSMM”) StableSwap Pool via Mobius, in which the reserve will provide liquidity - namely 5M cUSD and 5M USDC. This allows a similar experience that GigaMento would offer, that is large cUSD<->USDC trades with minimal fees and slippage, but requires no new code to be deployed, making it viable as an immediate solution. It effectively leads to protocol-owned cUSD-USDC liquidity in Mobius that can be withdrawn and returned to the reserve via governance at any time. The differences between a StableSwap pool and GigaMento can be thought of in terms of where the liquidity comes from:
*depending on how the pool is utilized, governance might add more liquidity in the future via additional governance proposals until GigaMento comes into effect, see Rebalancing.
Deployment
StableSwap Pool
The StableSwap pool (see contract) will be deployed and managed by Mobius. They will use these parameters:
CGP Mechanics
When executed, the CGP should:
- Mint cUSD
- Deposit cUSD + USDC (Wormhole) to the pool
Minting cUSD to Governance
Currently only Validators, GrandaMento, and the Mento Exchange contracts can mint stable tokens. To get around this we propose temporarily (that is for one transaction, the CGP execution) deploying an altered version of the StableToken.sol contract, namely StableTokenMintableByOwner. The only difference is that it adds msg.sender == owner() as a passing condition in the mint and burn functions, which allows Governance to mint cUSD.
This new implementation would only be in effect in the transaction that executes the CGP. The StableToken proxy will switch to this new implementation and then switch back, making the minting part of the CGP look like this:
Depositing cUSD and USDC to the pool
In order to deposit the tokens in the pool, Governance needs to hold the bridged USDC when the governance proposal is executed. This will be covered in more detail in the Deployment Timing section.
For the addLiquidity function call on the pool to succeed, the pool needs the approval to transfer cUSD and USDC from Governance to itself, thus requiring two ERC20 approve calls beforehand - making the process look like this:
For the more technically savvy, here’s an example of a transaction simulated on top of a mainnet fork which adds cUSD and USDC via a CGP to the existing cUSD-USDC Mobius pool.
Deployment Timing
Phase I - Prepare
This phase consists of:
- Deploy the StableTokenMintableByOwner implementation (responsibility of Mento team)
- Deploying the GnosisSafe multisig (responsibility of Mento team)
- Deploying the StableSwap Pool (responsibility of Mobius team)
- Submitting the CGP (responsibility of Mento team)
StableTokenMintableByOwner
This contract is a slight modification of the StableToken contract which allows the owner to mint/burn tokens. During the CGP execution, the StableToken proxy will temporarily point to this implementation (see Minting cUSD to Governance), so it needs to be deployed before the CGP is submitted.
Multisig
This setup involves three types of tokens:
- cUSD
- USDC (Wormhole)
- LPTokens from the StableSwap pool.
Our aim is for Governance to hold these tokens for most steps throughout the process, but due to limitations in the setup, a 4/6 Multisig (GnosisSafe) on Ethereum will be used as an intermediary. It will have signers from Mento and cLabs:
The Multisig is used to orchestrate the bridging of USDC from the reserve in Phase II via Wormhole, more details in the Phase II - Bridging section.
Submitting the CGP
Because the CGP needs to execute the process defined in CGP Mechanics atomically and there’s a time window between when the transactions are crafted and when the transaction executes and great care needs to be taken when picking values for the variables. This will be covered in the CGP Variables section.
Phase II - Bridge
This phase begins after the CGP is in the Execution phase, which means that the proposal has passed and USDC can now be bridged from the Reserve Custodian on Ethereum to the Governance contract on Celo, via the Wormhole/Portal bridge.
In an ideal world, this could be done in a fully permissionless manner, the Custodian bridging directly to the Governance contract. But the Custodian only supports simple transfers of funds, not arbitrary transactions therefore an intermediate Multisig on Ethereum is necessary. It will receive a transfer from the Reserve Custodian and bridge the tokens to the Governance contract.
The process to bridge will look like the following:
- Transfer from USDC from Custodian to the 4/6 GnosisSafe Multisig explained above
- Propose USD.approve(BRIDGE_ADDRESS, USDC_AMOUNT) from the GnosisSafe
- MultiSig owners confirm and execute
- Propose depositing USDC in the bridge from GnosisSafe
- MultiSig owners confirm and execute
- Redeem bridged USDC on Celo, which transfers them to Governance
The Portal UI has a limitation: you can’t specify the recipient when bridging tokens from Ethereum to Celo, it must be the same address as the sender, but they provide a Javascript SDK that can be used to orchestrate the transfer. These two scripts will be used for steps (2) and (4) above here:
If the multisig signers are available it should take no more than 10 minutes to bridge the USDC to Governance.
Phase III - Execute
At this point USDC should be bridged and sitting with Governance, therefore the CGP can now execute the steps outlined above:
- Mint cUSD to itself
- Deposit cUSD and USDC into the Stableswap Pool
Because this is done by the Governance contract the LP tokens and thus custody of the liquidity will remain with Governance.
CGP Variables
Make governance able to mint:
{stableTokenProxy}._setImplementation({stableTokenMintableByOwner})
Mint cUSD to Governance:
{stableTokenProxy}.mint({governanceProxy}, {cUSD_amount})
Make governance unable to mint:
{stableTokenProxy}._setImplementation({stableToken})
Approve cUSD transfers:
{stableTokenProxy}.approve({pool}, {cUSD_amount})
Approve USDC transfers:
{USDC_wormhole}.approve({pool}, {USDC_amount})
Deposit cUSD and USDC:
{pool}.addLiquidity**({amounts}**, {minLPTokensToMint}, {deadline})
Because the CGP is submitted prior to the bridging of USDC, the USDC_amount needs to be well defined beforehand taking into account all fees involved in bridging the assets, so that Governance’s balance ends up greater or equal to {USDC_amount} otherwise the execution will fail.
The other point of failure is the deadline but it can set that to a timestamp that’s after the CGP expiration.
Rebalancing
The pool will start with 5M each of USDC and cUSD, but depending on the volume of inflows and outflows the pool might end up unbalanced on either side which will require rebalancing. There are four possible operations to consider:
- Adding cUSD
- Adding USDC
- Removing cUSD
- Removing USDC
Adding cUSD
If the pool gets to be mostly USDC, governance would potentially want to top up the cUSD bucket. This would require another CGP without the bridging phase which only adds cUSD liquidity to the pool. The mechanics are similar, but the Bridge phase can be skipped and there is no need to approve USDC transfers in the CGP.
Adding USDC
If the pool gets to be mostly cUSD, new CGPs can be submitted to add additional USDC. This new CGP would be similar to the Deployment one, but without the cUSD minting part:
Removing cUSD/USDC
Removing cUSD/USDC from the pool while it’s live can get tricky because the transactions to burn cUSD/transfer USDC from governance need to know how much liquidity is removed before it’s executed. For example say the pool gets to be 3M cUSD + 7M USDC, and the CGP is crafted to withdraw 5M USDC and transfer it out, but by the time the CGP executes the pool is 5.5M cUSD + 4.5M USDC, the CGP would fail.
There are three options that can be revisited depending on the evolution of the pool:
- Issue CGPs with small amounts betting that pool wouldn’t rebalance dramatically
- Do a two-tier process, one CGP to withdraw liquidity and one to burn/transfer once the exact amount is known.
Rely only on Unwinding, given that this is a temporary measure
Unwinding
When the GigaMento MVP gets deployed, governance will need to unwind this pool. That entails:
- Pausing the pool so no more swaps are possible (Mobius team will have to do it)
- Remove liquidity (cUSD + USDC) from the pool
- Burn cUSD
- Transfer USDC to the GigaMento Reserve contract (or back through the bridge to the Reserve Custodian)
The pool needs to be paused before submitting the CGP because then all liquidity can be withdrawn and handled easily in a single CGP.
If for whatever reason pausing the pool is problematic, two CGPs can be issued. The first is just to remove liquidity, and the second to burn/transfer with the known balances that result from the liquidity removal.
Risks
Bridge risks
Bridging 5M reserve USDC to Celo via Portal/Wormhole exposes the reserve to a 5M USD bridge risk. In the worst case, the 5M native USDC of the reserve could get lost due to a bridge hack, rendering the USDCet on Celo worthless. Since the suggested pool enforces basically a 1:1 exchange, this would likely lead to all cUSD getting drained from the pool such that the total loss for the reserve could reach 5M native USDC plus an additional debt of 5M cUSD.
Contract risk
The proposed solution relies on a Mobius liquidity pool. In case of a Mobius failure or attack the deposited amounts of cUSD and USDCet are at risk.
cUSD/USDC Price Risk
The setup of the liquidity pool is based on the assumption that the cUSD/USDC price is 1, to be precise that the cUSD/USDCet price is 1. Two market scenarios can have unwanted consequences:
- USDC/USD price deviation from 1,
- USDCet/USD price deviation from 1.
The cUSD/USDCet pool will offer cUSD (and USDCet) at an incorrect price that is either higher (cUSD/USDCet < 1) or lower (cUSD/USDCet > 1) than the market price. The availability of expensive/cheap liquidity can have a price impact that is undesirable for cUSD as it is purely caused by a USDCet price deviation.