Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: cToken Wrapper and gas cost comparison #281

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .solcover.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module.exports = {
'Migrations.sol',
'mocks/',
'packages/',
'external/canonical-weth'
'external/canonical-weth',
'external/proxies/CTokenProxy.sol'
]
}
90 changes: 90 additions & 0 deletions contracts/external/proxies/CTokenProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* SPDX-License-Identifier: UNLICENSED
*/
pragma solidity =0.6.10;
pragma experimental ABIEncoderV2;

import {ReentrancyGuard} from "../../packages/oz/ReentrancyGuard.sol";
import {SafeERC20} from "../../packages/oz/SafeERC20.sol";
import {ERC20Interface} from "../../interfaces/ERC20Interface.sol";
import {Actions} from "../../libs/Actions.sol";
import {Controller} from "../../Controller.sol";
import {CTokenInterface} from "../../interfaces/CTokenInterface.sol";

/**
* @title CTokenProxy
* @author Opyn Team
* @dev Contract for wrapping cToken before minting options
*/
contract CTokenProxy is ReentrancyGuard {
using SafeERC20 for ERC20Interface;
using SafeERC20 for CTokenInterface;

Controller public controller;
address public marginPool;

constructor(address _controller, address _marginPool) public {
controller = Controller(_controller);
marginPool = _marginPool;
}

/**
* @notice execute a number of actions after minting some cTokens
* @dev a wrapper for the Controller operate function, to wrap uderlying to cToken before the excution.
* @param _actions array of actions arguments
* @param _underlying underlying asset
* @param _cToken the cToken to mint
* @param _amountUnderlying the amount of underlying to supply to Compound
*/
function operate(
Actions.ActionArgs[] memory _actions,
address _underlying,
address _cToken,
uint256 _amountUnderlying
) external payable nonReentrant {
ERC20Interface underlying = ERC20Interface(_underlying);
CTokenInterface cToken = CTokenInterface(_cToken);

// if depositing token: pull token from user
uint256 cTokenBalance = 0;
if (_amountUnderlying > 0) {
underlying.safeTransferFrom(msg.sender, address(this), _amountUnderlying);
// mint cToken
underlying.safeApprove(_cToken, _amountUnderlying);
cToken.mint(_amountUnderlying);
cTokenBalance = cToken.balanceOf(address(this));
uint256 allowance = cToken.allowance(address(this), marginPool);
if (allowance < cTokenBalance) {
cToken.safeApprove(marginPool, uint256(-1));
}
}

// verify sender
for (uint256 i = 0; i < _actions.length; i++) {
Actions.ActionArgs memory action = _actions[i];

// check that msg.sender is an owner or operator
if (action.owner != address(0)) {
require(
(msg.sender == action.owner) || (controller.isOperator(action.owner, msg.sender)),
"PayableProxyController: cannot execute action "
);
}

// overwrite the deposit amount by the exact amount minted
if (action.actionType == Actions.ActionType.DepositCollateral && action.amount == 0) {
_actions[i].amount = cTokenBalance;
}
}

controller.operate(_actions);

// if it's withdraw, may have some cToken left in this contract
uint256 cTokenBalanceAfter = cToken.balanceOf(address(this));
if (cTokenBalanceAfter > 0) {
require(cToken.redeem(cTokenBalanceAfter) == 0, "CTokenPricer: Redeem Failed");
uint256 underlyingBalance = underlying.balanceOf(address(this));
underlying.safeTransfer(msg.sender, underlyingBalance);
}
}
}
13 changes: 11 additions & 2 deletions contracts/interfaces/CTokenInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@
*/
pragma solidity 0.6.10;

import {ERC20Interface} from "./ERC20Interface.sol";

/**
* @dev Interface of Compound cToken
*/
interface CTokenInterface {
interface CTokenInterface is ERC20Interface {
/**
* @notice Calculates the exchange rate from the underlying to the CToken
* @return Calculated exchange rate scaled by 1e18
*/
function exchangeRateStored() external view returns (uint256);

function decimals() external view returns (uint256);
/**
* msg.sender: The account which shall supply the asset, and own the minted cTokens.
* @param _mintAmount: The amount of the asset to be supplied, in units of the underlying asset.
* RETURN: 0 on success, otherwise an Error code
*/
function mint(uint256 _mintAmount) external returns (uint256);

function redeem(uint256 redeemTokens) external returns (uint256);
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"test:unit": "truffle test ./test/unit-tests/*.ts",
"test:integration": "truffle test ./test/integration-tests/*.ts",
"test:testing-engine": "truffle test ./test/test-engine/*.ts",
"coverage": "truffle run coverage",
"coverage": "truffle run coverage --file \"test/unit-tests/*.test.ts\"",
"contract-size": "truffle run contract-size --checkMaxSize",
"generate:doc": "npm run generate:doc:control-flow & npm run generate:doc:uml",
"generate:doc:control-flow": "npm run doc:control-flow & npm run doc:control-flow:high-level & npm run doc:control-flow:controller & npm run doc:control-flow:pool & npm run doc:control-flow:oracle & npm run doc:control-flow:pricer & npm run doc:control-flow:otoken & npm run doc:control-flow:factory & npm run doc:control-flow:whitelist & npm run doc:control-flow:addressbook ",
Expand Down
Loading