Lending can be seen as leverage. For example, I predict ETH price is going up in the near future. I supply 10 ETH as collateral to Compound and borrow 300 DAI. I would buy more ETH using this 300 DAI I just borrowed, so that when ETH price indeed goes up my asset total value goes up even more. I will buy 300 DAI with my ETH balance and repay the loan, and I still have more ETH at hand because ETH price went up. These extra ETH is my profit. This is called "long ETH".
"Short ETH" is just the other way around. If we expect ETH price to drop, we can deposit DAI as collateral and borrow ETH. Then we sell these borrowed ETH and buy DAI. When the price of ETH does go down, we sell DAI and buy ETH, then pay back the loan. We will be left some DAI and that is our profit.
We will be writing functions to long ETH. It takes the following steps:
supply ETH
borrow stable coin (DAI, USDC)
buy ETH on Uniswap
When the price of ETH goes up:
sell ETH on Uniswap
repay borrowed stable coin
Code:
Setup
Nothing special here:
CEth public cEth;
CErc20 public cTokenBorrow;
IERC20 public tokenBorrow;
uint public decimals;
Comptroller public comptroller = Comptroller(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);
PriceFeed public priceFeed = PriceFeed(0x922018674c12a7F0D394ebEEf9B58F186CdE13c1);
IUniswapV2Router private constant UNI = IUniswapV2Router(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
IERC20 private constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
constructor(
address _cEth,
address _cTokenBorrow,
address _tokenBorrow,
uint _decimals
) {
cEth = CEth(_cEth);
cTokenBorrow = CErc20(_cTokenBorrow);
tokenBorrow = IERC20(_tokenBorrow);
decimals = _decimals;
// enter market to enable borrow
// we are going to supply ETH (cEth)
address[] memory cTokens = new address[](1);
cTokens[0] = address(cEth);
uint[] memory errors = comptroller.enterMarkets(cTokens);
require(errors[0] == 0, "Comptroller.enterMarkets failed.");
}
receive() external payable {}
Step 1: supply ETH
We deposit ETH into Compound as collateral, which is equivalent to minting cEth:
function supply() external payable {
cEth.mint{value: msg.value}();
}
Step 2 and 3: borrow stable coin (DAI, USDC) and buy ETH on Uniswap
Compute how much DAI we can borrow:
function getMaxBorrow() external view returns (uint) {
// This is the amount we can borrow in USD
(uint error, uint liquidity, uint shortfall) = comptroller.getAccountLiquidity(
address(this)
);
require(error == 0, "error");
require(shortfall == 0, "shortfall > 0");
require(liquidity > 0, "liquidity = 0");
// Price is also in USD
uint price = priceFeed.getUnderlyingPrice(address(cTokenBorrow));
uint maxBorrow = (liquidity * (10**decimals)) / price;
return maxBorrow;
}
Borrow DAI and swap DAI for WETH in Uniswap:
function long(uint _borrowAmount) external {
// borrow DAI
require(cTokenBorrow.borrow(_borrowAmount) == 0, "borrow failed");
// buy WETH on Uniswap
uint bal = tokenBorrow.balanceOf(address(this));
tokenBorrow.approve(address(UNI), bal);
address[] memory path = new address[](2);
path[0] = address(tokenBorrow);
path[1] = address(WETH);
UNI.swapExactTokensForETH(bal, 1, path, address(this), block.timestamp);
}
Step 4 and 5: sell ETH on Uniswap and repay borrowed stable coin
function repay() external {
// sell ETH on Uniswap
address[] memory path = new address[](2);
path[0] = address(WETH);
path[1] = address(tokenBorrow);
UNI.swapExactETHForTokens{value: address(this).balance}(
1,
path,
address(this),
block.timestamp
);
// repay borrowed tokens
uint borrowed = cTokenBorrow.borrowBalanceCurrent(address(this));
tokenBorrow.approve(address(cTokenBorrow), borrowed);
require(cTokenBorrow.repayBorrow(borrowed) == 0, "repay failed");
// Withdraw collateral ETH
uint supplied = cEth.balanceOfUnderlying(address(this));
require(cEth.redeemUnderlying(supplied) == 0, "redeem failed");
// supplied ETH + supplied interest + profit (in token borrow)
}