From 52c8edec062ae0e6f7856a749fd9db327a298999 Mon Sep 17 00:00:00 2001
From: q1q0 <carloslin9999@gmail.com>
Date: Thu, 11 May 2023 18:09:47 -0400
Subject: [PATCH 1/4] add spark connector

---
 contracts/mainnet/connectors/spark/events.sol |  65 +++
 .../mainnet/connectors/spark/helpers.sol      |  99 ++++
 .../mainnet/connectors/spark/interface.sol    |  87 +++
 contracts/mainnet/connectors/spark/main.sol   | 519 ++++++++++++++++++
 test/mainnet/spark/spark.test.ts              | 226 ++++++++
 5 files changed, 996 insertions(+)
 create mode 100644 contracts/mainnet/connectors/spark/events.sol
 create mode 100644 contracts/mainnet/connectors/spark/helpers.sol
 create mode 100644 contracts/mainnet/connectors/spark/interface.sol
 create mode 100644 contracts/mainnet/connectors/spark/main.sol
 create mode 100644 test/mainnet/spark/spark.test.ts

diff --git a/contracts/mainnet/connectors/spark/events.sol b/contracts/mainnet/connectors/spark/events.sol
new file mode 100644
index 00000000..fc2787f6
--- /dev/null
+++ b/contracts/mainnet/connectors/spark/events.sol
@@ -0,0 +1,65 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+
+contract Events {
+	event LogDeposit(
+		address indexed token,
+		uint256 tokenAmt,
+		uint256 getId,
+		uint256 setId
+	);
+	event LogWithdraw(
+		address indexed token,
+		uint256 tokenAmt,
+		uint256 getId,
+		uint256 setId
+	);
+	event LogBorrow(
+		address indexed token,
+		uint256 tokenAmt,
+		uint256 indexed rateMode,
+		uint256 getId,
+		uint256 setId
+	);
+	event LogPayback(
+		address indexed token,
+		uint256 tokenAmt,
+		uint256 indexed rateMode,
+		uint256 getId,
+		uint256 setId
+	);
+	event LogEnableCollateral(address[] tokens);
+	event LogSwapRateMode(address indexed token, uint256 rateMode);
+	event LogRebalanceStableBorrowRate(address indexed token, address indexed user);
+	event LogSetUserEMode(uint8 categoryId);
+	event LogDelegateBorrow(
+		address token,
+		uint256 amount,
+		uint256 rateMode,
+		address delegateTo,
+		uint256 getId,
+		uint256 setId
+	);
+	event LogDepositWithoutCollateral(
+		address token,
+		uint256 amt,
+		uint256 getId,
+		uint256 setId
+	);
+	event LogBorrowOnBehalfOf(
+		address token,
+		uint256 amt,
+		uint256 rateMode,
+		address onBehalfOf,
+		uint256 getId,
+		uint256 setId
+	);
+	event LogPaybackOnBehalfOf(
+		address token,
+		uint256 amt,
+		uint256 rateMode,
+		address onBehalfOf,
+		uint256 getId,
+		uint256 setId
+	);
+}
diff --git a/contracts/mainnet/connectors/spark/helpers.sol b/contracts/mainnet/connectors/spark/helpers.sol
new file mode 100644
index 00000000..d08495f7
--- /dev/null
+++ b/contracts/mainnet/connectors/spark/helpers.sol
@@ -0,0 +1,99 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+
+import { DSMath } from "../../common/math.sol";
+import { Basic } from "../../common/basic.sol";
+import { SparkPoolAddressesProviderInterface, AaveDataProviderInterface } from "./interface.sol";
+
+abstract contract Helpers is DSMath, Basic {
+	/**
+	 * @dev Spark Pool Provider
+	 */
+	SparkPoolAddressesProviderInterface internal constant sparkProvider =
+		SparkPoolAddressesProviderInterface(0x02C3eA4e34C0cBd694D2adFa2c690EECbC1793eE);
+
+	/**
+	 * @dev Spark Pool Data Provider
+	 */
+	AaveDataProviderInterface internal constant sparkData =
+		AaveDataProviderInterface(0x7B4EB56E7CD4b454BA8ff71E4518426369a138a3);
+
+	/**
+	 * @dev Spark Referral Code
+	 */
+	uint16 internal constant referralCode = 0;
+
+	/**
+	 * @dev Checks if collateral is enabled for an asset
+	 * @param token token address of the asset.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 */
+
+	function getIsColl(address token) internal view returns (bool isCol) {
+		(, , , , , , , , isCol) = sparkData.getUserReserveData(
+			token,
+			address(this)
+		);
+	}
+
+	/**
+	 * @dev Get total debt balance & fee for an asset
+	 * @param token token address of the debt.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param rateMode Borrow rate mode (Stable = 1, Variable = 2)
+	 */
+	function getPaybackBalance(address token, uint256 rateMode)
+		internal
+		view
+		returns (uint256)
+	{
+		(, uint256 stableDebt, uint256 variableDebt, , , , , , ) = sparkData
+			.getUserReserveData(token, address(this));
+		return rateMode == 1 ? stableDebt : variableDebt;
+	}
+
+	/**
+	 * @dev Get OnBehalfOf user's total debt balance & fee for an asset
+	 * @param token token address of the debt.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param rateMode Borrow rate mode (Stable = 1, Variable = 2)
+	 */
+	function getOnBehalfOfPaybackBalance(address token, uint256 rateMode, address onBehalfOf)
+		internal
+		view
+		returns (uint256)
+	{
+		(, uint256 stableDebt, uint256 variableDebt, , , , , , ) = sparkData
+			.getUserReserveData(token, onBehalfOf);
+		return rateMode == 1 ? stableDebt : variableDebt;
+	}
+
+	/**
+	 * @dev Get total collateral balance for an asset
+	 * @param token token address of the collateral.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 */
+	function getCollateralBalance(address token)
+		internal
+		view
+		returns (uint256 bal)
+	{
+		(bal, , , , , , , , ) = sparkData.getUserReserveData(
+			token,
+			address(this)
+		);
+	}
+
+	/**
+	 * @dev Get debt token address for an asset
+	 * @param token token address of the asset
+	 * @param rateMode Debt type: stable-1, variable-2
+	 */
+	function getDTokenAddr(address token, uint256 rateMode)
+		internal
+		view
+		returns(address dToken)
+	{
+		if (rateMode == 1) {
+			(, dToken, ) = sparkData.getReserveTokensAddresses(token);
+		} else {
+			(, , dToken) = sparkData.getReserveTokensAddresses(token);
+		}
+	}
+}
diff --git a/contracts/mainnet/connectors/spark/interface.sol b/contracts/mainnet/connectors/spark/interface.sol
new file mode 100644
index 00000000..0fb4019a
--- /dev/null
+++ b/contracts/mainnet/connectors/spark/interface.sol
@@ -0,0 +1,87 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+
+interface SparkInterface {
+	function supply(
+		address asset,
+		uint256 amount,
+		address onBehalfOf,
+		uint16 referralCode
+	) external;
+
+	function withdraw(
+		address asset,
+		uint256 amount,
+		address to
+	) external returns (uint256);
+
+	function borrow(
+		address asset,
+		uint256 amount,
+		uint256 interestRateMode,
+		uint16 referralCode,
+		address onBehalfOf
+	) external;
+
+	function repay(
+		address asset,
+		uint256 amount,
+		uint256 interestRateMode,
+		address onBehalfOf
+	) external returns (uint256);
+
+	function repayWithATokens(
+		address asset,
+		uint256 amount,
+		uint256 interestRateMode
+	) external returns (uint256);
+
+	function setUserUseReserveAsCollateral(address asset, bool useAsCollateral)
+		external;
+
+	function swapBorrowRateMode(address asset, uint256 interestRateMode)
+		external;
+
+	function setUserEMode(uint8 categoryId) external;
+
+	function rebalanceStableBorrowRate(address asset, address user) external;
+}
+
+interface SparkPoolAddressesProviderInterface {
+	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
+		);
+
+	function getReserveEModeCategory(address asset)
+		external
+		view
+		returns (uint256);
+}
+
+interface DTokenInterface {
+	function approveDelegation(address delegatee, uint256 amount) external;
+}
diff --git a/contracts/mainnet/connectors/spark/main.sol b/contracts/mainnet/connectors/spark/main.sol
new file mode 100644
index 00000000..15cc7893
--- /dev/null
+++ b/contracts/mainnet/connectors/spark/main.sol
@@ -0,0 +1,519 @@
+//SPDX-License-Identifier: MIT
+pragma solidity ^0.7.0;
+
+/**
+ * @title Sparklend.
+ * @dev Lending & Borrowing.
+ */
+
+import { TokenInterface } from "../../common/interfaces.sol";
+import { Stores } from "../../common/stores.sol";
+import { Helpers } from "./helpers.sol";
+import { Events } from "./events.sol";
+import { SparkInterface, DTokenInterface } from "./interface.sol";
+
+import "hardhat/console.sol";
+
+abstract contract SparkResolver is Events, Helpers {
+	/**
+	 * @dev Deposit ETH/ERC20_Token.
+	 * @notice Deposit a token to Sparklend for lending / collaterization.
+	 * @param token The address of the token to deposit.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param amt The amount of the token to deposit. (For max: `uint256(-1)`)
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens deposited.
+	 */
+	function deposit(
+		address token,
+		uint256 amt,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _amt = getUint(getId, amt);
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		TokenInterface tokenContract = TokenInterface(_token);
+
+		if (isEth) {
+			_amt = _amt == uint256(-1) ? address(this).balance : _amt;
+			convertEthToWeth(isEth, tokenContract, _amt);
+		} else {
+			_amt = _amt == uint256(-1)
+				? tokenContract.balanceOf(address(this))
+				: _amt;
+		}
+
+		approve(tokenContract, address(spark), _amt);
+
+		spark.supply(_token, _amt, address(this), referralCode);
+
+		if (!getIsColl(_token)) {
+			spark.setUserUseReserveAsCollateral(_token, true);
+		}
+
+		setUint(setId, _amt);
+
+		_eventName = "LogDeposit(address,uint256,uint256,uint256)";
+		_eventParam = abi.encode(token, _amt, getId, setId);
+	}
+
+	/**
+	 * @dev Deposit ETH/ERC20_Token without collateral
+	 * @notice Deposit a token to Sparklend without enabling it as collateral.
+	 * @param token The address of the token to deposit.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param amt The amount of the token to deposit. (For max: `uint256(-1)`)
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens deposited.
+	 */
+	function depositWithoutCollateral(
+		address token,
+		uint256 amt,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _amt = getUint(getId, amt);
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		TokenInterface tokenContract = TokenInterface(_token);
+
+		if (isEth) {
+			_amt = _amt == uint256(-1) ? address(this).balance : _amt;
+			convertEthToWeth(isEth, tokenContract, _amt);
+		} else {
+			_amt = _amt == uint256(-1)
+				? tokenContract.balanceOf(address(this))
+				: _amt;
+		}
+
+		approve(tokenContract, address(spark), _amt);
+
+		spark.supply(_token, _amt, address(this), referralCode);
+
+		if (getCollateralBalance(_token) > 0 && getIsColl(_token)) {
+			spark.setUserUseReserveAsCollateral(_token, false);
+		}
+
+		setUint(setId, _amt);
+
+		_eventName = "LogDepositWithoutCollateral(address,uint256,uint256,uint256)";
+		_eventParam = abi.encode(token, _amt, getId, setId);
+	}
+
+	/**
+	 * @dev Withdraw ETH/ERC20_Token.
+	 * @notice Withdraw deposited token from Sparklend
+	 * @param token The address of the token to withdraw.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`)
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens withdrawn.
+	 */
+	function withdraw(
+		address token,
+		uint256 amt,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _amt = getUint(getId, amt);
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		TokenInterface tokenContract = TokenInterface(_token);
+
+		uint256 initialBal = tokenContract.balanceOf(address(this));
+		spark.withdraw(_token, _amt, address(this));
+		uint256 finalBal = tokenContract.balanceOf(address(this));
+
+		_amt = sub(finalBal, initialBal);
+
+		convertWethToEth(isEth, tokenContract, _amt);
+
+		setUint(setId, _amt);
+
+		_eventName = "LogWithdraw(address,uint256,uint256,uint256)";
+		_eventParam = abi.encode(token, _amt, getId, setId);
+	}
+
+	/**
+	 * @dev Borrow ETH/ERC20_Token.
+	 * @notice Borrow a token using Sparklend
+	 * @param token The address of the token to borrow.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param amt The amount of the token to borrow.
+	 * @param rateMode The type of debt. (For Stable: 1, Variable: 2)
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens borrowed.
+	 */
+	function borrow(
+		address token,
+		uint256 amt,
+		uint256 rateMode,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _amt = getUint(getId, amt);
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		spark.borrow(_token, _amt, rateMode, referralCode, address(this));
+		convertWethToEth(isEth, TokenInterface(_token), _amt);
+
+		setUint(setId, _amt);
+
+		_eventName = "LogBorrow(address,uint256,uint256,uint256,uint256)";
+		_eventParam = abi.encode(token, _amt, rateMode, getId, setId);
+	}
+
+	/**
+	 * @dev Borrow ETH/ERC20_Token on behalf of a user.
+	 * @notice Borrow a token using Sparklend on behalf of a user
+	 * @param token The address of the token to borrow.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param amt The amount of the token to borrow.
+	 * @param rateMode The type of debt. (For Stable: 1, Variable: 2)
+	 * @param onBehalfOf The user who will incur the debt
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens borrowed.
+	 */
+	function borrowOnBehalfOf(
+		address token,
+		uint256 amt,
+		uint256 rateMode,
+		address onBehalfOf,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _amt = getUint(getId, amt);
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		spark.borrow(_token, _amt, rateMode, referralCode, onBehalfOf);
+		convertWethToEth(isEth, TokenInterface(_token), _amt);
+
+		setUint(setId, _amt);
+
+		_eventName = "LogBorrowOnBehalfOf(address,uint256,uint256,address,uint256,uint256)";
+		_eventParam = abi.encode(
+			token,
+			_amt,
+			rateMode,
+			onBehalfOf,
+			getId,
+			setId
+		);
+	}
+
+	/**
+	 * @dev Payback borrowed ETH/ERC20_Token.
+	 * @notice Payback debt owed.
+	 * @param token The address of the token to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param amt The amount of the token to payback. (For max: `uint256(-1)`)
+	 * @param rateMode The type of debt paying back. (For Stable: 1, Variable: 2)
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens paid back.
+	 */
+	function payback(
+		address token,
+		uint256 amt,
+		uint256 rateMode,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _amt = getUint(getId, amt);
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		TokenInterface tokenContract = TokenInterface(_token);
+		console.log(_amt);
+		_amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt;
+		console.log(_amt);
+
+		if (isEth) convertEthToWeth(isEth, tokenContract, _amt);
+
+		approve(tokenContract, address(spark), _amt);
+
+		spark.repay(_token, _amt, rateMode, address(this));
+
+		setUint(setId, _amt);
+
+		_eventName = "LogPayback(address,uint256,uint256,uint256,uint256)";
+		_eventParam = abi.encode(token, _amt, rateMode, getId, setId);
+	}
+
+	/**
+	 * @dev Payback borrowed ETH/ERC20_Token using aTokens.
+	 * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the equivalent debt tokens.
+	 * @param token The address of the token to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param amt The amount of the token to payback. (For max: `uint256(-1)`)
+	 * @param rateMode The type of debt paying back. (For Stable: 1, Variable: 2)
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens paid back.
+	 */
+	function paybackWithATokens(
+		address token,
+		uint256 amt,
+		uint256 rateMode,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _amt = getUint(getId, amt);
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		TokenInterface tokenContract = TokenInterface(_token);
+
+		_amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt;
+
+		if (isEth) convertEthToWeth(isEth, tokenContract, _amt);
+
+		approve(tokenContract, address(spark), _amt);
+
+		spark.repayWithATokens(_token, _amt, rateMode);
+
+		setUint(setId, _amt);
+
+		_eventName = "LogPayback(address,uint256,uint256,uint256,uint256)";
+		_eventParam = abi.encode(token, _amt, rateMode, getId, setId);
+	}
+
+	/**
+	 * @dev Payback borrowed ETH/ERC20_Token on behalf of a user.
+	 * @notice Payback debt owed on behalf os a user.
+	 * @param token The address of the token to payback.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param amt The amount of the token to payback. (For max: `uint256(-1)`)
+	 * @param rateMode The type of debt paying back. (For Stable: 1, Variable: 2)
+	 * @param onBehalfOf Address of user who's debt to repay.
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens paid back.
+	 */
+	function paybackOnBehalfOf(
+		address token,
+		uint256 amt,
+		uint256 rateMode,
+		address onBehalfOf,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _amt = getUint(getId, amt);
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		TokenInterface tokenContract = TokenInterface(_token);
+
+		_amt = _amt == uint256(-1)
+			? getOnBehalfOfPaybackBalance(_token, rateMode, onBehalfOf)
+			: _amt;
+
+		if (isEth) convertEthToWeth(isEth, tokenContract, _amt);
+
+		approve(tokenContract, address(spark), _amt);
+
+		spark.repay(_token, _amt, rateMode, onBehalfOf);
+
+		setUint(setId, _amt);
+
+		_eventName = "LogPaybackOnBehalfOf(address,uint256,uint256,address,uint256,uint256)";
+		_eventParam = abi.encode(
+			token,
+			_amt,
+			rateMode,
+			onBehalfOf,
+			getId,
+			setId
+		);
+	}
+
+	/**
+	 * @dev Enable collateral
+	 * @notice Enable an array of tokens as collateral
+	 * @param tokens Array of tokens to enable collateral
+	 */
+	function enableCollateral(address[] calldata tokens)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		uint256 _length = tokens.length;
+		require(_length > 0, "0-tokens-not-allowed");
+
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		for (uint256 i = 0; i < _length; i++) {
+			bool isEth = tokens[i] == ethAddr;
+			address _token = isEth ? wethAddr : tokens[i];
+
+			if (getCollateralBalance(_token) > 0 && !getIsColl(_token)) {
+				spark.setUserUseReserveAsCollateral(_token, true);
+			}
+		}
+
+		_eventName = "LogEnableCollateral(address[])";
+		_eventParam = abi.encode(tokens);
+	}
+
+	/**
+	 * @dev Swap borrow rate mode
+	 * @notice Swaps user borrow rate mode between variable and stable
+	 * @param token The address of the token to swap borrow rate.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param rateMode Current rate mode. (Stable = 1, Variable = 2)
+	 */
+	function swapBorrowRateMode(address token, uint256 rateMode)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		if (getPaybackBalance(_token, rateMode) > 0) {
+			spark.swapBorrowRateMode(_token, rateMode);
+		}
+
+		_eventName = "LogSwapRateMode(address,uint256)";
+		_eventParam = abi.encode(token, rateMode);
+	}
+
+	/**
+	 * @dev Rebalances stable borrow rate of the user for given token
+	 * @notice Swaps user borrow rate mode between variable and stable
+	 * @param token Address of the underlying token that has been borrowed for which the position is being rebalanced.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
+	 * @param user Address of the user being rebalanced.
+	 */
+	function rebalanceStableBorrowRate(address token, address user)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		spark.rebalanceStableBorrowRate(_token, user);
+
+		_eventName = "LogRebalanceStableBorrowRate(address,address)";
+		_eventParam = abi.encode(token, user);
+	}
+
+	/**
+	 * @dev Set user e-mode
+	 * @notice Updates the user's e-mode category
+	 * @param categoryId The category Id of the e-mode user want to set
+	 */
+	function setUserEMode(uint8 categoryId)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		SparkInterface spark = SparkInterface(sparkProvider.getPool());
+
+		spark.setUserEMode(categoryId);
+
+		_eventName = "LogSetUserEMode(uint8)";
+		_eventParam = abi.encode(categoryId);
+	}
+
+	/**
+	 * @dev Approve Delegation
+	 * @notice Gives approval to delegate debt tokens
+	 * @param token The address of token
+	 * @param amount The amount
+	 * @param rateMode The type of debt
+	 * @param delegateTo The address to whom the user is delegating
+	 * @param getId ID to retrieve amt.
+	 * @param setId ID stores the amount of tokens delegated.
+	 */
+	function delegateBorrow(
+		address token,
+		uint256 amount,
+		uint256 rateMode,
+		address delegateTo,
+		uint256 getId,
+		uint256 setId
+	)
+		external
+		payable
+		returns (string memory _eventName, bytes memory _eventParam)
+	{
+		require(rateMode == 1 || rateMode == 2, "Invalid debt type");
+		uint256 _amt = getUint(getId, amount);
+
+		bool isEth = token == ethAddr;
+		address _token = isEth ? wethAddr : token;
+
+		address _dToken = getDTokenAddr(_token, rateMode);
+		DTokenInterface(_dToken).approveDelegation(delegateTo, _amt);
+
+		setUint(setId, _amt);
+
+		_eventName = "LogDelegateBorrow(address,uint256,uint256,address,uint256,uint256)";
+		_eventParam = abi.encode(
+			token,
+			_amt,
+			rateMode,
+			delegateTo,
+			getId,
+			setId
+		);
+	}
+}
+
+contract ConnectV2Spark is SparkResolver {
+	string public constant name = "Spark-v1.0";
+}
diff --git a/test/mainnet/spark/spark.test.ts b/test/mainnet/spark/spark.test.ts
new file mode 100644
index 00000000..9511219e
--- /dev/null
+++ b/test/mainnet/spark/spark.test.ts
@@ -0,0 +1,226 @@
+import { expect } from "chai";
+import hre from "hardhat";
+import { abis } from "../../../scripts/constant/abis";
+import { addresses } from "../../../scripts/tests/mainnet/addresses";
+import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector";
+import { getMasterSigner } from "../../../scripts/tests/getMasterSigner";
+import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2";
+import { ConnectV2Spark__factory } from "../../../typechain";
+import { parseEther } from "@ethersproject/units";
+import { encodeSpells } from "../../../scripts/tests/encodeSpells";
+import { tokens } from "../../../scripts/tests/mainnet/tokens";
+import { constants } from "../../../scripts/constant/constant";
+import { addLiquidity } from "../../../scripts/tests/addLiquidity";
+const { ethers } = hre;
+import type { Signer, Contract } from "ethers";
+
+describe("Sparklend", function () {
+  const connectorName = "SPARK-TEST-A";
+  let connector: any;
+
+  let wallet0: Signer, wallet1:Signer;
+  let dsaWallet0: any;
+  let instaConnectorsV2: Contract;
+  let masterSigner: Signer;
+  before(async () => {
+    await hre.network.provider.request({
+      method: "hardhat_reset",
+      params: [
+        {
+          forking: {
+            // @ts-ignore
+            jsonRpcUrl: hre.config.networks.hardhat.forking.url,
+            // blockNumber: 12796965,
+          },
+        },
+      ],
+    });
+    [wallet0, wallet1] = await ethers.getSigners();
+    masterSigner = await getMasterSigner();
+    instaConnectorsV2 = await ethers.getContractAt(
+      abis.core.connectorsV2,
+      addresses.core.connectorsV2
+    );
+    connector = await deployAndEnableConnector({
+      connectorName,
+      contractArtifact: ConnectV2Spark__factory,
+      signer: masterSigner,
+      connectors: instaConnectorsV2,
+    });
+    console.log("Connector address", connector.address);
+  });
+
+  it("should have contracts deployed", async () => {
+    expect(!!instaConnectorsV2.address).to.be.true;
+    expect(!!connector.address).to.be.true;
+    expect(!!(await masterSigner.getAddress())).to.be.true;
+  });
+
+  describe("DSA wallet setup", function () {
+    it("Should build DSA v2", async function () {
+      dsaWallet0 = await buildDSAv2(wallet0.getAddress());
+      expect(!!dsaWallet0.address).to.be.true;
+    });
+
+    it("Deposit ETH into DSA wallet", async function () {
+      await wallet0.sendTransaction({
+        to: dsaWallet0.address,
+        value: parseEther("10"),
+      });
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(
+        parseEther("10")
+      );
+    });
+  });
+
+  describe("Main", function () {
+    it("should deposit ETH in Sparklend", async function () {
+      const amt = parseEther("1");
+      const spells = [
+        {
+          connector: connectorName,
+          method: "deposit",
+          args: [tokens.eth.address, amt, 0, 0],
+        },
+      ];
+
+      const tx = await dsaWallet0
+        .connect(wallet0)
+        .cast(...encodeSpells(spells), wallet1.getAddress());
+
+      await tx.wait();
+
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq(
+        parseEther("9")
+      );
+    });
+
+    it("Should borrow and payback DAI from Sparklend", async function () {
+      const amt = parseEther("100"); // 100 DAI
+      const setId = "83478237";
+      const spells = [
+        {
+          connector: connectorName,
+          method: "borrow",
+          args: [tokens.dai.address, amt, 2, 0, setId],
+        },
+        {
+          connector: connectorName,
+          method: "payback",
+          args: [tokens.dai.address, amt, 2, setId, 0],
+        },
+      ];
+
+      const tx = await dsaWallet0
+        .connect(wallet0)
+        .cast(...encodeSpells(spells), wallet1.getAddress());
+      await tx.wait();
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(
+        ethers.utils.parseEther("9")
+      );
+    });
+
+    it("Should borrow and payback half DAI from Sparklend", async function () {
+      const amt = parseEther("100"); // 100 DAI
+      // const setId = "83478237";
+      await addLiquidity("dai", dsaWallet0.address, parseEther("1"));
+      let spells = [
+        {
+          connector: connectorName,
+          method: "borrow",
+          args: [tokens.dai.address, amt, 2, 0, 0],
+        },
+        {
+          connector: connectorName,
+          method: "payback",
+          args: [tokens.dai.address, amt.div(2), 2, 0, 0],
+        },
+      ];
+
+      let tx = await dsaWallet0
+        .connect(wallet0)
+        .cast(...encodeSpells(spells), wallet1.getAddress());
+      await tx.wait();
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(
+        ethers.utils.parseEther("9")
+      );
+
+      spells = [
+        {
+          connector: connectorName,
+          method: "payback",
+          args: [tokens.dai.address, constants.max_value, 2, 0, 0],
+        },
+      ];
+
+      tx = await dsaWallet0
+        .connect(wallet0)
+        .cast(...encodeSpells(spells), wallet1.getAddress());
+      await tx.wait();
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(
+        ethers.utils.parseEther("9")
+      );
+    });
+
+    it("Should deposit all ETH in Sparklend", async function () {
+      const spells = [
+        {
+          connector: connectorName,
+          method: "deposit",
+          args: [tokens.eth.address, constants.max_value, 0, 0],
+        },
+      ];
+
+      const tx = await dsaWallet0
+        .connect(wallet0)
+        .cast(...encodeSpells(spells), wallet1.getAddress());
+      await tx.wait();
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(
+        ethers.utils.parseEther("0")
+      );
+    });
+
+    it("Should withdraw all ETH from Sparklend", async function () {
+      const spells = [
+        {
+          connector: connectorName,
+          method: "withdraw",
+          args: [tokens.eth.address, constants.max_value, 0, 0],
+        },
+      ];
+
+      const tx = await dsaWallet0
+        .connect(wallet0)
+        .cast(...encodeSpells(spells), wallet1.getAddress());
+      await tx.wait();
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(
+        ethers.utils.parseEther("10")
+      );
+    });
+
+    it("should deposit and withdraw", async () => {
+      const amt = parseEther("1"); // 1 eth
+      const setId = "834782373";
+      const spells = [
+        {
+          connector: connectorName,
+          method: "deposit",
+          args: [tokens.eth.address, amt, 0, setId],
+        },
+        {
+          connector: connectorName,
+          method: "withdraw",
+          args: [tokens.eth.address, amt, setId, 0],
+        },
+      ];
+
+      const tx = await dsaWallet0
+        .connect(wallet0)
+        .cast(...encodeSpells(spells), wallet1.getAddress());
+      await tx.wait();
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(
+        ethers.utils.parseEther("10")
+      );
+    });
+  });
+});

From 04c60c20cfb8da3afbcbcf8751159d25b3290a11 Mon Sep 17 00:00:00 2001
From: q1q0 <carloslin9999@gmail.com>
Date: Thu, 11 May 2023 19:50:04 -0400
Subject: [PATCH 2/4] update address

---
 contracts/mainnet/connectors/spark/helpers.sol | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/contracts/mainnet/connectors/spark/helpers.sol b/contracts/mainnet/connectors/spark/helpers.sol
index d08495f7..4d511140 100644
--- a/contracts/mainnet/connectors/spark/helpers.sol
+++ b/contracts/mainnet/connectors/spark/helpers.sol
@@ -16,7 +16,7 @@ abstract contract Helpers is DSMath, Basic {
 	 * @dev Spark Pool Data Provider
 	 */
 	AaveDataProviderInterface internal constant sparkData =
-		AaveDataProviderInterface(0x7B4EB56E7CD4b454BA8ff71E4518426369a138a3);
+		AaveDataProviderInterface(0xFc21d6d146E6086B8359705C8b28512a983db0cb);
 
 	/**
 	 * @dev Spark Referral Code

From 86741c7afdff7c48165c81faa4fc8e21330cd4c8 Mon Sep 17 00:00:00 2001
From: q1q0 <carloslin9999@gmail.com>
Date: Fri, 12 May 2023 05:55:37 -0400
Subject: [PATCH 3/4] update test script

---
 test/mainnet/spark/spark.test.ts | 75 ++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/test/mainnet/spark/spark.test.ts b/test/mainnet/spark/spark.test.ts
index 9511219e..908cf18b 100644
--- a/test/mainnet/spark/spark.test.ts
+++ b/test/mainnet/spark/spark.test.ts
@@ -22,6 +22,18 @@ describe("Sparklend", function () {
   let dsaWallet0: any;
   let instaConnectorsV2: Contract;
   let masterSigner: Signer;
+  const account = "0x72a53cdbbcc1b9efa39c834a540550e23463aacb";
+  let signer: any;
+
+  const ABI = [
+    "function balanceOf(address account) public view returns (uint256)",
+    "function approve(address spender, uint256 amount) external returns(bool)",
+    "function transfer(address recipient, uint256 amount) external returns (bool)"
+  ];
+
+  const wethContract = new ethers.Contract(tokens.weth.address, ABI);
+  const daiContract = new ethers.Contract(tokens.dai.address, ABI);
+
   before(async () => {
     await hre.network.provider.request({
       method: "hardhat_reset",
@@ -48,6 +60,12 @@ describe("Sparklend", function () {
       connectors: instaConnectorsV2,
     });
     console.log("Connector address", connector.address);
+
+    await hre.network.provider.request({
+      method: "hardhat_impersonateAccount",
+      params: [account]
+    });
+    signer = await ethers.getSigner(account);
   });
 
   it("should have contracts deployed", async () => {
@@ -222,5 +240,62 @@ describe("Sparklend", function () {
         ethers.utils.parseEther("10")
       );
     });
+
+    it("should deposit without collateral and withdraw", async () => {
+      const amt = parseEther("1"); // 1 eth
+      const setId = "834782373";
+      const spells = [
+        {
+          connector: connectorName,
+          method: "depositWithoutCollateral",
+          args: [tokens.eth.address, amt, 0, setId],
+        },
+        {
+          connector: connectorName,
+          method: "withdraw",
+          args: [tokens.eth.address, amt, setId, 0],
+        },
+      ];
+
+      const tx = await dsaWallet0
+        .connect(wallet0)
+        .cast(...encodeSpells(spells), wallet1.getAddress());
+      await tx.wait();
+      expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(
+        ethers.utils.parseEther("10")
+      );
+    });
+
+    // it("should deposit ETH in Sparklend", async function () {
+    //   const amt = parseEther("1");
+    //   const spells = [
+    //     {
+    //       connector: connectorName,
+    //       method: "deposit",
+    //       args: [tokens.eth.address, amt, 0, 0],
+    //     },
+    //     {
+    //       connector: connectorName,
+    //       method: "borrow",
+    //       args: [tokens.dai.address, amt.mul(300), 2, 0, 0],
+    //     },
+    //     {
+    //       connector: connectorName,
+    //       method: "paybackWithATokens",
+    //       args: [tokens.dai.address, amt.mul(300), 2, 0, 0],
+    //     },
+    //   ];
+
+    //   const tx = await dsaWallet0
+    //     .connect(wallet0)
+    //     .cast(...encodeSpells(spells), wallet1.getAddress());
+
+    //   await tx.wait();
+
+    //   expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq(
+    //     parseEther("9")
+    //   );
+    // });
+
   });
 });

From 7f95f03611d2efea29f36d78f5c2d73b793e5477 Mon Sep 17 00:00:00 2001
From: Shriya Tyagi <shriya243@gmail.com>
Date: Sun, 14 May 2023 17:36:31 +0400
Subject: [PATCH 4/4] Remove console

---
 contracts/mainnet/connectors/spark/main.sol | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/contracts/mainnet/connectors/spark/main.sol b/contracts/mainnet/connectors/spark/main.sol
index 15cc7893..6295641d 100644
--- a/contracts/mainnet/connectors/spark/main.sol
+++ b/contracts/mainnet/connectors/spark/main.sol
@@ -12,9 +12,7 @@ import { Helpers } from "./helpers.sol";
 import { Events } from "./events.sol";
 import { SparkInterface, DTokenInterface } from "./interface.sol";
 
-import "hardhat/console.sol";
-
-abstract contract SparkResolver is Events, Helpers {
+abstract contract SparkConnector is Events, Helpers {
 	/**
 	 * @dev Deposit ETH/ERC20_Token.
 	 * @notice Deposit a token to Sparklend for lending / collaterization.
@@ -264,9 +262,7 @@ abstract contract SparkResolver is Events, Helpers {
 		address _token = isEth ? wethAddr : token;
 
 		TokenInterface tokenContract = TokenInterface(_token);
-		console.log(_amt);
 		_amt = _amt == uint256(-1) ? getPaybackBalance(_token, rateMode) : _amt;
-		console.log(_amt);
 
 		if (isEth) convertEthToWeth(isEth, tokenContract, _amt);
 
@@ -514,6 +510,6 @@ abstract contract SparkResolver is Events, Helpers {
 	}
 }
 
-contract ConnectV2Spark is SparkResolver {
+contract ConnectV2Spark is SparkConnector {
 	string public constant name = "Spark-v1.0";
 }