add logic for compound import except flashloan related functions

This commit is contained in:
Chinmay Chougaonkar 2022-02-27 09:32:51 +05:30
parent ffe0359ca8
commit 5516b27d7a
5 changed files with 941 additions and 86 deletions

View File

@ -0,0 +1,5 @@
pragma solidity ^0.7.6;
contract Events {
}

View File

@ -0,0 +1,41 @@
pragma solidity ^0.7.6;
import { DSMath } from "../../common/math.sol";
import { Basic } from "../../common/basic.sol";
import { ComptrollerInterface, CompoundMappingInterface, CETHInterface } from "./interface.sol";
abstract contract Helpers is DSMath, Basic {
/**
* @dev Compound CEth
*/
CETHInterface internal constant cEth = CETHInterface(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5);
/**
* @dev Compound Comptroller
*/
ComptrollerInterface internal constant troller = ComptrollerInterface(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);
/**
* @dev Compound Mapping
*/
CompoundMappingInterface internal constant compMapping = CompoundMappingInterface(0xe7a85d0adDB972A4f0A4e57B698B37f171519e88);
/**
* @dev enter compound market
*/
function enterMarket(address cToken) internal {
address[] memory markets = troller.getAssetsIn(address(this));
bool isEntered = false;
for (uint i = 0; i < markets.length; i++) {
if (markets[i] == cToken) {
isEntered = true;
}
}
if (!isEntered) {
address[] memory toEnter = new address[](1);
toEnter[0] = cToken;
troller.enterMarkets(toEnter);
}
}
}

View File

@ -0,0 +1,46 @@
pragma solidity ^0.7.6;
interface TokenInterface {
function balanceOf(address) external view returns (uint);
function allowance(address, address) external view returns (uint);
function approve(address, uint) external;
function transfer(address, uint) external returns (bool);
function transferFrom(address, address, uint) external returns (bool);
}
interface CTokenInterface {
function mint(uint mintAmount) external returns (uint);
function redeem(uint redeemTokens) external returns (uint);
function borrow(uint borrowAmount) external returns (uint);
function repayBorrow(uint repayAmount) external returns (uint);
function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); // For ERC20
function liquidateBorrow(address borrower, uint repayAmount, address cTokenCollateral) external returns (uint);
function borrowBalanceCurrent(address account) external returns (uint);
function redeemUnderlying(uint redeemAmount) external returns (uint);
function exchangeRateCurrent() external returns (uint);
function balanceOf(address owner) external view returns (uint256 balance);
function transferFrom(address, address, uint) external returns (bool);
function allowance(address, address) external view returns (uint);
}
interface CETHInterface {
function mint() external payable;
function repayBorrow() external payable;
function repayBorrowBehalf(address borrower) external payable;
function liquidateBorrow(address borrower, address cTokenCollateral) external payable;
}
interface ComptrollerInterface {
function enterMarkets(address[] calldata cTokens) external returns (uint[] memory);
function exitMarket(address cTokenAddress) external returns (uint);
function getAssetsIn(address account) external view returns (address[] memory);
function getAccountLiquidity(address account) external view returns (uint, uint, uint);
}
interface CompoundMappingInterface {
function cTokenMapping(string calldata tokenId) external view returns (address);
function getMapping(string calldata tokenId) external view returns (address, address);
}

View File

@ -0,0 +1,250 @@
pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2;
import { TokenInterface, AccountInterface } from "../../common/interfaces.sol";
import { CTokenInterface } from "./interface.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
// 1. Get info for all the assets the user has supplied as collateral and the assets he borrowed.
// 2. Take the flash loan for all the borrowed assets.
// 3. Using this flash loan, pay back the user's debt in the EOA account.
// 4. After paying the debt, transfer the user's tokens from EOA to DSA.
// 5. Then borrow debt of same tokens but include flash loan fee in it.
// 6. Payback the flash loan for all the tokens.
// fill logics in contract functions
contract FlashLoanHelper is Helpers, Events {
function _flashLoan(
address[] memory _tokens,
uint256[] memory _amts
) internal {
// fill in logic for flash loans
}
function _repayFlashLoan(
address[] memory _tokens,
uint256[] memory _amts
) internal {
// fill in logic for flash loan repayment
}
}
contract CompoundResolver is Helpers, Events {
function _repayUserDebt(
address _userAccount,
CTokenInterface[] memory _cTokenContracts,
uint[] memory _borrowAmts
) internal {
for(uint i = 0; i < _cTokenContracts.length; i++){
if(_borrowAmts[i] > 0){
if(address(_cTokenContracts[i]) == address(cEth)){
cEth.repayBorrowBehalf{value: _borrowAmts[i]}(_userAccount);
}
else{
require(_cTokenContracts[i].repayBorrowBehalf(_userAccount, _borrowAmts[i]) == 0, "repayOnBehalf-failed");
}
}
}
}
function _transferTokensToDsa(
address _userAccount,
CTokenInterface[] memory _cTokenContracts,
uint[] memory _amts
) internal {
for(uint i = 0; i < _cTokenContracts.length; i++) {
if(_amts[i] > 0) {
require(_cTokenContracts[i].transferFrom(_userAccount, address(this), _amts[i]), "ctoken-transfer-failed-allowance?");
}
}
}
function _borrowDebtPosition(
CTokenInterface[] memory _ctokenContracts,
uint[] memory _amts
) internal {
for (uint i = 0; i < _ctokenContracts.length; i++) {
if (_amts[i] > 0) {
// add _amts flash loan fees to _amts[i]
require(_ctokenContracts[i].borrow(_amts[i]) == 0, "borrow-failed-collateral?");
}
}
}
}
contract CompoundHelpers is CompoundResolver {
struct ImportData {
address[] cTokens; // is the list of all tokens the user has interacted with (supply/borrow) -> used to enter markets
uint[] borrowAmts;
uint[] supplyAmts;
address[] borrowTokens;
address[] supplyTokens;
CTokenInterface[] borrowCtokens;
CTokenInterface[] supplyCtokens;
address[] supplyCtokensAddr;
address[] borrowCtokensAddr;
}
struct ImportInputData {
address userAccount;
string[] supplyIds;
string[] borrowIds;
}
function getBorrowAmounts (
ImportInputData memory _importInputData,
ImportData memory data
) internal returns(ImportData memory) {
if (_importInputData.borrowIds.length > 0) {
// initialize arrays for borrow data
data.borrowTokens = new address[](_importInputData.borrowIds.length);
data.borrowCtokens = new CTokenInterface[](_importInputData.borrowIds.length);
data.borrowCtokensAddr = new address[](_importInputData.borrowIds.length);
data.borrowAmts = new uint[](_importInputData.borrowIds.length);
// check for repeated tokens
for (uint i = 0; i < _importInputData.borrowIds.length; i++) {
bytes32 i_hash = keccak256(abi.encode(_importInputData.borrowIds[i]));
for (uint j = i + 1; j < _importInputData.borrowIds.length; j++) {
bytes32 j_hash = keccak256(abi.encode(_importInputData.borrowIds[j]));
require(i_hash != j_hash, "token-repeated");
}
}
// populate the arrays with borrow tokens, cToken addresses and instances, and borrow amounts
for (uint i = 0; i < _importInputData.borrowIds.length; i++) {
(address _token, address _cToken) = compMapping.getMapping(_importInputData.borrowIds[i]);
require(_token != address(0) && _cToken != address(0), "ctoken mapping not found");
data.cTokens[i] = _cToken;
data.borrowTokens[i] = _token;
data.borrowCtokens[i] = CTokenInterface(_cToken);
data.borrowCtokensAddr[i] = _cToken;
data.borrowAmts[i] = data.borrowCtokens[i].borrowBalanceCurrent(_importInputData.userAccount);
// give the resp. cToken address approval to spend tokens
if (_token != ethAddr && data.borrowAmts[i] > 0) {
// will be required when repaying the borrow amount on behalf of the user
TokenInterface(_token).approve(_cToken, data.borrowAmts[i]);
}
}
}
return data;
}
function getSupplyAmounts (
ImportInputData memory _importInputData,
ImportData memory data
) internal view returns(ImportData memory) {
// initialize arrays for supply data
data.supplyTokens = new address[](_importInputData.supplyIds.length);
data.supplyCtokens = new CTokenInterface[](_importInputData.supplyIds.length);
data.supplyCtokensAddr = new address[](_importInputData.supplyIds.length);
data.supplyAmts = new uint[](_importInputData.supplyIds.length);
// check for repeated tokens
for (uint i = 0; i < _importInputData.supplyIds.length; i++) {
bytes32 i_hash = keccak256(abi.encode(_importInputData.supplyIds[i]));
for (uint j = i + 1; j < _importInputData.supplyIds.length; j++) {
bytes32 j_hash = keccak256(abi.encode(_importInputData.supplyIds[j]));
require(i_hash != j_hash, "token-repeated");
}
}
// populate arrays with supply data (supply tokens address, cToken addresses, cToken instances and supply amounts)
for (uint i = 0; i < _importInputData.supplyIds.length; i++) {
(address _token, address _cToken) = compMapping.getMapping(_importInputData.supplyIds[i]);
require(_token != address(0) && _cToken != address(0), "ctoken mapping not found");
uint _supplyIndex = add(i, _importInputData.borrowIds.length);
data.cTokens[_supplyIndex] = _cToken;
data.supplyTokens[i] = _token;
data.supplyCtokens[i] = CTokenInterface(_cToken);
data.supplyCtokensAddr[i] = (_cToken);
data.supplyAmts[i] = data.supplyCtokens[i].balanceOf(_importInputData.userAccount);
}
return data;
}
}
contract CompoundImportResolver is CompoundHelpers, FlashLoanHelper {
// get info for all the assets the user has supplied as collateral and the assets borrowed
function _importCompound(
ImportInputData memory importInputData
) internal returns (string memory _eventName, bytes memory _eventParam) {
require(AccountInterface(address(this)).isAuth(importInputData.userAccount), "user-account-not-auth");
require(importInputData.supplyIds.length > 0, "0-length-not-allowed");
ImportData memory data;
uint _length = add(importInputData.supplyIds.length, importInputData.borrowIds.length);
data.cTokens = new address[](_length);
data = getBorrowAmounts(importInputData, data);
data = getSupplyAmounts(importInputData, data);
for(uint i = 0; i < data.cTokens.length; i++){
enterMarket(data.cTokens[i]);
}
// take flash loan for all the borrowed assets
// use the addresses of the borrowed tokens and their amounts to get the same flash loans
_flashLoan(data.borrowTokens, data.borrowAmts);
// pay back user's debt using flash loan funds
_repayUserDebt(importInputData.userAccount, data.borrowCtokens, data.borrowAmts);
// transfer user's tokens to DSA
_transferTokensToDsa(importInputData.userAccount, data.supplyCtokens, data.supplyAmts);
// borrow the earlier position from Compound with flash loan fee added
_borrowDebtPosition(data.borrowCtokens, data.borrowAmts);
// payback flash loan
_repayFlashLoan(data.borrowTokens, data.borrowAmts); // update borrowAmounts with flash loan fee
_eventName = "LogCompoundImport(address,address[],string[],string[],uint256[],uint256[])";
_eventParam = abi.encode(
importInputData.userAccount,
data.cTokens,
importInputData.supplyIds,
importInputData.borrowIds,
data.supplyAmts,
data.borrowAmts
);
}
function importCompound(
address userAccount,
string[] memory supplyIds,
string[] memory borrowIds
) external payable returns (string memory _eventName, bytes memory _eventParam) {
ImportInputData memory inputData = ImportInputData({
userAccount: userAccount,
supplyIds: supplyIds,
borrowIds: borrowIds
});
(_eventName, _eventParam) = _importCompound(inputData);
}
function migrateCompound(
string[] memory supplyIds,
string[] memory borrowIds
) external payable returns (string memory _eventName, bytes memory _eventParam) {
ImportInputData memory inputData = ImportInputData({
userAccount: msg.sender,
supplyIds: supplyIds,
borrowIds: borrowIds
});
(_eventName, _eventParam) = _importCompound(inputData);
}
}

685
yarn.lock

File diff suppressed because it is too large Load Diff