mirror of
https://github.com/Instadapp/dsa-connectors.git
synced 2024-07-29 22:37:00 +00:00
251 lines
9.9 KiB
Solidity
251 lines
9.9 KiB
Solidity
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);
|
|
}
|
|
}
|