removed bugs and fixed lint

This commit is contained in:
pradyuman-verma 2022-03-23 13:41:24 +05:30
parent 98d9c158fc
commit 5ff4637b91
No known key found for this signature in database
GPG Key ID: E36FD6BC8923221F
4 changed files with 367 additions and 268 deletions

View File

@ -1,13 +1,14 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6; pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
contract Events { contract Events {
event LogCompoundImport( event LogCompoundImport(
address indexed user, address indexed user,
address[] ctokens, address[] ctokens,
string[] supplyIds, string[] supplyIds,
string[] borrowIds, string[] borrowIds,
uint256[] supplyAmts, uint256[] supplyAmts,
uint256[] borrowAmts uint256[] borrowAmts
); );
} }

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6; pragma solidity ^0.7.6;
import { DSMath } from "../../common/math.sol"; import { DSMath } from "../../common/math.sol";
@ -6,196 +7,216 @@ import { TokenInterface, AccountInterface } from "../../common/interfaces.sol";
import { ComptrollerInterface, CompoundMappingInterface, CETHInterface, CTokenInterface } from "./interface.sol"; import { ComptrollerInterface, CompoundMappingInterface, CETHInterface, CTokenInterface } from "./interface.sol";
abstract contract Helpers is DSMath, Basic { abstract contract Helpers is DSMath, Basic {
/**
* @dev Compound CEth
*/
CETHInterface internal constant cEth =
CETHInterface(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5);
/** /**
* @dev Compound CEth * @dev Compound Comptroller
*/ */
CETHInterface internal constant cEth = CETHInterface(0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5); ComptrollerInterface internal constant troller =
ComptrollerInterface(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B);
/** /**
* @dev Compound Comptroller * @dev Compound Mapping
*/ */
ComptrollerInterface internal constant troller = ComptrollerInterface(0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B); CompoundMappingInterface internal constant compMapping =
CompoundMappingInterface(0xe7a85d0adDB972A4f0A4e57B698B37f171519e88);
/** struct ImportData {
* @dev Compound Mapping address[] cTokens; // is the list of all tokens the user has interacted with (supply/borrow) -> used to enter markets
*/ uint256[] borrowAmts;
CompoundMappingInterface internal constant compMapping = CompoundMappingInterface(0xe7a85d0adDB972A4f0A4e57B698B37f171519e88); uint256[] supplyAmts;
address[] borrowTokens;
address[] supplyTokens;
CTokenInterface[] borrowCtokens;
CTokenInterface[] supplyCtokens;
address[] supplyCtokensAddr;
address[] borrowCtokensAddr;
}
struct ImportData { struct ImportInputData {
address[] cTokens; // is the list of all tokens the user has interacted with (supply/borrow) -> used to enter markets address userAccount;
uint[] borrowAmts; string[] supplyIds;
uint[] supplyAmts; string[] borrowIds;
address[] borrowTokens; }
address[] supplyTokens;
CTokenInterface[] borrowCtokens;
CTokenInterface[] supplyCtokens;
address[] supplyCtokensAddr;
address[] borrowCtokensAddr;
}
struct ImportInputData { /**
address userAccount; * @dev enter compound market
string[] supplyIds; * @param _cotkens array of ctoken addresses to enter compound market
string[] borrowIds; */
} function _enterMarkets(address[] memory _cotkens) internal {
troller.enterMarkets(_cotkens);
/** }
* @dev enter compound market
* @param _cotkens array of ctoken addresses to enter compound market
*/
function _enterMarkets(address[] memory _cotkens) internal {
troller.enterMarkets(_cotkens);
}
} }
contract CompoundHelper is Helpers { contract CompoundHelper is Helpers {
/**
* @notice fetch the borrow details of the user
* @dev approve the cToken to spend (borrowed amount of) tokens to allow for repaying later
* @param _importInputData the struct containing borrowIds of the users borrowed tokens
* @param data struct used to store the final data on which the CompoundHelper contract functions operate
* @return ImportData the final value of param data
*/
function getBorrowAmounts(
ImportInputData memory _importInputData,
ImportData memory data
) internal returns (ImportData memory) {
if (_importInputData.borrowIds.length > 0) {
// initialize arrays for borrow data
uint256 _length = _importInputData.borrowIds.length;
data.borrowTokens = new address[](_length);
data.borrowCtokens = new CTokenInterface[](_length);
data.borrowCtokensAddr = new address[](_length);
data.borrowAmts = new uint256[](_length);
/** // populate the arrays with borrow tokens, cToken addresses and instances, and borrow amounts
* @notice fetch the borrow details of the user for (uint256 i; i < _length; i++) {
* @dev approve the cToken to spend (borrowed amount of) tokens to allow for repaying later (address _token, address _cToken) = compMapping.getMapping(
* @param _importInputData the struct containing borrowIds of the users borrowed tokens _importInputData.borrowIds[i]
* @param data struct used to store the final data on which the CompoundHelper contract functions operate );
* @return ImportData the final value of param data
*/
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);
// populate the arrays with borrow tokens, cToken addresses and instances, and borrow amounts require(
for (uint i = 0; i < _importInputData.borrowIds.length; i++) { _token != address(0) && _cToken != address(0),
(address _token, address _cToken) = compMapping.getMapping(_importInputData.borrowIds[i]); "ctoken mapping not found"
);
require(_token != address(0) && _cToken != address(0), "ctoken mapping not found"); data.cTokens[i] = _cToken;
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
);
data.borrowTokens[i] = _token; // give the resp. cToken address approval to spend tokens
data.borrowCtokens[i] = CTokenInterface(_cToken); if (_token != ethAddr && data.borrowAmts[i] > 0) {
data.borrowCtokensAddr[i] = _cToken; // will be required when repaying the borrow amount on behalf of the user
data.borrowAmts[i] = data.borrowCtokens[i].borrowBalanceCurrent(_importInputData.userAccount); TokenInterface(_token).approve(_cToken, data.borrowAmts[i]);
}
}
}
return data;
}
// give the resp. cToken address approval to spend tokens /**
if (_token != ethAddr && data.borrowAmts[i] > 0) { * @notice fetch the supply details of the user
// will be required when repaying the borrow amount on behalf of the user * @dev only reads data from blockchain hence view
TokenInterface(_token).approve(_cToken, data.borrowAmts[i]); * @param _importInputData the struct containing supplyIds of the users supplied tokens
} * @param data struct used to store the final data on which the CompoundHelper contract functions operate
} * @return ImportData the final value of param data
} */
return data; function getSupplyAmounts(
} ImportInputData memory _importInputData,
ImportData memory data
) internal view returns (ImportData memory) {
// initialize arrays for supply data
uint256 _length = _importInputData.supplyIds.length;
data.supplyTokens = new address[](_length);
data.supplyCtokens = new CTokenInterface[](_length);
data.supplyCtokensAddr = new address[](_length);
data.supplyAmts = new uint256[](_length);
/** // populate arrays with supply data (supply tokens address, cToken addresses, cToken instances and supply amounts)
* @notice fetch the supply details of the user for (uint256 i; i < _length; i++) {
* @dev only reads data from blockchain hence view (address _token, address _cToken) = compMapping.getMapping(
* @param _importInputData the struct containing supplyIds of the users supplied tokens _importInputData.supplyIds[i]
* @param data struct used to store the final data on which the CompoundHelper contract functions operate );
* @return ImportData the final value of param 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);
// populate arrays with supply data (supply tokens address, cToken addresses, cToken instances and supply amounts) require(
for (uint i = 0; i < _importInputData.supplyIds.length; i++) { _token != address(0) && _cToken != address(0),
(address _token, address _cToken) = compMapping.getMapping(_importInputData.supplyIds[i]); "ctoken mapping not found"
);
require(_token != address(0) && _cToken != address(0), "ctoken mapping not found");
uint _supplyIndex = add(i, _importInputData.borrowIds.length); uint256 _supplyIndex = add(i, _importInputData.borrowIds.length);
data.cTokens[_supplyIndex] = _cToken; data.cTokens[_supplyIndex] = _cToken;
data.supplyTokens[i] = _token; data.supplyTokens[i] = _token;
data.supplyCtokens[i] = CTokenInterface(_cToken); data.supplyCtokens[i] = CTokenInterface(_cToken);
data.supplyCtokensAddr[i] = (_cToken); data.supplyCtokensAddr[i] = (_cToken);
data.supplyAmts[i] = data.supplyCtokens[i].balanceOf(_importInputData.userAccount); data.supplyAmts[i] = data.supplyCtokens[i].balanceOf(
} _importInputData.userAccount
return data; );
} }
return data;
}
/** /**
* @notice repays the debt taken by user on Compound on its behalf to free its collateral for transfer * @notice repays the debt taken by user on Compound on its behalf to free its collateral for transfer
* @dev uses the cEth contract for ETH repays, otherwise the general cToken interface * @dev uses the cEth contract for ETH repays, otherwise the general cToken interface
* @param _userAccount the user address for which debt is to be repayed * @param _userAccount the user address for which debt is to be repayed
* @param _cTokenContracts array containing all interfaces to the cToken contracts in which the user has debt positions * @param _cTokenContracts array containing all interfaces to the cToken contracts in which the user has debt positions
* @param _borrowAmts array containing the amount borrowed for each token * @param _borrowAmts array containing the amount borrowed for each token
*/ */
function _repayUserDebt( function _repayUserDebt(
address _userAccount, address _userAccount,
CTokenInterface[] memory _cTokenContracts, CTokenInterface[] memory _cTokenContracts,
uint[] memory _borrowAmts uint256[] memory _borrowAmts
) internal { ) internal {
for(uint i = 0; i < _cTokenContracts.length; i++){ for (uint256 i; i < _cTokenContracts.length; i++) {
if(_borrowAmts[i] > 0){ if (_borrowAmts[i] > 0) {
if(address(_cTokenContracts[i]) == address(cEth)){ if (address(_cTokenContracts[i]) == address(cEth))
cEth.repayBorrowBehalf{value: _borrowAmts[i]}(_userAccount); cEth.repayBorrowBehalf{ value: _borrowAmts[i] }(
} _userAccount
else{ );
require(_cTokenContracts[i].repayBorrowBehalf( else
_userAccount, require(
_borrowAmts[i] _cTokenContracts[i].repayBorrowBehalf(
) == 0, "repayOnBehalf-failed"); _userAccount,
} _borrowAmts[i]
} ) == 0,
} "repayOnBehalf-failed"
} );
}
}
}
/** /**
* @notice used to transfer user's supply position on Compound to DSA * @notice used to transfer user's supply position on Compound to DSA
* @dev uses the transferFrom token in cToken contracts to transfer positions, requires approval from user first * @dev uses the transferFrom token in cToken contracts to transfer positions, requires approval from user first
* @param _userAccount address of the user account whose position is to be transferred * @param _userAccount address of the user account whose position is to be transferred
* @param _cTokenContracts array containing all interfaces to the cToken contracts in which the user has supply positions * @param _cTokenContracts array containing all interfaces to the cToken contracts in which the user has supply positions
* @param _amts array containing the amount supplied for each token * @param _amts array containing the amount supplied for each token
*/ */
function _transferTokensToDsa( function _transferTokensToDsa(
address _userAccount, address _userAccount,
CTokenInterface[] memory _cTokenContracts, CTokenInterface[] memory _cTokenContracts,
uint[] memory _amts uint256[] memory _amts
) internal { ) internal {
for(uint i = 0; i < _cTokenContracts.length; i++) { for (uint256 i; i < _cTokenContracts.length; i++)
if(_amts[i] > 0) { if (_amts[i] > 0)
require(_cTokenContracts[i].transferFrom( require(
_userAccount, _cTokenContracts[i].transferFrom(
address(this), _userAccount,
_amts[i] address(this),
), "ctoken-transfer-failed-allowance?"); _amts[i]
} ),
} "ctoken-transfer-failed-allowance?"
} );
}
/** /**
* @notice borrows the user's debt positions from Compound via DSA, so that its debt positions get imported to DSA * @notice borrows the user's debt positions from Compound via DSA, so that its debt positions get imported to DSA
* @dev actually borrow some extra amount than the original position to cover the flash loan fee * @dev actually borrow some extra amount than the original position to cover the flash loan fee
* @param _cTokenContracts array containing all interfaces to the cToken contracts in which the user has debt positions * @param _cTokenContracts array containing all interfaces to the cToken contracts in which the user has debt positions
* @param _amts array containing the amounts the user had borrowed originally from Compound plus the flash loan fee * @param _amts array containing the amounts the user had borrowed originally from Compound plus the flash loan fee
* @param _flashLoanFees flash loan fee (in percentage and scaled up to 10**2) * @param _flashLoanFees flash loan fee (in percentage and scaled up to 10**2)
*/ */
function _borrowDebtPosition( function _borrowDebtPosition(
CTokenInterface[] memory _cTokenContracts, CTokenInterface[] memory _cTokenContracts,
uint256[] memory _amts, uint256[] memory _amts,
uint256[] memory _flashLoanFees uint256[] memory _flashLoanFees
) internal { ) internal {
for (uint i = 0; i < _cTokenContracts.length; i++) { for (uint256 i; i < _cTokenContracts.length; i++)
if (_amts[i] > 0) { if (_amts[i] > 0)
require(_cTokenContracts[i].borrow( require(
add( _cTokenContracts[i].borrow(
_amts[i], add(_amts[i], _flashLoanFees[i])
_flashLoanFees[i] ) == 0,
)) == 0, "borrow-failed-collateral?"); "borrow-failed-collateral?"
} );
} }
} }
}

View File

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

View File

@ -1,3 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6; pragma solidity ^0.7.6;
pragma experimental ABIEncoderV2; pragma experimental ABIEncoderV2;
@ -11,77 +12,99 @@ import { Events } from "./events.sol";
// 4. Then borrow debt of same tokens but include flash loan fee in it. // 4. Then borrow debt of same tokens but include flash loan fee in it.
contract CompoundImportResolver is CompoundHelper { contract CompoundImportResolver is CompoundHelper {
/**
* @notice this function performs the import of user's Compound positions into its DSA
* @dev called internally by the importCompound and migrateCompound functions
* @param _importInputData the struct containing borrowIds of the users borrowed tokens
* @param _flashLoanFees list of flash loan fees
*/
function _importCompound(
ImportInputData memory _importInputData,
uint256[] memory _flashLoanFees
) 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");
* @notice this function performs the import of user's Compound positions into its DSA
* @dev called internally by the importCompound and migrateCompound functions
* @param _importInputData the struct containing borrowIds of the users borrowed tokens
* @param _flashLoanFees list of flash loan fees
*/
function _importCompound(
ImportInputData memory _importInputData,
uint256[] memory _flashLoanFees
) 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;
ImportData memory data; uint256 _length = add(
_importInputData.supplyIds.length,
_importInputData.borrowIds.length
);
data.cTokens = new address[](_length);
uint _length = add(_importInputData.supplyIds.length, _importInputData.borrowIds.length); // get info about all borrowings and lendings by the user on Compound
data.cTokens = new address[](_length); data = getBorrowAmounts(_importInputData, data);
data = getSupplyAmounts(_importInputData, data);
// get info about all borrowings and lendings by the user on Compound _enterMarkets(data.cTokens);
data = getBorrowAmounts(_importInputData, data);
data = getSupplyAmounts(_importInputData, data);
_enterMarkets(_importInputData.cTokens); // pay back user's debt using flash loan funds
_repayUserDebt(
_importInputData.userAccount,
data.borrowCtokens,
data.borrowAmts
);
// pay back user's debt using flash loan funds // transfer user's tokens to DSA
_repayUserDebt(_importInputData.userAccount, data.borrowCtokens, data.borrowAmts); _transferTokensToDsa(
_importInputData.userAccount,
data.supplyCtokens,
data.supplyAmts
);
// transfer user's tokens to DSA // borrow the earlier position from Compound with flash loan fee added
_transferTokensToDsa(_importInputData.userAccount, data.supplyCtokens, data.supplyAmts); _borrowDebtPosition(
data.borrowCtokens,
data.borrowAmts,
_flashLoanFees
);
// borrow the earlier position from Compound with flash loan fee added _eventName = "LogCompoundImport(address,address[],string[],string[],uint256[],uint256[])";
_borrowDebtPosition(data.borrowCtokens, data.borrowAmts, _flashLoanFees); _eventParam = abi.encode(
_importInputData.userAccount,
data.cTokens,
_importInputData.supplyIds,
_importInputData.borrowIds,
data.supplyAmts,
data.borrowAmts
);
}
_eventName = "LogCompoundImport(address,address[],string[],string[],uint256[],uint256[])"; /**
_eventParam = abi.encode( * @notice import Compound position of the address passed in as userAccount
_importInputData.userAccount, * @dev internally calls _importContract to perform the actual import
data.cTokens, * @param _userAccount address of user whose position is to be imported to DSA
_importInputData.supplyIds, * @param _supplyIds Ids of all tokens the user has supplied to Compound
_importInputData.borrowIds, * @param _borrowIds Ids of all token borrowed by the user
data.supplyAmts, * @param _flashLoanFees list of flash loan fees
data.borrowAmts */
); function importCompound(
} address _userAccount,
string[] memory _supplyIds,
/** string[] memory _borrowIds,
* @notice import Compound position of the address passed in as userAccount uint256[] memory _flashLoanFees
* @dev internally calls _importContract to perform the actual import )
* @param _userAccount address of user whose position is to be imported to DSA external
* @param _supplyIds Ids of all tokens the user has supplied to Compound payable
* @param _borrowIds Ids of all token borrowed by the user returns (string memory _eventName, bytes memory _eventParam)
* @param _flashLoanFees list of flash loan fees {
*/ ImportInputData memory inputData = ImportInputData({
function importCompound( userAccount: _userAccount,
address _userAccount, supplyIds: _supplyIds,
string[] memory _supplyIds, borrowIds: _borrowIds
string[] memory _borrowIds, });
uint256[] memory _flashLoanFees
) external payable returns (string memory _eventName, bytes memory _eventParam) {
ImportInputData memory inputData = ImportInputData({
userAccount: _userAccount,
supplyIds: _supplyIds,
borrowIds: _borrowIds
});
(_eventName, _eventParam) = _importCompound(inputData, _flashLoanFees);
}
(_eventName, _eventParam) = _importCompound(inputData, _flashLoanFees);
}
} }
contract ConnectV2CompoundImport is CompoundImportResolver { contract ConnectV2CompoundImport is CompoundImportResolver {
string public constant name = "Compound-Import-v2"; string public constant name = "Compound-Import-v2";
} }