From 45cf2a8f3708b1f5580e0551eaeb09a87b86af4e Mon Sep 17 00:00:00 2001
From: bhavik-m <bhavikmehta2001@gmail.com>
Date: Sun, 6 Mar 2022 18:31:34 +0530
Subject: [PATCH] added connector on avalanche

---
 .../connectors/aave/v3-import/events.sol      |  13 +
 .../connectors/aave/v3-import/helpers.sol     | 279 ++++++++++++++++++
 .../connectors/aave/v3-import/interface.sol   |  94 ++++++
 .../connectors/aave/v3-import/main.sol        | 109 +++++++
 4 files changed, 495 insertions(+)
 create mode 100644 contracts/avalanche/connectors/aave/v3-import/events.sol
 create mode 100644 contracts/avalanche/connectors/aave/v3-import/helpers.sol
 create mode 100644 contracts/avalanche/connectors/aave/v3-import/interface.sol
 create mode 100644 contracts/avalanche/connectors/aave/v3-import/main.sol

diff --git a/contracts/avalanche/connectors/aave/v3-import/events.sol b/contracts/avalanche/connectors/aave/v3-import/events.sol
new file mode 100644
index 00000000..26850be7
--- /dev/null
+++ b/contracts/avalanche/connectors/aave/v3-import/events.sol
@@ -0,0 +1,13 @@
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+contract Events {
+	event LogAaveV3Import(
+		address indexed user,
+		address[] ctokens,
+		string[] supplyIds,
+		string[] borrowIds,
+		uint256[] supplyAmts,
+		uint256[] borrowAmts
+	);
+}
diff --git a/contracts/avalanche/connectors/aave/v3-import/helpers.sol b/contracts/avalanche/connectors/aave/v3-import/helpers.sol
new file mode 100644
index 00000000..272c1ee7
--- /dev/null
+++ b/contracts/avalanche/connectors/aave/v3-import/helpers.sol
@@ -0,0 +1,279 @@
+pragma solidity ^0.7.0;
+
+import { DSMath } from "../../../common/math.sol";
+import { Basic } from "../../../common/basic.sol";
+import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol";
+import { AaveInterface, AaveLendingPoolProviderInterface, AaveDataProviderInterface } from "./interface.sol";
+import "./events.sol";
+import "./interface.sol";
+
+abstract contract Helper is DSMath, Basic {
+	/**
+	 * @dev Aave referal code
+	 */
+	uint16 internal constant referalCode = 3228;
+
+	/**
+	 * @dev Aave Lending Pool Provider
+	 */
+	AaveLendingPoolProviderInterface internal constant aaveProvider =
+		AaveLendingPoolProviderInterface(
+			0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5 // v2 address TODO: need to update this
+		);
+
+	/**
+	 * @dev Aave Protocol Data Provider
+	 */
+	AaveDataProviderInterface internal constant aaveData =
+		AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d); // TODO: need to update this
+
+	function getIsColl(address token, address user)
+		internal
+		view
+		returns (bool isCol)
+	{
+		(, , , , , , , , isCol) = aaveData.getUserReserveData(token, user);
+	}
+
+	struct ImportData {
+		address[] _supplyTokens;
+		address[] _borrowTokens;
+		ATokenInterface[] aTokens;
+		uint256[] supplyAmts;
+		uint256[] variableBorrowAmts;
+		uint256[] variableBorrowAmtsWithFee;
+		uint256[] stableBorrowAmts;
+		uint256[] stableBorrowAmtsWithFee;
+		uint256[] totalBorrowAmts;
+		uint256[] totalBorrowAmtsWithFee;
+		bool convertStable;
+	}
+
+	struct ImportInputData {
+		address[] supplyTokens;
+		address[] borrowTokens;
+		bool convertStable;
+		uint256[] flashLoanFees;
+	}
+}
+
+contract AaveHelpers is Helper {
+	function getBorrowAmount(address _token, address userAccount)
+		internal
+		view
+		returns (uint256 stableBorrow, uint256 variableBorrow)
+	{
+		(
+			,
+			address stableDebtTokenAddress,
+			address variableDebtTokenAddress
+		) = aaveData.getReserveTokensAddresses(_token);
+
+		stableBorrow = ATokenInterface(stableDebtTokenAddress).balanceOf(
+			userAccount
+		);
+		variableBorrow = ATokenInterface(variableDebtTokenAddress).balanceOf(
+			userAccount
+		);
+	}
+
+	function getBorrowAmounts(
+		address userAccount,
+		AaveInterface aave,
+		ImportInputData memory inputData,
+		ImportData memory data
+	) internal returns (ImportData memory) {
+		if (inputData.borrowTokens.length > 0) {
+			data._borrowTokens = new address[](inputData.borrowTokens.length);
+			data.variableBorrowAmts = new uint256[](
+				inputData.borrowTokens.length
+			);
+			data.stableBorrowAmts = new uint256[](
+				inputData.borrowTokens.length
+			);
+			data.totalBorrowAmts = new uint256[](inputData.borrowTokens.length);
+			for (uint256 i = 0; i < inputData.borrowTokens.length; i++) {
+				for (uint256 j = i; j < inputData.borrowTokens.length; j++) {
+					if (j != i) {
+						require(
+							inputData.borrowTokens[i] !=
+								inputData.borrowTokens[j],
+							"token-repeated"
+						);
+					}
+				}
+			}
+			for (uint256 i = 0; i < inputData.borrowTokens.length; i++) {
+				address _token = inputData.borrowTokens[i] == avaxAddr
+					? wavaxAddr
+					: inputData.borrowTokens[i];
+				data._borrowTokens[i] = _token;
+
+				(
+					data.stableBorrowAmts[i],
+					data.variableBorrowAmts[i]
+				) = getBorrowAmount(_token, userAccount);
+
+				if (data.variableBorrowAmts[i] != 0) {
+					data.variableBorrowAmtsWithFee[i] = add(
+						data.variableBorrowAmts[i],
+						inputData.flashLoanFees[i]
+					);
+					data.stableBorrowAmtsWithFee[i] = data.stableBorrowAmts[i];
+				} else {
+					data.stableBorrowAmtsWithFee[i] = add(
+						data.stableBorrowAmts[i],
+						inputData.flashLoanFees[i]
+					);
+				}
+
+				data.totalBorrowAmts[i] = add(
+					data.stableBorrowAmts[i],
+					data.variableBorrowAmts[i]
+				);
+				data.totalBorrowAmtsWithFee[i] = add(
+					data.stableBorrowAmtsWithFee[i],
+					data.variableBorrowAmtsWithFee[i]
+				);
+
+				if (data.totalBorrowAmts[i] > 0) {
+					uint256 _amt = data.totalBorrowAmts[i];
+					TokenInterface(_token).approve(address(aave), _amt);
+				}
+			}
+		}
+		return data;
+	}
+
+	function getSupplyAmounts(
+		address userAccount,
+		ImportInputData memory inputData,
+		ImportData memory data
+	) internal view returns (ImportData memory) {
+		data.supplyAmts = new uint256[](inputData.supplyTokens.length);
+		data._supplyTokens = new address[](inputData.supplyTokens.length);
+		data.aTokens = new ATokenInterface[](inputData.supplyTokens.length);
+
+		for (uint256 i = 0; i < inputData.supplyTokens.length; i++) {
+			for (uint256 j = i; j < inputData.supplyTokens.length; j++) {
+				if (j != i) {
+					require(
+						inputData.supplyTokens[i] != inputData.supplyTokens[j],
+						"token-repeated"
+					);
+				}
+			}
+		}
+		for (uint256 i = 0; i < inputData.supplyTokens.length; i++) {
+			address _token = inputData.supplyTokens[i] == avaxAddr
+				? wavaxAddr
+				: inputData.supplyTokens[i];
+			(address _aToken, , ) = aaveData.getReserveTokensAddresses(_token);
+			data._supplyTokens[i] = _token;
+			data.aTokens[i] = ATokenInterface(_aToken);
+			data.supplyAmts[i] = data.aTokens[i].balanceOf(userAccount);
+		}
+
+		return data;
+	}
+
+	function _paybackBehalfOne(
+		AaveInterface aave,
+		address token,
+		uint256 amt,
+		uint256 rateMode,
+		address user
+	) private {
+		aave.repay(token, amt, rateMode, user);
+	}
+
+	function _PaybackStable(
+		uint256 _length,
+		AaveInterface aave,
+		address[] memory tokens,
+		uint256[] memory amts,
+		address user
+	) internal {
+		for (uint256 i = 0; i < _length; i++) {
+			if (amts[i] > 0) {
+				_paybackBehalfOne(aave, tokens[i], amts[i], 1, user);
+			}
+		}
+	}
+
+	function _PaybackVariable(
+		uint256 _length,
+		AaveInterface aave,
+		address[] memory tokens,
+		uint256[] memory amts,
+		address user
+	) internal {
+		for (uint256 i = 0; i < _length; i++) {
+			if (amts[i] > 0) {
+				_paybackBehalfOne(aave, tokens[i], amts[i], 2, user);
+			}
+		}
+	}
+
+	function _TransferAtokens(
+		uint256 _length,
+		AaveInterface aave,
+		ATokenInterface[] memory atokenContracts,
+		uint256[] memory amts,
+		address[] memory tokens,
+		address userAccount
+	) internal {
+		for (uint256 i = 0; i < _length; i++) {
+			if (amts[i] > 0) {
+				uint256 _amt = amts[i];
+				require(
+					atokenContracts[i].transferFrom(
+						userAccount,
+						address(this),
+						_amt
+					),
+					"allowance?"
+				);
+
+				if (!getIsColl(tokens[i], address(this))) {
+					aave.setUserUseReserveAsCollateral(tokens[i], true);
+				}
+			}
+		}
+	}
+
+	function _BorrowVariable(
+		uint256 _length,
+		AaveInterface aave,
+		address[] memory tokens,
+		uint256[] memory amts
+	) internal {
+		for (uint256 i = 0; i < _length; i++) {
+			if (amts[i] > 0) {
+				_borrowOne(aave, tokens[i], amts[i], 2);
+			}
+		}
+	}
+
+	function _BorrowStable(
+		uint256 _length,
+		AaveInterface aave,
+		address[] memory tokens,
+		uint256[] memory amts
+	) internal {
+		for (uint256 i = 0; i < _length; i++) {
+			if (amts[i] > 0) {
+				_borrowOne(aave, tokens[i], amts[i], 1);
+			}
+		}
+	}
+
+	function _borrowOne(
+		AaveInterface aave,
+		address token,
+		uint256 amt,
+		uint256 rateMode
+	) private {
+		aave.borrow(token, amt, rateMode, referalCode, address(this));
+	}
+}
diff --git a/contracts/avalanche/connectors/aave/v3-import/interface.sol b/contracts/avalanche/connectors/aave/v3-import/interface.sol
new file mode 100644
index 00000000..26a9fcaa
--- /dev/null
+++ b/contracts/avalanche/connectors/aave/v3-import/interface.sol
@@ -0,0 +1,94 @@
+pragma solidity ^0.7.0;
+
+interface AaveInterface {
+	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;
+
+	function swapBorrowRateMode(address _asset, uint256 _rateMode) external;
+}
+
+interface ATokenInterface {
+	function scaledBalanceOf(address _user) external view returns (uint256);
+
+	function isTransferAllowed(address _user, uint256 _amount)
+		external
+		view
+		returns (bool);
+
+	function balanceOf(address _user) external view returns (uint256);
+
+	function transferFrom(
+		address,
+		address,
+		uint256
+	) external returns (bool);
+
+	function allowance(address, address) external returns (uint256);
+}
+
+interface AaveLendingPoolProviderInterface {
+	function getPool() external view returns (address);
+}
+
+interface AaveDataProviderInterface {
+	function getReserveTokensAddresses(address _asset)
+		external
+		view
+		returns (
+			address aTokenAddress,
+			address stableDebtTokenAddress,
+			address variableDebtTokenAddress
+		);
+
+	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
+		);
+}
+
+interface AaveAddressProviderRegistryInterface {
+	function getAddressesProvidersList()
+		external
+		view
+		returns (address[] memory);
+}
diff --git a/contracts/avalanche/connectors/aave/v3-import/main.sol b/contracts/avalanche/connectors/aave/v3-import/main.sol
new file mode 100644
index 00000000..523adfa7
--- /dev/null
+++ b/contracts/avalanche/connectors/aave/v3-import/main.sol
@@ -0,0 +1,109 @@
+pragma solidity ^0.7.0;
+pragma experimental ABIEncoderV2;
+
+import { TokenInterface, AccountInterface } from "../../../common/interfaces.sol";
+import { AaveInterface, ATokenInterface } from "./interface.sol";
+import "./helpers.sol";
+import "./events.sol";
+
+contract AaveV3ImportResolver is AaveHelpers {
+	function _importAave(address userAccount, ImportInputData memory inputData)
+		internal
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		require(
+			AccountInterface(address(this)).isAuth(userAccount),
+			"user-account-not-auth"
+		);
+
+		require(inputData.supplyTokens.length > 0, "0-length-not-allowed");
+
+		ImportData memory data;
+
+		AaveInterface aave = AaveInterface(aaveProvider.getPool());
+
+		data = getBorrowAmounts(userAccount, aave, inputData, data);
+		data = getSupplyAmounts(userAccount, inputData, data);
+
+		//  payback borrowed amount;
+		_PaybackStable(
+			data._borrowTokens.length,
+			aave,
+			data._borrowTokens,
+			data.stableBorrowAmts,
+			userAccount
+		);
+		_PaybackVariable(
+			data._borrowTokens.length,
+			aave,
+			data._borrowTokens,
+			data.variableBorrowAmts,
+			userAccount
+		);
+
+		//  transfer atokens to this address;
+		_TransferAtokens(
+			data._supplyTokens.length,
+			aave,
+			data.aTokens,
+			data.supplyAmts,
+			data._supplyTokens,
+			userAccount
+		);
+
+		// borrow assets after migrating position
+		if (data.convertStable) {
+			_BorrowVariable(
+				data._borrowTokens.length,
+				aave,
+				data._borrowTokens,
+				data.totalBorrowAmtsWithFee
+			);
+		} else {
+			_BorrowStable(
+				data._borrowTokens.length,
+				aave,
+				data._borrowTokens,
+				data.stableBorrowAmtsWithFee
+			);
+			_BorrowVariable(
+				data._borrowTokens.length,
+				aave,
+				data._borrowTokens,
+				data.variableBorrowAmtsWithFee
+			);
+		}
+
+		_eventName = "LogAaveV3Import(address,bool,address[],address[],uint256[],uint256[],uint256[],uint256[])";
+		_eventParam = abi.encode(
+			userAccount,
+			inputData.convertStable,
+			inputData.supplyTokens,
+			inputData.borrowTokens,
+			inputData.flashLoanFees,
+			data.supplyAmts,
+			data.stableBorrowAmts,
+			data.variableBorrowAmts
+		);
+	}
+
+	function importAave(address userAccount, ImportInputData memory inputData)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		(_eventName, _eventParam) = _importAave(userAccount, inputData);
+	}
+
+	function migrateAave(ImportInputData memory inputData)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		(_eventName, _eventParam) = _importAave(msg.sender, inputData);
+	}
+}
+
+contract ConnectV2AaveV3ImportAvalanche is AaveV3ImportResolver {
+	string public constant name = "Aave-v3-import-v1";
+}