C.R.E.A.M. Finance
Published in

C.R.E.A.M. Finance

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)
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.
    * Added collectTokenReserves(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
    * Added calcReserves which calculates the amount of reserves.
    * (v2) Changed calcReserves to calcReservesFromFee which calculates the amount of reserves.
    * Changed calcPoolOutGivenSingleIn and calcPoolInGivenSingleOut to calculate reserves, required by joinswapExternAmountIn and exitswapExternAmountOut respectively.
    * (v2) Changed calcOutGivenIn and calcInGivenOut to calculate reserves, required by swapExactAmountIn and swapExactAmountOut respectively.
  • BNum.sol: https://www.diffchecker.com/4fqaXJq0
  • BPool.sol: https://www.diffchecker.com/P4aCA3nc
    * Added totalReserves 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).
    * Changed swapExactAmountIn, swapExactAmountOut, joinswapExternAmountIn, joinswapPoolAmountOut, exitswapPoolAmountIn, and exitswapExternAmountOut.
    * To keep track of the reserves from the swap fee.
    * Added drainTotalReserves to allow BFactory to move all totalReserves from this pool to the factory. This is required by BFactory.collectTokenReserves.
    * (v2) Fixed gulp 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.

Crypto Rules Everything Around Me, C.R.E.A.M.

Join us on Discord, follow us on Twitter, or visit us at cream.finance.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store