C.R.E.A.M. Swap v2
Since C.R.E.A.M. Swap launched, we have been gathering feedback and planning through improving the product given the help of our community.
We’ve launched C.R.E.A.M. Swap v2, a new version of C.R.E.A.M. Swap that is more security and robust. Smart contract diff is provided down below, along with some thoughts on our future plan.
Code on Etherscan
What is Changed in v2
Fix the Gulp Issue
In short, if the gulp
function were called, pool reserves would be given back to Liquidity Providers.
// Absorb any tokens that have been sent to this contract into the pool
function gulp(address token)
external
_logs_
_lock_
{
require(_records[token].bound, "ERR_NOT_BOUND");
_records[token].balance = IERC20(token).balanceOf(address(this));
}
The gulp
function was to absorb any tokens that have been sent directly to the contract. These balances won’t be counted since they are not increased by adding liquidity. Anyone who found this situation could call gulp
to ‘reset’ the balance. In v1, we didn’t find it necessary to modify this function when it comes to reserves, but then the problem emerged.
In C.R.E.A.M. Swap v1, a pool collects reserves every time users conduct a swap or a single asset deposit / withdraw. However, we didn’t collect reserves immediately, but kept reserves in the pool as part of the liquidity. If anyone calls gulp
function, the reserves will also be absorbed into balance, and we couldn’t retrieve them anymore.
All funds are safe in all v1 pools, but reserves didn’t worked as intended. This issue has been fixed in v2.
Fix reserves related
In v1, the swapExactAmountIn
function collects reserves from output token, which is no longer the case in v2. swapExactAmountIn
now collects reserves from input token swap fee.
Add Protection to Collect
collect(address)
in BFactory.sol was used to collect the exit fee of a given pool. When collect
is called and the input address is not a pool in BFactory but a malicious contract, the maclicious contract will be able to call drainTotalReserves
through delegatecall. I.e. a BFactory admin can drain a pool’s reserves by calling a seemingly unrelated collect
. It’s not a big issue since only a BFactory admin can call collect
; however, we still fixed this problem for the sake of robustness.
Get Error Message Back
In v1, we removed all the error messages in the contract to reduce bytecode size. It allowed us to implement new functions. However, we found it difficult for users to troubleshoot when interacting with the contract. Therefore, we’ve decided to add some error messages to public functions in BPool.sol, BNum.sol, and BMath.sol.
Diff Checker
Here is the full file diffs of Balancer and C.R.E.A.M. Swap v2. It is mainly copied from C.R.E.A.M. Swap v1 smart contract diff with some highlights of the change in v2.
- BConst.sol: https://www.diffchecker.com/uBE7XGKe
* Added default reserves ratio to be 20%, which means 20% of swap fees are kept by the pool as reserves. - BFactory.sol: https://www.diffchecker.com/5WRaEKz0
* Added a switch_allowNonAdminPool
to [dis]allow non-admin to open new pools (admin is the address who deployed the factory contract).
* Reserves address: the address where reserves go.
* Added_reservesAddress
and its getter and setter. Only admin can change reserves address.
* AddedcollectTokenReserves(BPool pool)
, which allows admin to move all reserve tokens in the pool to the reserve address.
* (v2) Added protection to collect which prevents potential security concerns. - BMath.sol: https://www.diffchecker.com/8j3H7cb1
* Calculate reserves function
* AddedcalcReserves
which calculates the amount of reserves.
* (v2) ChangedcalcReserves
tocalcReservesFromFee
which calculates the amount of reserves.
* ChangedcalcPoolOutGivenSingleIn
andcalcPoolInGivenSingleOut
to calculate reserves, required byjoinswapExternAmountIn
andexitswapExternAmountOut
respectively.
* (v2) ChangedcalcOutGivenIn
andcalcInGivenOut
to calculate reserves, required byswapExactAmountIn
andswapExactAmountOut
respectively. - BNum.sol: https://www.diffchecker.com/4fqaXJq0
- BPool.sol: https://www.diffchecker.com/P4aCA3nc
* AddedtotalReserves
to record the reserves for each token in the pool.
* Added seize to move the unbound tokens to the pool controller. This is useful when the rewards are distributed to the pool (e.g. COMP or CREAM).
* ChangedswapExactAmountIn
,swapExactAmountOut
,joinswapExternAmountIn
,joinswapPoolAmountOut
,exitswapPoolAmountIn
, andexitswapExternAmountOut
.
* To keep track of the reserves from the swap fee.
* AddeddrainTotalReserves
to allow BFactory to move alltotalReserves
from this pool to the factory. This is required byBFactory.collectTokenReserves
.
* (v2) Fixedgulp
function to include reserves.
* (v2) Added reserves to events.
What’s Next?
We are considering making C.R.E.A.M. Swap open for anyone to create pool, including Swap Pool and Reward Pool. Permissonlessly creating a pool and adding a Reward Pool allows for projects to create their own liquidity mining program on C.R.E.A.M. Swap. We believe this additional feature can lead to more swaps in our protocol, thus more usage and reserves.
Got feedback? Hit us up through the below channels.
C.R.E.A.M. DAO
Crypto Rules Everything Around Me, C.R.E.A.M.
Join us on Discord, follow us on Twitter, or visit us at cream.finance.