From 2126be64e2385514932ae59eb5613172d4081fff Mon Sep 17 00:00:00 2001
From: Thrilok Kumar <thrilok2000@gmail.com>
Date: Wed, 23 Jun 2021 02:18:01 +0530
Subject: [PATCH] Added refinanace connector

---
 .../mainnet/connectors/refinance/events.sol   |   0
 .../mainnet/connectors/refinance/helpers.sol  | 155 +++++++++++
 .../connectors/refinance/helpers/aaveV1.sol   | 202 ++++++++++++++
 .../connectors/refinance/helpers/aaveV2.sol   | 200 ++++++++++++++
 .../connectors/refinance/helpers/compound.sol | 160 +++++++++++
 .../connectors/refinance/interfaces.sol       | 113 ++++++++
 .../mainnet/connectors/refinance/main.sol     | 251 ++++++++++++++++++
 7 files changed, 1081 insertions(+)
 create mode 100644 contracts/mainnet/connectors/refinance/events.sol
 create mode 100644 contracts/mainnet/connectors/refinance/helpers.sol
 create mode 100644 contracts/mainnet/connectors/refinance/helpers/aaveV1.sol
 create mode 100644 contracts/mainnet/connectors/refinance/helpers/aaveV2.sol
 create mode 100644 contracts/mainnet/connectors/refinance/helpers/compound.sol
 create mode 100644 contracts/mainnet/connectors/refinance/interfaces.sol
 create mode 100644 contracts/mainnet/connectors/refinance/main.sol

diff --git a/contracts/mainnet/connectors/refinance/events.sol b/contracts/mainnet/connectors/refinance/events.sol
new file mode 100644
index 00000000..e69de29b
diff --git a/contracts/mainnet/connectors/refinance/helpers.sol b/contracts/mainnet/connectors/refinance/helpers.sol
new file mode 100644
index 00000000..c3593d50
--- /dev/null
+++ b/contracts/mainnet/connectors/refinance/helpers.sol
@@ -0,0 +1,155 @@
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+import { Basic } from "../../common/basic.sol";
+
+
+import {
+    AaveV1ProviderInterface,
+    AaveV1Interface,
+    AaveV2LendingPoolProviderInterface, 
+    AaveV2DataProviderInterface,
+    AaveV2Interface,
+    ComptrollerInterface,
+    CTokenInterface,
+    CompoundMappingInterface
+} from "./interfaces.sol";
+
+
+import { TokenInterface } from "../../common/interfaces.sol";
+
+import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
+import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
+
+contract Helpers is Basic {
+    using SafeERC20 for IERC20;
+
+    enum Protocol {
+        Aave,
+        AaveV2,
+        Compound
+    }
+
+    address payable constant feeCollector = 0xb1DC62EC38E6E3857a887210C38418E4A17Da5B2;
+
+    /**
+     * @dev Return InstaDApp Mapping Address
+     */
+    address constant internal getMappingAddr = 0xA8F9D4aA7319C54C04404765117ddBf9448E2082; // CompoundMapping Address
+
+    /**
+     * @dev Return Compound Comptroller Address
+     */
+    address constant internal getComptrollerAddress = 0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B; // CompoundMapping Address
+
+    /**
+     * @dev get Aave Provider
+    */
+    AaveV1ProviderInterface constant internal getAaveProvider =
+                    AaveV1ProviderInterface(0x24a42fD28C976A61Df5D00D0599C34c4f90748c8);
+
+    /**
+     * @dev get Aave Lending Pool Provider
+    */
+    AaveV2LendingPoolProviderInterface constant internal getAaveV2Provider =
+                    AaveV2LendingPoolProviderInterface(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5);
+
+    /**
+     * @dev get Aave Protocol Data Provider
+    */
+    AaveV2DataProviderInterface constant internal getAaveV2DataProvider =
+                    AaveV2DataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d);
+
+    /**
+     * @dev get Referral Code
+    */
+    uint16 constant internal getReferralCode =  3228;
+}
+
+contract protocolHelpers is Helpers {
+    using SafeERC20 for IERC20;
+
+    function getWithdrawBalance(AaveV1Interface aave, address token) internal view returns (uint bal) {
+        (bal, , , , , , , , , ) = aave.getUserReserveData(token, address(this));
+    }
+
+    function getPaybackBalance(AaveV1Interface aave, address token) internal view returns (uint bal, uint fee) {
+        (, bal, , , , , fee, , , ) = aave.getUserReserveData(token, address(this));
+    }
+
+    function getTotalBorrowBalance(AaveV1Interface aave, address token) internal view returns (uint amt) {
+        (, uint bal, , , , , uint fee, , , ) = aave.getUserReserveData(token, address(this));
+        amt = add(bal, fee);
+    }
+
+    function getWithdrawBalanceV2(AaveV2DataProviderInterface aaveData, address token) internal view returns (uint bal) {
+        (bal, , , , , , , , ) = aaveData.getUserReserveData(token, address(this));
+    }
+
+    function getPaybackBalanceV2(AaveV2DataProviderInterface aaveData, address token, uint rateMode) internal view returns (uint bal) {
+        if (rateMode == 1) {
+            (, bal, , , , , , , ) = aaveData.getUserReserveData(token, address(this));
+        } else {
+            (, , bal, , , , , , ) = aaveData.getUserReserveData(token, address(this));
+        }
+    }
+
+    function getIsColl(AaveV1Interface aave, address token) internal view returns (bool isCol) {
+        (, , , , , , , , , isCol) = aave.getUserReserveData(token, address(this));
+    }
+
+    function getIsCollV2(AaveV2DataProviderInterface aaveData, address token) internal view returns (bool isCol) {
+        (, , , , , , , , isCol) = aaveData.getUserReserveData(token, address(this));
+    }
+
+    function getMaxBorrow(Protocol target, address token, CTokenInterface ctoken, uint rateMode) internal returns (uint amt) {
+        AaveV1Interface aaveV1 = AaveV1Interface(getAaveProvider.getLendingPool());
+        AaveV2DataProviderInterface aaveData = getAaveV2DataProvider;
+
+        if (target == Protocol.Aave) {
+            (uint _amt, uint _fee) = getPaybackBalance(aaveV1, token);
+            amt = _amt + _fee;
+        } else if (target == Protocol.AaveV2) {
+            amt = getPaybackBalanceV2(aaveData, token, rateMode);
+        } else if (target == Protocol.Compound) {
+            amt = ctoken.borrowBalanceCurrent(address(this));
+        }
+    }
+
+    function transferFees(address token, uint feeAmt) internal {
+        if (feeAmt > 0) {
+            if (token == ethAddr) {
+                feeCollector.transfer(feeAmt);
+            } else {
+                IERC20(token).safeTransfer(feeCollector, feeAmt);
+            }
+        }
+    }
+
+    function calculateFee(uint256 amount, uint256 fee, bool toAdd) internal pure returns(uint feeAmount, uint _amount){
+        feeAmount = wmul(amount, fee);
+        _amount = toAdd ? add(amount, feeAmount) : sub(amount, feeAmount);
+    }
+
+    function getTokenInterfaces(uint length, address[] memory tokens) internal pure returns (TokenInterface[] memory) {
+        TokenInterface[] memory _tokens = new TokenInterface[](length);
+        for (uint i = 0; i < length; i++) {
+            if (tokens[i] ==  ethAddr) {
+                _tokens[i] = TokenInterface(wethAddr);
+            } else {
+                _tokens[i] = TokenInterface(tokens[i]);
+            }
+        }
+        return _tokens;
+    }
+
+    function getCtokenInterfaces(uint length, string[] memory tokenIds) internal view returns (CTokenInterface[] memory) {
+        CTokenInterface[] memory _ctokens = new CTokenInterface[](length);
+        for (uint i = 0; i < length; i++) {
+            (address token, address cToken) = CompoundMappingInterface(getMappingAddr).getMapping(tokenIds[i]);
+            require(token != address(0) && cToken != address(0), "invalid token/ctoken address");
+            _ctokens[i] = CTokenInterface(cToken);
+        }
+        return _ctokens;
+    }
+}
\ No newline at end of file
diff --git a/contracts/mainnet/connectors/refinance/helpers/aaveV1.sol b/contracts/mainnet/connectors/refinance/helpers/aaveV1.sol
new file mode 100644
index 00000000..bc33c624
--- /dev/null
+++ b/contracts/mainnet/connectors/refinance/helpers/aaveV1.sol
@@ -0,0 +1,202 @@
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+import { protocolHelpers } from "../helpers.sol";
+
+import {
+    AaveV1ProviderInterface,
+    AaveV1Interface,
+    AaveV1CoreInterface,
+    ATokenV1Interface,
+    CTokenInterface
+    // AaveV2LendingPoolProviderInterface, 
+    // AaveV2DataProviderInterface,
+    // AaveV2Interface,
+} from "../interfaces.sol";
+
+import { TokenInterface } from "../../../common/interfaces.sol";
+
+contract AaveV1Helpers is protocolHelpers {
+
+    struct AaveV1BorrowData {
+        AaveV1Interface aave;
+        uint length;
+        uint fee;
+        Protocol target;
+        TokenInterface[] tokens;
+        CTokenInterface[] ctokens;
+        uint[] amts;
+        uint[] borrowRateModes;
+        uint[] paybackRateModes;
+    }
+
+    struct AaveV1DepositData {
+        AaveV1Interface aave;
+        AaveV1CoreInterface aaveCore;
+        uint length;
+        uint fee;
+        TokenInterface[] tokens;
+        uint[] amts;
+    }
+
+    function _aaveV1BorrowOne(
+        AaveV1Interface aave,
+        uint fee,
+        Protocol target,
+        TokenInterface token,
+        CTokenInterface ctoken,
+        uint amt,
+        uint borrowRateMode,
+        uint paybackRateMode
+    ) internal returns (uint) {
+        if (amt > 0) {
+
+            address _token = address(token) == wethAddr ? ethAddr : address(token);
+
+            if (amt == uint(-1)) {
+                amt = getMaxBorrow(target, address(token), ctoken, paybackRateMode);
+            }
+
+            (uint feeAmt, uint _amt) = calculateFee(amt, fee, true);
+
+            aave.borrow(_token, _amt, borrowRateMode, getReferralCode);
+            transferFees(_token, feeAmt);
+        }
+        return amt;
+    }
+
+    function _aaveV1Borrow(
+        AaveV1BorrowData memory data
+    ) internal returns (uint[] memory) {
+        uint[] memory finalAmts = new uint[](data.length);
+        for (uint i = 0; i < data.length; i++) {
+            finalAmts[i] = _aaveV1BorrowOne(
+                data.aave,
+                data.fee,
+                data.target,
+                data.tokens[i],
+                data.ctokens[i],
+                data.amts[i],
+                data.borrowRateModes[i],
+                data.paybackRateModes[i]
+            );
+        }
+        return finalAmts;
+    }
+
+    function _aaveV1DepositOne(
+        AaveV1Interface aave,
+        AaveV1CoreInterface aaveCore,
+        uint fee,
+        TokenInterface token,
+        uint amt
+    ) internal {
+        if (amt > 0) {
+            uint ethAmt;
+            (uint feeAmt, uint _amt) = calculateFee(amt, fee, false);
+
+            bool isEth = address(token) == wethAddr;
+
+            address _token = isEth ? ethAddr : address(token);
+
+            if (isEth) {
+                ethAmt = _amt;
+            } else {
+                token.approve(address(aaveCore), _amt);
+            }
+
+            transferFees(_token, feeAmt);
+
+            aave.deposit{value:ethAmt}(_token, _amt, getReferralCode);
+
+            if (!getIsColl(aave, _token))
+                aave.setUserUseReserveAsCollateral(_token, true);
+        }
+    }
+
+    function _aaveV1Deposit(
+        AaveV1DepositData memory data
+    ) internal {
+        for (uint i = 0; i < data.length; i++) {
+            _aaveV1DepositOne(
+                data.aave,
+                data.aaveCore,
+                data.fee,
+                data.tokens[i],
+                data.amts[i]
+            );
+        }
+    }
+
+    function _aaveV1WithdrawOne(
+        AaveV1Interface aave,
+        AaveV1CoreInterface aaveCore,
+        TokenInterface token,
+        uint amt
+    ) internal returns (uint) {
+        if (amt > 0) {
+            address _token = address(token) == wethAddr ? ethAddr : address(token);
+            ATokenV1Interface atoken = ATokenV1Interface(aaveCore.getReserveATokenAddress(_token));
+            if (amt == uint(-1)) {
+                amt = getWithdrawBalance(aave, _token);
+            }
+            atoken.redeem(amt);
+        }
+        return amt;
+    }
+
+    function _aaveV1Withdraw(
+        AaveV1Interface aave,
+        AaveV1CoreInterface aaveCore,
+        uint length,
+        TokenInterface[] memory tokens,
+        uint[] memory amts
+    ) internal returns (uint[] memory) {
+        uint[] memory finalAmts = new uint[](length);
+        for (uint i = 0; i < length; i++) {
+            finalAmts[i] = _aaveV1WithdrawOne(aave, aaveCore, tokens[i], amts[i]);
+        }
+        return finalAmts;
+    }
+
+    function _aaveV1PaybackOne(
+        AaveV1Interface aave,
+        AaveV1CoreInterface aaveCore,
+        TokenInterface token,
+        uint amt
+    ) internal returns (uint) {
+        if (amt > 0) {
+            uint ethAmt;
+
+            bool isEth = address(token) == wethAddr;
+
+            address _token = isEth ? ethAddr : address(token);
+
+            if (amt == uint(-1)) {
+                (uint _amt, uint _fee) = getPaybackBalance(aave, _token);
+                amt = _amt + _fee;
+            }
+
+            if (isEth) {
+                ethAmt = amt;
+            } else {
+                token.approve(address(aaveCore), amt);
+            }
+
+            aave.repay{value:ethAmt}(_token, amt, payable(address(this)));
+        }
+        return amt;
+    }
+
+    function _aaveV1Payback(
+        AaveV1Interface aave,
+        AaveV1CoreInterface aaveCore,
+        uint length,
+        TokenInterface[] memory tokens,
+        uint[] memory amts
+    ) internal {
+        for (uint i = 0; i < length; i++) {
+            _aaveV1PaybackOne(aave, aaveCore, tokens[i], amts[i]);
+        }
+    }
+}
diff --git a/contracts/mainnet/connectors/refinance/helpers/aaveV2.sol b/contracts/mainnet/connectors/refinance/helpers/aaveV2.sol
new file mode 100644
index 00000000..488deae3
--- /dev/null
+++ b/contracts/mainnet/connectors/refinance/helpers/aaveV2.sol
@@ -0,0 +1,200 @@
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+import { protocolHelpers } from "../helpers.sol";
+
+import {
+    // AaveV1ProviderInterface,
+    // AaveV1Interface,
+    // AaveV1CoreInterface,
+    AaveV2LendingPoolProviderInterface, 
+    AaveV2DataProviderInterface,
+    AaveV2Interface,
+    ATokenV1Interface,
+    CTokenInterface
+} from "../interfaces.sol";
+
+import { TokenInterface } from "../../../common/interfaces.sol";
+
+contract AaveV2Helpers is protocolHelpers {
+
+    struct AaveV2BorrowData {
+        AaveV2Interface aave;
+        uint length;
+        uint fee;
+        Protocol target;
+        TokenInterface[] tokens;
+        CTokenInterface[] ctokens;
+        uint[] amts;
+        uint[] rateModes;
+    }
+
+    struct AaveV2PaybackData {
+        AaveV2Interface aave;
+        AaveV2DataProviderInterface aaveData;
+        uint length;
+        TokenInterface[] tokens;
+        uint[] amts;
+        uint[] rateModes;
+    }
+
+    struct AaveV2WithdrawData {
+        AaveV2Interface aave;
+        AaveV2DataProviderInterface aaveData;
+        uint length;
+        TokenInterface[] tokens;
+        uint[] amts;
+    }
+
+    function _aaveV2BorrowOne(
+        AaveV2Interface aave,
+        uint fee,
+        Protocol target,
+        TokenInterface token,
+        CTokenInterface ctoken,
+        uint amt,
+        uint rateMode
+    ) internal returns (uint) {
+        if (amt > 0) {
+            bool isEth = address(token) == wethAddr;
+            
+            address _token = isEth ? ethAddr : address(token);
+
+            if (amt == uint(-1)) {
+                amt = getMaxBorrow(target, _token, ctoken, rateMode);
+            }
+
+            (uint feeAmt, uint _amt) = calculateFee(amt, fee, true);
+
+            aave.borrow(address(token), _amt, rateMode, getReferralCode, address(this));
+            convertWethToEth(isEth, token, amt);
+
+            transferFees(_token, feeAmt);
+        }
+        return amt;
+    }
+
+    function _aaveV2Borrow(
+        AaveV2BorrowData memory data
+    ) internal returns (uint[] memory) {
+        uint[] memory finalAmts = new uint[](data.length);
+        for (uint i = 0; i < data.length; i++) {
+            finalAmts[i] = _aaveV2BorrowOne(
+                data.aave,
+                data.fee,
+                data.target,
+                data.tokens[i],
+                data.ctokens[i],
+                data.amts[i],
+                data.rateModes[i]
+            );
+        }
+        return finalAmts;
+    }
+
+    function _aaveV2DepositOne(
+        AaveV2Interface aave,
+        AaveV2DataProviderInterface aaveData,
+        uint fee,
+        TokenInterface token,
+        uint amt
+    ) internal {
+        if (amt > 0) {
+            (uint feeAmt, uint _amt) = calculateFee(amt, fee, false);
+
+            bool isEth = address(token) == wethAddr;
+            address _token = isEth ? ethAddr : address(token);
+
+            transferFees(_token, feeAmt);
+
+            convertEthToWeth(isEth, token, _amt);
+
+            token.approve(address(aave), _amt);
+
+            aave.deposit(address(token), _amt, address(this), getReferralCode);
+
+            if (!getIsCollV2(aaveData, address(token))) {
+                aave.setUserUseReserveAsCollateral(address(token), true);
+            }
+        }
+    }
+
+    function _aaveV2Deposit(
+        AaveV2Interface aave,
+        AaveV2DataProviderInterface aaveData,
+        uint length,
+        uint fee,
+        TokenInterface[] memory tokens,
+        uint[] memory amts
+    ) internal {
+        for (uint i = 0; i < length; i++) {
+            _aaveV2DepositOne(aave, aaveData, fee, tokens[i], amts[i]);
+        }
+    }
+
+    function _aaveV2WithdrawOne(
+        AaveV2Interface aave,
+        AaveV2DataProviderInterface aaveData,
+        TokenInterface token,
+        uint amt
+    ) internal returns (uint _amt) {
+        if (amt > 0) {
+            bool isEth = address(token) == wethAddr;
+
+            aave.withdraw(address(token), amt, address(this));
+
+            _amt = amt == uint(-1) ? getWithdrawBalanceV2(aaveData, address(token)) : amt;
+
+            convertWethToEth(isEth, token, _amt);
+        }
+    }
+
+    function _aaveV2Withdraw(
+        AaveV2WithdrawData memory data
+    ) internal returns (uint[] memory) {
+        uint[] memory finalAmts = new uint[](data.length);
+        for (uint i = 0; i < data.length; i++) {
+            finalAmts[i] = _aaveV2WithdrawOne(
+                data.aave,
+                data.aaveData,
+                data.tokens[i],
+                data.amts[i]
+            );
+        }
+        return finalAmts;
+    }
+
+    function _aaveV2PaybackOne(
+        AaveV2Interface aave,
+        AaveV2DataProviderInterface aaveData,
+        TokenInterface token,
+        uint amt,
+        uint rateMode
+    ) internal returns (uint _amt) {
+        if (amt > 0) {
+            bool isEth = address(token) == wethAddr;
+
+            _amt = amt == uint(-1) ? getPaybackBalanceV2(aaveData, address(token), rateMode) : amt;
+
+            convertEthToWeth(isEth, token, _amt);
+
+            token.approve(address(aave), _amt);
+
+            aave.repay(address(token), _amt, rateMode, address(this));
+        }
+    }
+
+    function _aaveV2Payback(
+        AaveV2PaybackData memory data
+    ) internal {
+        for (uint i = 0; i < data.length; i++) {
+            _aaveV2PaybackOne(
+                data.aave,
+                data.aaveData,
+                data.tokens[i],
+                data.amts[i],
+                data.rateModes[i]
+            );
+        }
+    }
+}
diff --git a/contracts/mainnet/connectors/refinance/helpers/compound.sol b/contracts/mainnet/connectors/refinance/helpers/compound.sol
new file mode 100644
index 00000000..c6bebc42
--- /dev/null
+++ b/contracts/mainnet/connectors/refinance/helpers/compound.sol
@@ -0,0 +1,160 @@
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+import { protocolHelpers } from "../helpers.sol";
+
+import {
+    ComptrollerInterface,
+    CTokenInterface,
+    CompoundMappingInterface,
+    CETHInterface
+} from "../interfaces.sol";
+
+import { TokenInterface } from "../../../common/interfaces.sol";
+
+
+
+contract CompoundHelpers is protocolHelpers {
+
+    struct CompoundBorrowData {
+        uint length;
+        uint fee;
+        Protocol target;
+        CTokenInterface[] ctokens;
+        TokenInterface[] tokens;
+        uint[] amts;
+        uint[] rateModes;
+    }
+
+    function _compEnterMarkets(uint length, CTokenInterface[] memory ctokens) internal {
+        ComptrollerInterface troller = ComptrollerInterface(getComptrollerAddress);
+        address[] memory _cTokens = new address[](length);
+
+        for (uint i = 0; i < length; i++) {
+            _cTokens[i] = address(ctokens[i]);
+        }
+        troller.enterMarkets(_cTokens);
+    }
+
+    function _compBorrowOne(
+        uint fee,
+        CTokenInterface ctoken,
+        TokenInterface token,
+        uint amt,
+        Protocol target,
+        uint rateMode
+    ) internal returns (uint) {
+        if (amt > 0) {
+            address _token = address(token) == wethAddr ? ethAddr : address(token);
+
+            if (amt == uint(-1)) {
+                amt = getMaxBorrow(target, address(token), ctoken, rateMode);
+            }
+
+            (uint feeAmt, uint _amt) = calculateFee(amt, fee, true);
+
+            require(ctoken.borrow(_amt) == 0, "borrow-failed-collateral?");
+            transferFees(_token, feeAmt);
+        }
+        return amt;
+    }
+
+    function _compBorrow(
+        CompoundBorrowData memory data
+    ) internal returns (uint[] memory) {
+        uint[] memory finalAmts = new uint[](data.length);
+        for (uint i = 0; i < data.length; i++) {
+            finalAmts[i] = _compBorrowOne(
+                data.fee, 
+                data.ctokens[i], 
+                data.tokens[i], 
+                data.amts[i], 
+                data.target, 
+                data.rateModes[i]
+            );
+        }
+        return finalAmts;
+    }
+
+    function _compDepositOne(uint fee, CTokenInterface ctoken, TokenInterface token, uint amt) internal {
+        if (amt > 0) {
+            address _token = address(token) == wethAddr ? ethAddr : address(token);
+
+            (uint feeAmt, uint _amt) = calculateFee(amt, fee, false);
+
+            if (_token != ethAddr) {
+                token.approve(address(ctoken), _amt);
+                require(ctoken.mint(_amt) == 0, "deposit-failed");
+            } else {
+                CETHInterface(address(ctoken)).mint{value:_amt}();
+            }
+            transferFees(_token, feeAmt);
+        }
+    }
+
+    function _compDeposit(
+        uint length,
+        uint fee,
+        CTokenInterface[] memory ctokens,
+        TokenInterface[] memory tokens,
+        uint[] memory amts
+    ) internal {
+        for (uint i = 0; i < length; i++) {
+            _compDepositOne(fee, ctokens[i], tokens[i], amts[i]);
+        }
+    }
+
+    function _compWithdrawOne(CTokenInterface ctoken, TokenInterface token, uint amt) internal returns (uint) {
+        if (amt > 0) {
+            if (amt == uint(-1)) {
+                bool isEth = address(token) == wethAddr;
+                uint initalBal = isEth ? address(this).balance : token.balanceOf(address(this));
+                require(ctoken.redeem(ctoken.balanceOf(address(this))) == 0, "withdraw-failed");
+                uint finalBal = isEth ? address(this).balance : token.balanceOf(address(this));
+                amt = sub(finalBal, initalBal);
+            } else {
+                require(ctoken.redeemUnderlying(amt) == 0, "withdraw-failed");
+            }
+        }
+        return amt;
+    }
+
+    function _compWithdraw(
+        uint length,
+        CTokenInterface[] memory ctokens,
+        TokenInterface[] memory tokens,
+        uint[] memory amts
+    ) internal returns(uint[] memory) {
+        uint[] memory finalAmts = new uint[](length);
+        for (uint i = 0; i < length; i++) {
+            finalAmts[i] = _compWithdrawOne(ctokens[i], tokens[i], amts[i]);
+        }
+        return finalAmts;
+    }
+
+    function _compPaybackOne(CTokenInterface ctoken, TokenInterface token, uint amt) internal returns (uint) {
+        if (amt > 0) {
+            if (amt == uint(-1)) {
+                amt = ctoken.borrowBalanceCurrent(address(this));
+            }
+            if (address(token) != wethAddr) {
+                token.approve(address(ctoken), amt);
+                require(ctoken.repayBorrow(amt) == 0, "repay-failed.");
+            } else {
+                CETHInterface(address(ctoken)).repayBorrow{value:amt}();
+            }
+        }
+        return amt;
+    }
+
+    function _compPayback(
+        uint length,
+        CTokenInterface[] memory ctokens,
+        TokenInterface[] memory tokens,
+        uint[] memory amts
+    ) internal {
+        for (uint i = 0; i < length; i++) {
+            _compPaybackOne(ctokens[i], tokens[i], amts[i]);
+        }
+    }
+}
\ No newline at end of file
diff --git a/contracts/mainnet/connectors/refinance/interfaces.sol b/contracts/mainnet/connectors/refinance/interfaces.sol
new file mode 100644
index 00000000..a42edc61
--- /dev/null
+++ b/contracts/mainnet/connectors/refinance/interfaces.sol
@@ -0,0 +1,113 @@
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+// Compound Helpers
+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 borrowBalanceCurrent(address account) external returns (uint);
+    function redeemUnderlying(uint redeemAmount) external returns (uint);
+
+    function balanceOf(address owner) external view returns (uint256 balance);
+}
+
+interface CETHInterface {
+    function mint() external payable;
+    function repayBorrow() external payable;
+}
+
+interface CompoundMappingInterface {
+    function cTokenMapping(string calldata tokenId) external view returns (address);
+    function getMapping(string calldata tokenId) external view returns (address, address);
+}
+
+interface ComptrollerInterface {
+    function enterMarkets(address[] calldata cTokens) external returns (uint[] memory);
+}
+// End Compound Helpers
+
+// Aave v1 Helpers
+interface AaveV1Interface {
+    function deposit(address _reserve, uint256 _amount, uint16 _referralCode) external payable;
+    function redeemUnderlying(
+        address _reserve,
+        address payable _user,
+        uint256 _amount,
+        uint256 _aTokenBalanceAfterRedeem
+    ) external;
+    
+    function setUserUseReserveAsCollateral(address _reserve, bool _useAsCollateral) external;
+    function getUserReserveData(address _reserve, address _user) external view returns (
+        uint256 currentATokenBalance,
+        uint256 currentBorrowBalance,
+        uint256 principalBorrowBalance,
+        uint256 borrowRateMode,
+        uint256 borrowRate,
+        uint256 liquidityRate,
+        uint256 originationFee,
+        uint256 variableBorrowIndex,
+        uint256 lastUpdateTimestamp,
+        bool usageAsCollateralEnabled
+    );
+    function borrow(address _reserve, uint256 _amount, uint256 _interestRateMode, uint16 _referralCode) external;
+    function repay(address _reserve, uint256 _amount, address payable _onBehalfOf) external payable;
+}
+
+interface AaveV1ProviderInterface {
+    function getLendingPool() external view returns (address);
+    function getLendingPoolCore() external view returns (address);
+}
+
+interface AaveV1CoreInterface {
+    function getReserveATokenAddress(address _reserve) external view returns (address);
+}
+
+interface ATokenV1Interface {
+    function redeem(uint256 _amount) external;
+    function balanceOf(address _user) external view returns(uint256);
+    function principalBalanceOf(address _user) external view returns(uint256);
+
+    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);
+}
+// End Aave v1 Helpers
+
+// Aave v2 Helpers
+interface AaveV2Interface {
+    function deposit(address _asset, uint256 _amount, address _onBehalfOf, uint16 _referralCode) external;
+    function withdraw(address _asset, uint256 _amount, address _to) external;
+    function borrow(
+        address _asset,
+        uint256 _amount,
+        uint256 _interestRateMode,
+        uint16 _referralCode,
+        address _onBehalfOf
+    ) external;
+    function repay(address _asset, uint256 _amount, uint256 _rateMode, address _onBehalfOf) external;
+    function setUserUseReserveAsCollateral(address _asset, bool _useAsCollateral) external;
+}
+
+interface AaveV2LendingPoolProviderInterface {
+    function getLendingPool() external view returns (address);
+}
+
+// Aave Protocol Data Provider
+interface AaveV2DataProviderInterface {
+    function getUserReserveData(address _asset, address _user) external view returns (
+        uint256 currentATokenBalance,
+        uint256 currentStableDebt,
+        uint256 currentVariableDebt,
+        uint256 principalStableDebt,
+        uint256 scaledVariableDebt,
+        uint256 stableBorrowRate,
+        uint256 liquidityRate,
+        uint40 stableRateLastUpdated,
+        bool usageAsCollateralEnabled
+    );
+}
+// End Aave v2 Helpers
diff --git a/contracts/mainnet/connectors/refinance/main.sol b/contracts/mainnet/connectors/refinance/main.sol
new file mode 100644
index 00000000..bafbb69a
--- /dev/null
+++ b/contracts/mainnet/connectors/refinance/main.sol
@@ -0,0 +1,251 @@
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+/**
+ * @title Refinance.
+ * @dev Refinancing.
+ */
+
+import { TokenInterface } from "../../common/interfaces.sol";
+
+import {
+    AaveV1ProviderInterface,
+    AaveV1Interface,
+    AaveV1CoreInterface,
+    AaveV2LendingPoolProviderInterface, 
+    AaveV2DataProviderInterface,
+    AaveV2Interface,
+    ComptrollerInterface,
+    CTokenInterface,
+    CompoundMappingInterface
+} from "./interfaces.sol";
+
+
+import { AaveV1Helpers } from "./helpers/aaveV1.sol";
+import { AaveV2Helpers } from "./helpers/aaveV2.sol";
+import { CompoundHelpers } from "./helpers/compound.sol";
+
+
+contract RefinanceResolver is CompoundHelpers, AaveV1Helpers, AaveV2Helpers {
+
+    struct RefinanceData {
+        Protocol source;
+        Protocol target;
+        uint collateralFee;
+        uint debtFee;
+        address[] tokens;
+        string[] ctokenIds;
+        uint[] borrowAmts;
+        uint[] withdrawAmts;
+        uint[] borrowRateModes;
+        uint[] paybackRateModes;
+    }
+
+    /**
+     * @dev Refinance
+     * @notice Refinancing between AaveV1, AaveV2 and Compound
+     * @param data refinance data.
+    */
+    function refinance(RefinanceData calldata data) external payable {
+
+        require(data.source != data.target, "source-and-target-unequal");
+
+        uint length = data.tokens.length;
+
+        require(data.borrowAmts.length == length, "length-mismatch");
+        require(data.withdrawAmts.length == length, "length-mismatch");
+        require(data.borrowRateModes.length == length, "length-mismatch");
+        require(data.paybackRateModes.length == length, "length-mismatch");
+        require(data.ctokenIds.length == length, "length-mismatch");
+
+        AaveV2Interface aaveV2 = AaveV2Interface(getAaveV2Provider.getLendingPool());
+        AaveV1Interface aaveV1 = AaveV1Interface(getAaveProvider.getLendingPool());
+        AaveV1CoreInterface aaveCore = AaveV1CoreInterface(getAaveProvider.getLendingPoolCore());
+        AaveV2DataProviderInterface aaveData = getAaveV2DataProvider;
+
+        uint[] memory depositAmts;
+        uint[] memory paybackAmts;
+
+        TokenInterface[] memory tokens = getTokenInterfaces(length, data.tokens);
+        CTokenInterface[] memory _ctokens = getCtokenInterfaces(length, data.ctokenIds);
+
+        if (data.source == Protocol.Aave && data.target == Protocol.AaveV2) {
+            AaveV2BorrowData memory _aaveV2BorrowData;
+
+            _aaveV2BorrowData.aave = aaveV2;
+            _aaveV2BorrowData.length = length;
+            _aaveV2BorrowData.fee = data.debtFee;
+            _aaveV2BorrowData.target = data.source;
+            _aaveV2BorrowData.tokens = tokens;
+            _aaveV2BorrowData.ctokens = _ctokens;
+            _aaveV2BorrowData.amts = data.borrowAmts;
+            _aaveV2BorrowData.rateModes = data.borrowRateModes;
+
+            paybackAmts = _aaveV2Borrow(_aaveV2BorrowData);
+            _aaveV1Payback(aaveV1, aaveCore, length, tokens, paybackAmts);
+            depositAmts = _aaveV1Withdraw(aaveV1, aaveCore, length, tokens, data.withdrawAmts);
+            _aaveV2Deposit(aaveV2, aaveData, length, data.collateralFee, tokens, depositAmts);
+        } else if (data.source == Protocol.Aave && data.target == Protocol.Compound) {
+            _compEnterMarkets(length, _ctokens);
+
+            CompoundBorrowData memory _compoundBorrowData;
+
+            _compoundBorrowData.length = length;
+            _compoundBorrowData.fee = data.debtFee;
+            _compoundBorrowData.target = data.source;
+            _compoundBorrowData.ctokens = _ctokens;
+            _compoundBorrowData.tokens = tokens;
+            _compoundBorrowData.amts = data.borrowAmts;
+            _compoundBorrowData.rateModes = data.borrowRateModes;
+
+            paybackAmts = _compBorrow(_compoundBorrowData);
+            
+            _aaveV1Payback(aaveV1, aaveCore, length, tokens, paybackAmts);
+            depositAmts = _aaveV1Withdraw(aaveV1, aaveCore, length, tokens, data.withdrawAmts);
+            _compDeposit(length, data.collateralFee, _ctokens, tokens, depositAmts);
+        } else if (data.source == Protocol.AaveV2 && data.target == Protocol.Aave) {
+
+            AaveV1BorrowData memory _aaveV1BorrowData;
+            AaveV2PaybackData memory _aaveV2PaybackData;
+            AaveV2WithdrawData memory _aaveV2WithdrawData;
+
+            {
+                _aaveV1BorrowData.aave = aaveV1;
+                _aaveV1BorrowData.length = length;
+                _aaveV1BorrowData.fee = data.debtFee;
+                _aaveV1BorrowData.target = data.source;
+                _aaveV1BorrowData.tokens = tokens;
+                _aaveV1BorrowData.ctokens = _ctokens;
+                _aaveV1BorrowData.amts = data.borrowAmts;
+                _aaveV1BorrowData.borrowRateModes = data.borrowRateModes;
+                _aaveV1BorrowData.paybackRateModes = data.paybackRateModes;
+
+                paybackAmts = _aaveV1Borrow(_aaveV1BorrowData);
+            }
+            
+            {
+                _aaveV2PaybackData.aave = aaveV2;
+                _aaveV2PaybackData.aaveData = aaveData;
+                _aaveV2PaybackData.length = length;
+                _aaveV2PaybackData.tokens = tokens;
+                _aaveV2PaybackData.amts = paybackAmts;
+                _aaveV2PaybackData.rateModes = data.paybackRateModes;
+                _aaveV2Payback(_aaveV2PaybackData);
+            }
+
+            {
+                _aaveV2WithdrawData.aave = aaveV2;
+                _aaveV2WithdrawData.aaveData = aaveData;
+                _aaveV2WithdrawData.length = length;
+                _aaveV2WithdrawData.tokens = tokens;
+                _aaveV2WithdrawData.amts = data.withdrawAmts;
+                depositAmts = _aaveV2Withdraw(_aaveV2WithdrawData);
+            }
+            {
+                AaveV1DepositData memory _aaveV1DepositData;
+                
+                _aaveV1DepositData.aave = aaveV1;
+                _aaveV1DepositData.aaveCore = aaveCore;
+                _aaveV1DepositData.length = length;
+                _aaveV1DepositData.fee = data.collateralFee;
+                _aaveV1DepositData.tokens = tokens;
+                _aaveV1DepositData.amts = depositAmts;
+
+                _aaveV1Deposit(_aaveV1DepositData);
+            }
+        } else if (data.source == Protocol.AaveV2 && data.target == Protocol.Compound) {
+            _compEnterMarkets(length, _ctokens);
+
+            {
+                CompoundBorrowData memory _compoundBorrowData;
+
+                _compoundBorrowData.length = length;
+                _compoundBorrowData.fee = data.debtFee;
+                _compoundBorrowData.target = data.source;
+                _compoundBorrowData.ctokens = _ctokens;
+                _compoundBorrowData.tokens = tokens;
+                _compoundBorrowData.amts = data.borrowAmts;
+                _compoundBorrowData.rateModes = data.borrowRateModes;
+
+                paybackAmts = _compBorrow(_compoundBorrowData);
+            }
+
+            AaveV2PaybackData memory _aaveV2PaybackData;
+
+            _aaveV2PaybackData.aave = aaveV2;
+            _aaveV2PaybackData.aaveData = aaveData;
+            _aaveV2PaybackData.length = length;
+            _aaveV2PaybackData.tokens = tokens;
+            _aaveV2PaybackData.amts = paybackAmts;
+            _aaveV2PaybackData.rateModes = data.paybackRateModes;
+            
+            _aaveV2Payback(_aaveV2PaybackData);
+
+            {
+                AaveV2WithdrawData memory _aaveV2WithdrawData;
+
+                _aaveV2WithdrawData.aave = aaveV2;
+                _aaveV2WithdrawData.aaveData = aaveData;
+                _aaveV2WithdrawData.length = length;
+                _aaveV2WithdrawData.tokens = tokens;
+                _aaveV2WithdrawData.amts = data.withdrawAmts;
+                depositAmts = _aaveV2Withdraw(_aaveV2WithdrawData);
+            }
+            _compDeposit(length, data.collateralFee, _ctokens, tokens, depositAmts);
+        } else if (data.source == Protocol.Compound && data.target == Protocol.Aave) {
+
+            AaveV1BorrowData memory _aaveV1BorrowData;
+
+            _aaveV1BorrowData.aave = aaveV1;
+            _aaveV1BorrowData.length = length;
+            _aaveV1BorrowData.fee = data.debtFee;
+            _aaveV1BorrowData.target = data.source;
+            _aaveV1BorrowData.tokens = tokens;
+            _aaveV1BorrowData.ctokens = _ctokens;
+            _aaveV1BorrowData.amts = data.borrowAmts;
+            _aaveV1BorrowData.borrowRateModes = data.borrowRateModes;
+            _aaveV1BorrowData.paybackRateModes = data.paybackRateModes;
+            
+            paybackAmts = _aaveV1Borrow(_aaveV1BorrowData);
+            {
+            _compPayback(length, _ctokens, tokens, paybackAmts);
+            depositAmts = _compWithdraw(length, _ctokens, tokens, data.withdrawAmts);
+            }
+
+            {
+                AaveV1DepositData memory _aaveV1DepositData;
+                
+                _aaveV1DepositData.aave = aaveV1;
+                _aaveV1DepositData.aaveCore = aaveCore;
+                _aaveV1DepositData.length = length;
+                _aaveV1DepositData.fee = data.collateralFee;
+                _aaveV1DepositData.tokens = tokens;
+                _aaveV1DepositData.amts = depositAmts;
+
+                _aaveV1Deposit(_aaveV1DepositData);
+            }
+        } else if (data.source == Protocol.Compound && data.target == Protocol.AaveV2) {
+            AaveV2BorrowData memory _aaveV2BorrowData;
+
+            _aaveV2BorrowData.aave = aaveV2;
+            _aaveV2BorrowData.length = length;
+            _aaveV2BorrowData.fee = data.debtFee;
+            _aaveV2BorrowData.target = data.source;
+            _aaveV2BorrowData.tokens = tokens;
+            _aaveV2BorrowData.ctokens = _ctokens;
+            _aaveV2BorrowData.amts = data.borrowAmts;
+            _aaveV2BorrowData.rateModes = data.borrowRateModes;
+            
+            paybackAmts = _aaveV2Borrow(_aaveV2BorrowData);
+            _compPayback(length, _ctokens, tokens, paybackAmts);
+            depositAmts = _compWithdraw(length, _ctokens, tokens, data.withdrawAmts);
+            _aaveV2Deposit(aaveV2, aaveData, length, data.collateralFee, tokens, depositAmts);
+        } else {
+            revert("invalid-options");
+        }
+    }
+}
+
+contract ConnectV2Refinance is RefinanceResolver {
+    string public name = "Refinance-v1.0";
+}
\ No newline at end of file