Merge pull request #2 from Instadapp/aave-polygon-updates

Major updates in Migration
This commit is contained in:
Sowmay Jain 2021-04-11 03:42:53 +05:30 committed by GitHub
commit 5c65053570
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 4735 additions and 15843 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.sol linguist-language=Solidity

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"solidity.compileUsingRemoteVersion": "v0.7.0+commit.9e61f92b"
}

View File

@ -0,0 +1,13 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
contract Events {
event LogMigrateAaveV2(
address indexed owner,
address[] supplyTokens,
address[] borrowTokens,
uint[] supplyAmts,
uint[] variableBorrowAmts,
uint[] stableBorrowAmts
);
}

View File

@ -0,0 +1,31 @@
pragma solidity ^0.7.0;
import { DSMath } from "../../common/math.sol";
import { Stores } from "../../common/stores-polygon.sol";
import {
AaveLendingPoolProviderInterface,
AaveDataProviderInterface,
AaveInterface
} from "./interfaces.sol";
abstract contract Helpers is DSMath, Stores {
/**
* @dev Aave referal code
*/
uint16 constant internal referralCode = 3228;
/**
* @dev Aave Provider (TODO - Replace the address)
*/
AaveLendingPoolProviderInterface constant internal aaveProvider = AaveLendingPoolProviderInterface(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5);
/**
* @dev Aave Data Provider (TODO - Replace the address)
*/
AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d);
function getIsColl(address token) internal view returns (bool isCol) {
(, , , , , , , , isCol) = aaveData.getUserReserveData(token, address(this));
}
}

View File

@ -0,0 +1,86 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
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 getUserAccountData(address user) external view returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}
interface AaveLendingPoolProviderInterface {
function getLendingPool() external view returns (address);
}
// Aave Protocol Data Provider
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 getReserveConfigurationData(address asset) external view returns (
uint256 decimals,
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
uint256 reserveFactor,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive,
bool isFrozen
);
}
interface AaveAddressProviderRegistryInterface {
function getAddressesProvidersList() external view returns (address[] memory);
}
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, uint) external returns (bool);
function approve(address, uint256) external;
}
struct AaveData {
bool isFinal;
address targetDsa;
uint[] supplyAmts;
uint[] variableBorrowAmts;
uint[] stableBorrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}
interface ReceiverInterface {
function getPosition(address) external view returns (AaveData memory);
}

View File

@ -0,0 +1,55 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { TokenInterface, AccountInterface } from "../../common/interfaces.sol";
import { AaveInterface, ReceiverInterface, AaveData } from "./interfaces.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
contract AaveMigratorResolver is Helpers, Events {
using SafeERC20 for IERC20;
ReceiverInterface public immutable receiver;
constructor(address _receiver) {
receiver = ReceiverInterface(_receiver);
}
function migrateAave(address owner) external payable returns (bytes32) {
require(msg.sender == address(receiver) && AccountInterface(address(this)).isAuth(owner), "not-authorized");
AaveData memory data = receiver.getPosition(owner);
require(!data.isFinal, "already-migrated");
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
for (uint i = 0; i < data.supplyTokens.length; i++) {
TokenInterface token = TokenInterface(data.supplyTokens[i]);
uint amt = data.supplyAmts[i];
token.approve(address(aave), amt);
aave.deposit(address(token), amt, address(this), referralCode);
if (!getIsColl(address(token))) {
aave.setUserUseReserveAsCollateral(address(token), true);
}
}
for (uint i = 0; i < data.borrowTokens.length; i++) {
address token = data.borrowTokens[i];
uint variableAmt = data.variableBorrowAmts[i];
uint stableAmt = data.stableBorrowAmts[i];
if (variableAmt > 0) {
aave.borrow(token, variableAmt, 2, referralCode, address(this));
}
if (stableAmt > 0) {
aave.borrow(token, stableAmt, 1, referralCode, address(this));
}
uint totalAmt = add(variableAmt, stableAmt);
IERC20(token).safeTransfer(address(receiver), totalAmt);
}
}
}

View File

@ -0,0 +1,16 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
contract Events {
event LogDeposit(
address owner,
address[] tokens,
uint[] amts
);
event LogWithdraw(
address owner,
address[] tokens,
uint[] amts
);
}

View File

@ -0,0 +1,21 @@
pragma solidity ^0.7.0;
import { DSMath } from "../../common/math.sol";
import { TokenMappingInterface, AaveData } from "./interfaces.sol";
abstract contract Helpers is DSMath {
// Replace this
TokenMappingInterface tokenMapping = TokenMappingInterface(address(2));
function remapTokens(AaveData memory data) internal returns (AaveData memory) {
for (uint i = 0; i < data.supplyTokens.length; i++) {
data.supplyTokens[i] = tokenMapping.getMapping(data.supplyTokens[i]);
}
for (uint i = 0; i < data.borrowTokens.length; i++) {
data.borrowTokens[i] = tokenMapping.getMapping(data.borrowTokens[i]);
}
return data;
}
}

View File

@ -0,0 +1,28 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
interface AccountInterface {
function enable(address) external;
function disable(address) external;
function isAuth(address) external view returns (bool);
function cast(
string[] calldata _targets,
bytes[] calldata _datas,
address _origin
) external payable returns (bytes32);
function migrateAave(address) external payable returns (bytes32);
}
interface TokenMappingInterface {
function getMapping(address) external view returns (address);
}
struct AaveData {
bool isFinal;
address targetDsa;
uint[] supplyAmts;
uint[] variableBorrowAmts;
uint[] stableBorrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}

View File

@ -0,0 +1,115 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { AccountInterface, AaveData } from "./interfaces.sol";
import { Events } from "./events.sol";
import { Helpers } from "./helpers.sol";
contract MigrateResolver is Helpers, Events {
using SafeERC20 for IERC20;
mapping (address => AaveData) public positions;
mapping(address => mapping(address => uint)) deposits;
function deposit(address[] calldata tokens, uint[] calldata amts) external {
uint _length = tokens.length;
require(_length == amts.length, "invalid-length");
uint[] memory _amts = new uint[](_length);
for (uint256 i = 0; i < _length; i++) {
address _token = tokens[i];
IERC20 tokenContract = IERC20(_token);
uint _amt = amts[i] == uint(-1) ? tokenContract.balanceOf(msg.sender) : amts[i];
tokenContract.safeTransferFrom(msg.sender, address(this), _amt);
deposits[msg.sender][_token] = _amt;
_amts[i] = _amt;
}
emit LogDeposit(msg.sender, tokens, _amts);
}
function withdraw(address[] calldata tokens, uint[] calldata amts) external {
uint _length = tokens.length;
require(_length == amts.length, "invalid-length");
uint[] memory _amts = new uint[](_length);
for (uint256 i = 0; i < _length; i++) {
uint _amt = amts[i];
address _token = tokens[i];
uint maxAmt = deposits[msg.sender][_token];
if (_amt > maxAmt) {
_amt = maxAmt;
}
IERC20(_token).safeTransfer(msg.sender, _amt);
deposits[msg.sender][_token] = sub(maxAmt, _amt);
_amts[i] = _amt;
}
emit LogWithdraw(msg.sender, tokens, _amts);
}
}
contract AaveV2Migrator is MigrateResolver {
using SafeERC20 for IERC20;
uint private lastStateId;
function _migratePosition(address owner) internal {
AaveData storage data = positions[owner];
require(!data.isFinal, "already-migrated");
for (uint i = 0; i < data.supplyTokens.length; i++) {
IERC20(data.supplyTokens[i]).safeTransfer(data.targetDsa, data.supplyAmts[i]);
}
AccountInterface(data.targetDsa).migrateAave(owner);
data.isFinal = true;
}
function getPosition(address owner) public view returns (AaveData memory data) {
data = positions[owner];
}
function canMigrate(address owner) public view returns (bool can) {
can = true;
AaveData memory data = getPosition(owner);
for (uint i = 0; i < data.supplyTokens.length; i++) {
IERC20 token = IERC20(data.supplyTokens[i]);
if (token.balanceOf(address(this)) < data.supplyAmts[i]) {
can = false;
}
}
}
function onStateReceive(uint256 stateId, bytes calldata receivedData) external {
// require(stateId > lastStateId, "wrong-data");
lastStateId = stateId;
(address owner, AaveData memory data) = abi.decode(receivedData, (address, AaveData));
positions[owner] = remapTokens(data);
if (canMigrate(owner)) {
_migratePosition(owner);
}
}
function migrate(address owner) external {
require(canMigrate(owner), "not-enough-liquidity");
_migratePosition(owner);
}
}

View File

@ -0,0 +1,10 @@
pragma solidity ^0.7.0;
contract Events {
event LogAaveV2Migrate(
address indexed user,
address indexed targetDsa,
address[] supplyTokens,
address[] borrowTokens
);
}

View File

@ -0,0 +1,15 @@
pragma solidity ^0.7.0;
import { DSMath } from "../../common/math.sol";
import { Stores } from "../../common/stores-mainnet.sol";
import { AaveLendingPoolProviderInterface, AaveDataProviderInterface, AaveMigratorInterface } from "./interfaces.sol";
abstract contract Helpers is DSMath, Stores {
AaveMigratorInterface constant internal migrator = AaveMigratorInterface(address(2)); // Replace this (Migrator contract)
/**
* @dev Aave Data Provider
*/
AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d);
}

View File

@ -0,0 +1,87 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
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 getUserAccountData(address user) external view returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}
interface AaveLendingPoolProviderInterface {
function getLendingPool() external view returns (address);
}
// Aave Protocol Data Provider
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 getReserveConfigurationData(address asset) external view returns (
uint256 decimals,
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
uint256 reserveFactor,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive,
bool isFrozen
);
}
interface AaveAddressProviderRegistryInterface {
function getAddressesProvidersList() external view returns (address[] memory);
}
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, uint) external returns (bool);
function approve(address, uint256) external;
}
interface AaveMigratorInterface {
function migrate(address, address, address[] calldata, address[] calldata) external;
function migrate(address, AaveData memory) external;
}
struct AaveData {
bool isFinal;
address targetDsa;
uint[] supplyAmts;
uint[] variableBorrowAmts;
uint[] stableBorrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}

View File

@ -0,0 +1,72 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { TokenInterface, AccountInterface } from "../../common/interfaces.sol";
import { AaveInterface, ATokenInterface, AaveData } from "./interfaces.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
contract AaveMigrateResolver is Helpers, Events {
// function migrate(
// address targetDsa,
// address[] calldata supplyTokens,
// address[] calldata borrowTokens
// ) external payable returns (string memory _eventName, bytes memory _eventParam) {
// require(supplyTokens.length > 0, "0-length-not-allowed");
// require(targetDsa != address(0), "invalid-address");
// for (uint i = 0; i < supplyTokens.length; i++) {
// address _token = supplyTokens[i] == ethAddr ? wethAddr : supplyTokens[i];
// (address _aToken, ,) = aaveData.getReserveTokensAddresses(_token);
// ATokenInterface _aTokenContract = ATokenInterface(_aToken);
// _aTokenContract.approve(address(migrator), _aTokenContract.balanceOf(address(this)));
// }
// migrator.migrate(msg.sender, targetDsa, supplyTokens, borrowTokens);
// _eventName = "LogAaveV2Migrate(address,address,address[],address[])";
// _eventParam = abi.encode(msg.sender, targetDsa, supplyTokens, borrowTokens);
// }
function migrate(
AaveData calldata _data
) external payable returns (string memory _eventName, bytes memory _eventParam) {
require(_data.supplyTokens.length > 0, "0-length-not-allowed");
require(_data.supplyTokens.length == _data.supplyAmts.length, "invalid-length");
require(_data.targetDsa != address(0), "invalid-address");
require(!_data.isFinal, "wrong-data");
AaveData memory data;
data.borrowTokens = _data.borrowTokens;
data.isFinal = _data.isFinal;
data.stableBorrowAmts = _data.stableBorrowAmts;
data.supplyAmts = _data.supplyAmts;
data.supplyTokens = _data.supplyTokens;
data.targetDsa = _data.targetDsa;
data.variableBorrowAmts = _data.variableBorrowAmts;
for (uint i = 0; i < data.supplyTokens.length; i++) {
address _token = data.supplyTokens[i] == ethAddr ? wethAddr : data.supplyTokens[i];
data.supplyTokens[i] = _token;
(address _aToken, ,) = aaveData.getReserveTokensAddresses(_token);
ATokenInterface _aTokenContract = ATokenInterface(_aToken);
if (data.supplyAmts[i] == uint(-1)) {
data.supplyAmts[i] = _aTokenContract.balanceOf(address(this));
}
_aTokenContract.approve(address(migrator), data.supplyAmts[i]);
}
migrator.migrate(msg.sender, data);
_eventName = "LogAaveV2Migrate(address,address,address[],address[])";
_eventParam = abi.encode(msg.sender, data.targetDsa, data.supplyTokens, data.borrowTokens);
}
}
contract AaveV2Migrator is AaveMigrateResolver {
string constant public name = "AaveV2PolygonMigrator-v1";
}

View File

@ -0,0 +1,26 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
contract Events {
event LogDeposit(
address owner,
address[] tokens,
uint[] amts
);
event LogWithdraw(
address owner,
address[] tokens,
uint[] amts
);
event LogAaveV2Migrate(
address indexed user,
address indexed targetDsa,
address[] supplyTokens,
address[] borrowTokens,
uint[] supplyAmts,
uint[] variableBorrowAmts,
uint[] stableBorrowAmts
);
}

View File

@ -0,0 +1,80 @@
pragma solidity ^0.7.0;
import { DSMath } from "../../common/math.sol";
import { Stores } from "../../common/stores-mainnet.sol";
import {
AaveLendingPoolProviderInterface,
AaveDataProviderInterface,
AaveInterface,
StateSenderInterface
} from "./interfaces.sol";
abstract contract Helpers is DSMath, Stores {
/**
* @dev Aave referal code
*/
uint16 constant internal referralCode = 3228;
address constant internal polygonReceiver = address(2); // Replace this
/**
* @dev Aave Provider
*/
AaveLendingPoolProviderInterface constant internal aaveProvider = AaveLendingPoolProviderInterface(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5);
/**
* @dev Aave Data Provider
*/
AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d);
/**
* @dev Polygon State Sync Contract
*/
StateSenderInterface constant internal stateSender = StateSenderInterface(0x28e4F3a7f651294B9564800b2D01f35189A5bFbE);
function _paybackBehalfOne(AaveInterface aave, address token, uint amt, uint rateMode, address user) private {
aave.repay(token, amt, rateMode, user);
}
function _PaybackStable(
uint _length,
AaveInterface aave,
address[] memory tokens,
uint256[] memory amts,
address user
) internal {
for (uint i = 0; i < _length; i++) {
if (amts[i] > 0) {
_paybackBehalfOne(aave, tokens[i], amts[i], 1, user);
}
}
}
function _PaybackVariable(
uint _length,
AaveInterface aave,
address[] memory tokens,
uint256[] memory amts,
address user
) internal {
for (uint i = 0; i < _length; i++) {
if (amts[i] > 0) {
_paybackBehalfOne(aave, tokens[i], amts[i], 2, user);
}
}
}
function _Withdraw(
uint _length,
AaveInterface aave,
address[] memory tokens,
uint256[] memory amts
) internal {
for (uint i = 0; i < _length; i++) {
if (amts[i] > 0) {
aave.withdraw(tokens[i], amts[i], address(this));
}
}
}
}

View File

@ -0,0 +1,77 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
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 getUserAccountData(address user) external view returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}
interface AaveLendingPoolProviderInterface {
function getLendingPool() external view returns (address);
}
// Aave Protocol Data Provider
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 getReserveConfigurationData(address asset) external view returns (
uint256 decimals,
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
uint256 reserveFactor,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive,
bool isFrozen
);
}
interface AaveAddressProviderRegistryInterface {
function getAddressesProvidersList() external view returns (address[] memory);
}
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, uint) external returns (bool);
function approve(address, uint256) external;
}
interface StateSenderInterface {
function syncState(address receiver, bytes calldata data) external;
function register(address sender, address receiver) external;
}

View File

@ -0,0 +1,241 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { TokenInterface } from "../../common/interfaces.sol";
import { Helpers } from "./helpers.sol";
import { AaveInterface, ATokenInterface } from "./interfaces.sol";
import { Events } from "./events.sol";
contract LiquidityResolver is Helpers, Events {
using SafeERC20 for IERC20;
mapping(address => mapping(address => uint)) deposits;
function deposit(address[] calldata tokens, uint[] calldata amts) external payable {
uint _length = tokens.length;
require(_length == amts.length, "invalid-length");
uint[] memory _amts = new uint[](_length);
for (uint256 i = 0; i < _length; i++) {
uint _amt;
address _token = tokens[i];
if (_token == ethAddr) {
require(msg.value == amts[i]);
_amt = msg.value;
TokenInterface(wethAddr).deposit{value: msg.value}();
} else {
IERC20 tokenContract = IERC20(_token);
_amt = amts[i] == uint(-1) ? tokenContract.balanceOf(msg.sender) : amts[i];
tokenContract.safeTransferFrom(msg.sender, address(this), _amt);
}
_amts[i] = _amt;
deposits[msg.sender][_token] = _amt;
}
emit LogDeposit(msg.sender, tokens, _amts);
}
function withdraw(address[] calldata tokens, uint[] calldata amts) external {
uint _length = tokens.length;
require(_length == amts.length, "invalid-length");
uint[] memory _amts = new uint[](_length);
for (uint256 i = 0; i < _length; i++) {
uint _amt = amts[i];
address _token = tokens[i];
uint maxAmt = deposits[msg.sender][_token];
if (_amt > maxAmt) {
_amt = maxAmt;
}
if (_token == ethAddr) {
TokenInterface(wethAddr).withdraw(_amt);
msg.sender.call{value: _amt}("");
} else {
IERC20(_token).safeTransfer(msg.sender, _amt);
}
_amts[i] = _amt;
deposits[msg.sender][_token] = sub(maxAmt, _amt);
}
emit LogWithdraw(msg.sender, tokens, _amts);
}
}
contract MigrateResolver is LiquidityResolver {
using SafeERC20 for IERC20;
struct AaveData {
bool isFinal;
address targetDsa;
uint[] supplyAmts;
uint[] variableBorrowAmts;
uint[] stableBorrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}
mapping (address => AaveData) public positions;
function migrate(address owner, AaveData calldata _data) external {
require(_data.supplyTokens.length > 0, "0-length-not-allowed");
require(_data.targetDsa != address(0), "invalid-address");
require(_data.supplyTokens.length == _data.supplyAmts.length, "invalid-length");
require(
_data.borrowTokens.length == _data.variableBorrowAmts.length &&
_data.borrowTokens.length == _data.stableBorrowAmts.length,
"invalid-length"
);
AaveData memory data;
data.borrowTokens = _data.borrowTokens;
data.isFinal = _data.isFinal;
data.stableBorrowAmts = _data.stableBorrowAmts;
data.supplyAmts = _data.supplyAmts;
data.supplyTokens = _data.supplyTokens;
data.targetDsa = _data.targetDsa;
data.variableBorrowAmts = _data.variableBorrowAmts;
address sourceDsa = msg.sender;
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
(,,,,,uint healthFactor) = aave.getUserAccountData(sourceDsa);
require(healthFactor > 1e18, "position-not-safe");
for (uint i = 0; i < data.supplyTokens.length; i++) {
(address _aToken, ,) = aaveData.getReserveTokensAddresses(data.supplyTokens[i]);
ATokenInterface aTokenContract = ATokenInterface(_aToken);
aTokenContract.transferFrom(msg.sender, address(this), data.supplyAmts[i]);
}
for (uint i = 0; i < data.borrowTokens.length; i++) {
address _token = data.borrowTokens[i] == ethAddr ? wethAddr : data.borrowTokens[i];
data.borrowTokens[i] = _token;
(
,
uint stableDebt,
uint variableDebt,
,,,,,
) = aaveData.getUserReserveData(_token, sourceDsa);
data.stableBorrowAmts[i] = data.stableBorrowAmts[i] == uint(-1) ? stableDebt : data.stableBorrowAmts[i];
data.variableBorrowAmts[i] = data.variableBorrowAmts[i] == uint(-1) ? variableDebt : data.variableBorrowAmts[i];
uint totalBorrowAmt = add(data.stableBorrowAmts[i], data.variableBorrowAmts[i]);
if (totalBorrowAmt > 0) {
IERC20(_token).safeApprove(address(aave), totalBorrowAmt);
}
}
_PaybackStable(data.borrowTokens.length, aave, data.borrowTokens, data.stableBorrowAmts, sourceDsa);
_PaybackVariable(data.borrowTokens.length, aave, data.borrowTokens, data.variableBorrowAmts, sourceDsa);
_Withdraw(data.supplyTokens.length, aave, data.supplyTokens, data.supplyAmts);
positions[owner] = data;
bytes memory positionData = abi.encode(owner, data);
stateSender.syncState(polygonReceiver, positionData);
emit LogAaveV2Migrate(
msg.sender,
data.targetDsa,
data.supplyTokens,
data.borrowTokens,
data.supplyAmts,
data.variableBorrowAmts,
data.stableBorrowAmts
);
}
// function migrate(
// address owner,
// address targetDsa,
// address[] calldata supplyTokens,
// address[] calldata borrowTokens
// ) external {
// require(supplyTokens.length > 0, "0-length-not-allowed");
// require(targetDsa != address(0), "invalid-address");
// address sourceDsa = msg.sender;
// AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
// AaveData memory data;
// (,,,,,uint healthFactor) = aave.getUserAccountData(sourceDsa);
// require(healthFactor > 1e18, "position-not-safe");
// data.supplyAmts = new uint[](supplyTokens.length);
// data.supplyTokens = new address[](supplyTokens.length);
// data.targetDsa = targetDsa;
// for (uint i = 0; i < supplyTokens.length; i++) {
// address _token = supplyTokens[i] == ethAddr ? wethAddr : supplyTokens[i];
// (address _aToken, ,) = aaveData.getReserveTokensAddresses(_token);
// ATokenInterface aTokenContract = ATokenInterface(_aToken);
// data.supplyTokens[i] = _token;
// data.supplyAmts[i] = aTokenContract.balanceOf(sourceDsa);
// aTokenContract.transferFrom(msg.sender, address(this), data.supplyAmts[i]);
// }
// if (borrowTokens.length > 0) {
// data.variableBorrowAmts = new uint[](borrowTokens.length);
// data.stableBorrowAmts = new uint[](borrowTokens.length);
// for (uint i = 0; i < borrowTokens.length; i++) {
// address _token = borrowTokens[i] == ethAddr ? wethAddr : borrowTokens[i];
// data.borrowTokens[i] = _token;
// (
// ,
// data.stableBorrowAmts[i],
// data.variableBorrowAmts[i],
// ,,,,,
// ) = aaveData.getUserReserveData(_token, sourceDsa);
// uint totalBorrowAmt = add(data.stableBorrowAmts[i], data.variableBorrowAmts[i]);
// if (totalBorrowAmt > 0) {
// IERC20(_token).safeApprove(address(aave), totalBorrowAmt);
// }
// }
// _PaybackStable(borrowTokens.length, aave, data.borrowTokens, data.stableBorrowAmts, sourceDsa);
// _PaybackVariable(borrowTokens.length, aave, data.borrowTokens, data.variableBorrowAmts, sourceDsa);
// }
// _Withdraw(supplyTokens.length, aave, data.supplyTokens, data.supplyAmts);
// positions[owner] = data;
// bytes memory positionData = abi.encode(owner, data);
// stateSender.syncState(polygonReceiver, positionData);
// emit LogAaveV2Migrate(
// msg.sender,
// targetDsa,
// supplyTokens,
// borrowTokens,
// data.supplyAmts,
// data.variableBorrowAmts,
// data.stableBorrowAmts
// );
// }
}

View File

@ -0,0 +1,28 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
interface TokenInterface {
function approve(address, uint256) external;
function transfer(address, uint) external;
function transferFrom(address, address, uint) external;
function deposit() external payable;
function withdraw(uint) external;
function balanceOf(address) external view returns (uint);
function decimals() external view returns (uint);
}
interface MemoryInterface {
function getUint(uint id) external returns (uint num);
function setUint(uint id, uint val) external;
}
interface AccountInterface {
function enable(address) external;
function disable(address) external;
function isAuth(address) external view returns (bool);
function cast(
string[] calldata _targets,
bytes[] calldata _datas,
address _origin
) external payable returns (bytes32);
}

49
contracts/common/math.sol Normal file
View File

@ -0,0 +1,49 @@
pragma solidity ^0.7.0;
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
contract DSMath {
uint constant WAD = 10 ** 18;
uint constant RAY = 10 ** 27;
function add(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(x, y);
}
function sub(uint x, uint y) internal virtual pure returns (uint z) {
z = SafeMath.sub(x, y);
}
function mul(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.mul(x, y);
}
function div(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.div(x, y);
}
function wmul(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(SafeMath.mul(x, y), WAD / 2) / WAD;
}
function wdiv(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(SafeMath.mul(x, WAD), y / 2) / y;
}
function rdiv(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(SafeMath.mul(x, RAY), y / 2) / y;
}
function rmul(uint x, uint y) internal pure returns (uint z) {
z = SafeMath.add(SafeMath.mul(x, y), RAY / 2) / RAY;
}
function toInt(uint x) internal pure returns (int y) {
y = int(x);
require(y >= 0, "int-overflow");
}
function toRad(uint wad) internal pure returns (uint rad) {
rad = mul(wad, 10 ** 27);
}
}

View File

@ -0,0 +1,37 @@
pragma solidity ^0.7.0;
import { MemoryInterface } from "./interfaces.sol";
abstract contract Stores {
/**
* @dev Return ethereum address
*/
address constant internal ethAddr = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/**
* @dev Return Wrapped ETH address
*/
address constant internal wethAddr = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
/**
* @dev Return memory variable address
*/
MemoryInterface constant internal instaMemory = MemoryInterface(0x8a5419CfC711B2343c17a6ABf4B2bAFaBb06957F);
/**
* @dev Get Uint value from InstaMemory Contract.
*/
function getUint(uint getId, uint val) internal returns (uint returnVal) {
returnVal = getId == 0 ? val : instaMemory.getUint(getId);
}
/**
* @dev Set Uint value in InstaMemory Contract.
*/
function setUint(uint setId, uint val) virtual internal {
if (setId != 0) instaMemory.setUint(setId, val);
}
}

View File

@ -0,0 +1,37 @@
pragma solidity ^0.7.0;
import { MemoryInterface } from "./interfaces.sol";
abstract contract Stores {
/**
* @dev Return matic address
*/
address constant internal maticAddr = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/**
* @dev Return Wrapped MATIC address
*/
address constant internal wmaticAddr = 0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270;
/**
* @dev Return memory variable address
*/
MemoryInterface constant internal instaMemory = MemoryInterface(address(0)); // TODO: memory address on Polygon
/**
* @dev Get Uint value from InstaMemory Contract.
*/
function getUint(uint getId, uint val) internal returns (uint returnVal) {
returnVal = getId == 0 ? val : instaMemory.getUint(getId);
}
/**
* @dev Set Uint value in InstaMemory Contract.
*/
function setUint(uint setId, uint val) virtual internal {
if (setId != 0) instaMemory.setUint(setId, val);
}
}

View File

@ -0,0 +1,119 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { Variables } from "./variables.sol";
/**
* @title InstaAccountV2.
* @dev DeFi Smart Account Wallet.
*/
interface ConnectorsInterface {
function isConnectors(string[] calldata connectorNames) external view returns (bool, address[] memory);
}
contract Constants is Variables {
// InstaIndex Address.
address internal constant instaIndex = 0xA9B99766E6C676Cf1975c0D3166F96C0848fF5ad;
// Migration contract Address.
address internal constant migrationContract = address(0); // TODO: update address on deployment
// Connnectors Address.
address public immutable connectorsM1;
constructor(address _connectors) {
connectorsM1 = _connectors;
}
}
contract InstaImplementationM1 is Constants {
constructor(address _connectors) Constants(_connectors) {}
function decodeEvent(bytes memory response) internal pure returns (string memory _eventCode, bytes memory _eventParams) {
if (response.length > 0) {
(_eventCode, _eventParams) = abi.decode(response, (string, bytes));
}
}
event LogCastMigrate(
address indexed origin,
address indexed sender,
uint256 value,
string[] targetsNames,
address[] targets,
string[] eventNames,
bytes[] eventParams
);
receive() external payable {}
/**
* @dev Delegate the calls to Connector.
* @param _target Connector address
* @param _data CallData of function.
*/
function spell(address _target, bytes memory _data) internal returns (bytes memory response) {
require(_target != address(0), "target-invalid");
assembly {
let succeeded := delegatecall(gas(), _target, add(_data, 0x20), mload(_data), 0, 0)
let size := returndatasize()
response := mload(0x40)
mstore(0x40, add(response, and(add(add(size, 0x20), 0x1f), not(0x1f))))
mstore(response, size)
returndatacopy(add(response, 0x20), 0, size)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
returndatacopy(0x00, 0x00, size)
revert(0x00, size)
}
}
}
/**
* @dev This is the main function, Where all the different functions are called
* from Smart Account.
* @param _targetNames Array of Connector address.
* @param _datas Array of Calldata.
*/
function castMigrate(
string[] calldata _targetNames,
bytes[] calldata _datas,
address _origin
)
external
payable
returns (bytes32) // Dummy return to fix instaIndex buildWithCast function
{
uint256 _length = _targetNames.length;
require(_auth[msg.sender] || msg.sender == instaIndex || msg.sender == migrationContract, "1: permission-denied");
require(_length != 0, "1: length-invalid");
require(_length == _datas.length , "1: array-length-invalid");
string[] memory eventNames = new string[](_length);
bytes[] memory eventParams = new bytes[](_length);
// TODO: restrict migration contract to run something specific? or give is all access as it doesn't have power to run anything else
(bool isOk, address[] memory _targets) = ConnectorsInterface(connectorsM1).isConnectors(_targetNames);
require(isOk, "1: not-connector");
for (uint i = 0; i < _length; i++) {
bytes memory response = spell(_targets[i], _datas[i]);
(eventNames[i], eventParams[i]) = decodeEvent(response);
}
emit LogCastMigrate(
_origin,
msg.sender,
msg.value,
_targetNames,
_targets,
eventNames,
eventParams
);
}
}

View File

@ -0,0 +1,6 @@
pragma solidity ^0.7.0;
contract Variables {
// Auth Module(Address of Auth => bool).
mapping (address => bool) internal _auth;
}

315
contracts/liquidity.sol Normal file
View File

@ -0,0 +1,315 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { DSMath } from "./common/math.sol";
interface Account {
struct Info {
address owner; // The address that owns the account
uint256 number; // A nonce that allows a single address to control many accounts
}
}
interface ListInterface {
function accountID(address) external view returns (uint64);
}
interface Actions {
enum ActionType {
Deposit, // supply tokens
Withdraw, // borrow tokens
Transfer, // transfer balance between accounts
Buy, // buy an amount of some token (publicly)
Sell, // sell an amount of some token (publicly)
Trade, // trade tokens against another account
Liquidate, // liquidate an undercollateralized or expiring account
Vaporize, // use excess tokens to zero-out a completely negative account
Call // send arbitrary data to an address
}
struct ActionArgs {
ActionType actionType;
uint256 accountId;
Types.AssetAmount amount;
uint256 primaryMarketId;
uint256 secondaryMarketId;
address otherAddress;
uint256 otherAccountId;
bytes data;
}
struct DepositArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 market;
address from;
}
struct WithdrawArgs {
Types.AssetAmount amount;
Account.Info account;
uint256 market;
address to;
}
struct CallArgs {
Account.Info account;
address callee;
bytes data;
}
}
interface Types {
enum AssetDenomination {
Wei, // the amount is denominated in wei
Par // the amount is denominated in par
}
enum AssetReference {
Delta, // the amount is given as a delta from the current value
Target // the amount is given as an exact number to end up at
}
struct AssetAmount {
bool sign; // true if positive
AssetDenomination denomination;
AssetReference ref;
uint256 value;
}
struct Wei {
bool sign; // true if positive
uint256 value;
}
}
interface ISoloMargin {
struct OperatorArg {
address operator;
bool trusted;
}
function getMarketTokenAddress(uint256 marketId)
external
view
returns (address);
function getNumMarkets() external view returns (uint256);
function operate(
Account.Info[] calldata accounts,
Actions.ActionArgs[] calldata actions
) external;
function getAccountWei(Account.Info calldata account, uint256 marketId)
external
view
returns (Types.Wei memory);
}
contract DydxFlashloanBase {
function _getMarketIdFromTokenAddress(address _solo, address token)
internal
view
returns (uint256)
{
ISoloMargin solo = ISoloMargin(_solo);
uint256 numMarkets = solo.getNumMarkets();
address curToken;
for (uint256 i = 0; i < numMarkets; i++) {
curToken = solo.getMarketTokenAddress(i);
if (curToken == token) {
return i;
}
}
revert("No marketId found for provided token");
}
function _getAccountInfo() internal view returns (Account.Info memory) {
return Account.Info({owner: address(this), number: 1});
}
function _getWithdrawAction(uint marketId, uint256 amount)
internal
view
returns (Actions.ActionArgs memory)
{
return
Actions.ActionArgs({
actionType: Actions.ActionType.Withdraw,
accountId: 0,
amount: Types.AssetAmount({
sign: false,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: amount
}),
primaryMarketId: marketId,
secondaryMarketId: 0,
otherAddress: address(this),
otherAccountId: 0,
data: ""
});
}
function _getCallAction(bytes memory data)
internal
view
returns (Actions.ActionArgs memory)
{
return
Actions.ActionArgs({
actionType: Actions.ActionType.Call,
accountId: 0,
amount: Types.AssetAmount({
sign: false,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: 0
}),
primaryMarketId: 0,
secondaryMarketId: 0,
otherAddress: address(this),
otherAccountId: 0,
data: data
});
}
function _getDepositAction(uint marketId, uint256 amount)
internal
view
returns (Actions.ActionArgs memory)
{
return
Actions.ActionArgs({
actionType: Actions.ActionType.Deposit,
accountId: 0,
amount: Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: amount
}),
primaryMarketId: marketId,
secondaryMarketId: 0,
otherAddress: address(this),
otherAccountId: 0,
data: ""
});
}
}
/**
* @title ICallee
* @author dYdX
*
* Interface that Callees for Solo must implement in order to ingest data.
*/
interface ICallee {
// ============ Public Functions ============
/**
* Allows users to send this contract arbitrary data.
*
* @param sender The msg.sender to Solo
* @param accountInfo The account from which the data is being sent
* @param data Arbitrary data given by the sender
*/
function callFunction(
address sender,
Account.Info calldata accountInfo,
bytes calldata data
)
external;
}
interface IndexInterface {
function master() external view returns (address);
}
interface TokenInterface {
function approve(address, uint256) external;
function transfer(address, uint) external;
function transferFrom(address, address, uint) external;
function deposit() external payable;
function withdraw(uint) external;
function balanceOf(address) external view returns (uint);
function decimals() external view returns (uint);
}
struct AaveDataRaw {
address targetDsa;
uint[] supplyAmts;
uint[] variableBorrowAmts;
uint[] stableBorrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}
interface MigrationInterface {
function migrateFlashCallback(AaveDataRaw calldata _data, address dsa, uint ethAmt) external;
}
contract Setup {
address public constant soloAddr = 0x1E0447b19BB6EcFdAe1e4AE1694b0C3659614e4e;
address public constant wethAddr = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
MigrationInterface public constant migrationAddr = MigrationInterface(address(0)); // TODO: Migration address
TokenInterface wethContract = TokenInterface(wethAddr);
ISoloMargin solo = ISoloMargin(soloAddr);
}
contract DydxFlashloaner is Setup, ICallee, DydxFlashloanBase, DSMath {
using SafeERC20 for IERC20;
function callFunction(
address sender,
Account.Info memory account,
bytes memory data
) public override {
require(sender == address(this), "not-same-sender");
require(msg.sender == soloAddr, "not-solo-dydx-sender");
(AaveDataRaw memory _data, address dsa, uint ethAmt) = abi.decode(
data,
(AaveDataRaw, address, uint)
);
wethContract.transfer(address(migrationAddr), ethAmt);
migrationAddr.migrateFlashCallback(_data, dsa, ethAmt);
}
function initiateFlashLoan(bytes memory data, uint ethAmt) external {
require(msg.sender == address(migrationAddr), "not-migration-contract");
uint marketId = _getMarketIdFromTokenAddress(soloAddr, wethAddr); // TODO: set Static market ID?
Actions.ActionArgs[] memory operations = new Actions.ActionArgs[](3);
operations[0] = _getWithdrawAction(marketId, ethAmt);
operations[1] = _getCallAction(data);
operations[2] = _getDepositAction(marketId, ethAmt + 2);
Account.Info[] memory accountInfos = new Account.Info[](1);
accountInfos[0] = _getAccountInfo();
solo.operate(accountInfos, operations);
}
}
contract InstaPool is DydxFlashloaner {
constructor() public {
wethContract.approve(wethAddr, uint(-1));
}
receive() external payable {}
}

View File

@ -0,0 +1,16 @@
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
contract Events {
event LogDeposit(
address owner,
address[] tokens,
uint[] amts
);
event LogWithdraw(
address owner,
address[] tokens,
uint[] amts
);
}

View File

@ -0,0 +1,133 @@
pragma solidity >=0.7.0;
import { DSMath } from "../../common/math.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Stores } from "../../common/stores-polygon.sol";
import { Variables } from "./variables.sol";
import {
TokenMappingInterface,
AaveData,
AaveDataProviderInterface,
AaveInterface
} from "./interfaces.sol";
abstract contract Helpers is Stores, DSMath, Variables {
using SafeERC20 for IERC20;
function remapTokens(AaveData memory data) internal view returns (AaveData memory) {
for (uint i = 0; i < data.supplyTokens.length; i++) {
data.supplyTokens[i] = tokenMapping.getMapping(data.supplyTokens[i]);
}
for (uint i = 0; i < data.borrowTokens.length; i++) {
data.borrowTokens[i] = tokenMapping.getMapping(data.borrowTokens[i]);
}
return data;
}
function isPositionSafe() internal returns (bool isOk) {
// TODO: Check the final position health
require(isOk, "position-at-risk");
}
function transferAtokens(AaveInterface aave, address dsa, address[] memory supplyTokens, uint[] memory supplyAmts) internal {
for (uint i = 0; i < supplyTokens.length; i++) {
address _token = supplyTokens[i];
IERC20 _atokenContract = IERC20(_token); // TODO: Fetch atoken from Aave mapping (change _token to atoken address)
uint _atokenBal = _atokenContract.balanceOf(address(this));
uint _supplyAmt = supplyAmts[i];
bool isFlash;
uint _flashAmt;
// get Aave liquidity of token
uint tokenLiq = uint(0);
if (_atokenBal < _supplyAmt) {
uint _reqAmt = _supplyAmt - _atokenBal;
if (tokenLiq < _reqAmt) {
_flashAmt = flashAmts[_token];
if (_flashAmt > 0) {
aave.deposit(_token, _flashAmt, address(this), 3288); // TODO: what is our ID on Polygon?
tokenLiq += _flashAmt;
isFlash = true;
}
}
uint num = _reqAmt/tokenLiq + 1; // TODO: Is this right
uint splitAmt = _reqAmt/num; // TODO: Check decimal
uint finalSplit = _reqAmt - (splitAmt * (num - 1)); // TODO: to resolve upper decimal error
for (uint j = 0; j < num; j++) {
if (i < num - 1) {
aave.borrow(_token, splitAmt, 2, 3288, address(this)); // TODO: is "2" for interest rate mode. Right?
aave.deposit(_token, splitAmt, address(this), 3288);
} else {
aave.borrow(_token, finalSplit, 2, 3288, address(this)); // TODO: is "2" for interest rate mode. Right?
aave.deposit(_token, finalSplit, address(this), 3288);
}
}
}
if (isFlash) {
aave.withdraw(_token, _flashAmt, address(this));
}
_atokenContract.safeTransfer(dsa, _supplyAmt);
}
}
function borrowAndTransferSpells(AaveInterface aave, address dsa, address[] memory borrowTokens, uint[] memory borrowAmts) internal {
for (uint i = 0; i < borrowTokens.length; i++) {
address _token = borrowTokens[i];
address _atoken = address(0); // TODO: Fetch atoken address
// get Aave liquidity of token
uint tokenLiq = uint(0);
uint _borrowAmt = borrowAmts[i];
uint _flashAmt;
bool isFlash;
if (tokenLiq < _borrowAmt) {
_flashAmt = flashAmts[_token];
aave.deposit(_token, _flashAmt, address(this), 3288); // TODO: what is our ID on Polygon?
isFlash = true;
tokenLiq += _flashAmt;
}
// TODO: Check number of loops needed. Borrow and supply on user's account.
uint num = _borrowAmt/tokenLiq + 1; // TODO: Is this right
uint splitAmt = _borrowAmt/num; // TODO: Check decimal
uint finalSplit = _borrowAmt - (splitAmt * (num - 1)); // TODO: to resolve upper decimal error
uint spellsAmt = (2 * num) + 1;
string[] memory targets = new string[](spellsAmt);
bytes[] memory castData = new bytes[](spellsAmt);
for (uint j = 0; j < num; j++) {
targets[j] = "AAVE-A";
uint k = j * 2;
if (i < num - 1) {
// borrow spell
castData[k] = abi.encode("6abcd3de", _token, splitAmt, 2, 0, 0); // TODO: verify this & is rate mode right?
// deposit spell
castData[k+1] = abi.encode("ce88b439", _token, splitAmt, 2, 0, 0); // TODO: verify this & is rate mode right?
} else {
// borrow spell
castData[k] = abi.encode("6abcd3de", _token, finalSplit, 2, 0, 0); // TODO: verify this & is rate mode right?
// deposit spell
castData[k+1] = abi.encode("ce88b439", _token, finalSplit, 2, 0, 0); // TODO: verify this & is rate mode right?
}
}
if (isFlash) {
aave.withdraw(_token, _flashAmt, address(this));
}
targets[spellsAmt] = "BASIC-A"; // TODO: right spell?
castData[spellsAmt] = abi.encode("4bd3ab82", _atoken, _borrowAmt, address(this), 0, 0); // encode the data of atoken withdrawal
// TODO: Call DSAs cast and borrow (maybe create a new implementation which only this contract can run?)
}
}
}

View File

@ -0,0 +1,87 @@
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
interface AccountInterface {
function enable(address) external;
function disable(address) external;
function isAuth(address) external view returns (bool);
function cast(
string[] calldata _targets,
bytes[] calldata _datas,
address _origin
) external payable returns (bytes32);
function migrateAave(address) external payable returns (bytes32);
}
interface TokenMappingInterface {
function getMapping(address) external view returns (address);
}
struct AaveData {
address targetDsa;
uint[] supplyAmts;
uint[] borrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}
interface IndexInterface {
function master() external view returns (address);
}
interface AaveLendingPoolProviderInterface {
function getLendingPool() 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 getReserveConfigurationData(address asset) external view returns (
uint256 decimals,
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
uint256 reserveFactor,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive,
bool isFrozen
);
}
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 getUserAccountData(address user) external view returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}

View File

@ -0,0 +1,197 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { TokenInterface } from "../../common/interfaces.sol";
import { AccountInterface, AaveData, AaveInterface, IndexInterface } from "./interfaces.sol";
import { Events } from "./events.sol";
import { Helpers } from "./helpers.sol";
contract MigrateResolver is Helpers, Events {
using SafeERC20 for IERC20;
function spell(address _target, bytes memory _data) external {
require(msg.sender == instaIndex.master(), "not-master");
require(_target != address(0), "target-invalid");
assembly {
let succeeded := delegatecall(gas(), _target, add(_data, 0x20), mload(_data), 0, 0)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
let size := returndatasize()
returndatacopy(0x00, 0x00, size)
revert(0x00, size)
}
}
}
// TODO: @mubaris Make this similar to L1 migrator. Have to change ETH by MATIC
function deposit(address[] calldata tokens, uint[] calldata amts) external payable {
uint _length = tokens.length;
require(_length == amts.length, "invalid-length");
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
uint[] memory _amts = new uint[](_length);
for (uint256 i = 0; i < _length; i++) {
require(isSupportedToken[tokens[i]], "token-not-enabled");
uint _amt;
address _token = tokens[i];
if (_token == maticAddr) {
require(msg.value == amts[i]);
_amt = msg.value;
TokenInterface(wmaticAddr).deposit{value: msg.value}();
aave.deposit(wmaticAddr, _amt, address(this), 3288);
} else {
IERC20 tokenContract = IERC20(_token);
_amt = amts[i] == uint(-1) ? tokenContract.balanceOf(msg.sender) : amts[i];
tokenContract.safeTransferFrom(msg.sender, address(this), _amt);
aave.deposit(_token, _amt, address(this), 3288);
}
_amts[i] = _amt;
deposits[msg.sender][_token] += _amt;
}
emit LogDeposit(msg.sender, tokens, _amts);
}
function withdraw(address[] calldata tokens, uint[] calldata amts) external {
uint _length = tokens.length;
require(_length == amts.length, "invalid-length");
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
uint[] memory _amts = new uint[](_length);
for (uint256 i = 0; i < _length; i++) {
require(isSupportedToken[tokens[i]], "token-not-enabled");
uint _amt = amts[i];
address _token = tokens[i];
uint maxAmt = deposits[msg.sender][_token];
if (_amt > maxAmt) {
_amt = maxAmt;
}
deposits[msg.sender][_token] = sub(maxAmt, _amt);
if (_token == maticAddr) {
TokenInterface _tokenContract = TokenInterface(wmaticAddr);
uint _maticBal = address(this).balance;
uint _tknBal = _tokenContract.balanceOf(address(this));
if ((_maticBal + _tknBal) < _amt) {
aave.withdraw(wmaticAddr, sub(_amt, (_tknBal + _maticBal)), address(this));
}
_tokenContract.withdraw((sub(_amt, _maticBal)));
msg.sender.call{value: _amt}("");
} else {
IERC20 _tokenContract = IERC20(_token);
uint _tknBal = _tokenContract.balanceOf(address(this));
if (_tknBal < _amt) {
aave.withdraw(_token, sub(_amt, _tknBal), address(this));
}
_tokenContract.safeTransfer(msg.sender, _amt);
}
_amts[i] = _amt;
}
isPositionSafe();
emit LogWithdraw(msg.sender, tokens, _amts);
}
function settle() external {
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
for (uint i = 0; i < supportedTokens.length; i++) {
address _token = supportedTokens[i];
if (_token == maticAddr) {
_token = wmaticAddr;
if (address(this).balance > 0) {
TokenInterface(wmaticAddr).deposit{value: address(this).balance}();
}
}
IERC20 _tokenContract = IERC20(_token);
uint _tokenBal = _tokenContract.balanceOf(address(this));
if (_tokenBal > 0) {
_tokenContract.approve(address(this), _tokenBal);
aave.deposit(_token, _tokenBal, address(this), 3288);
}
(
uint supplyBal,,
uint borrowBal,
,,,,,
) = aaveData.getUserReserveData(_token, address(this));
if (supplyBal != 0 && borrowBal != 0) {
if (supplyBal > borrowBal) {
aave.withdraw(_token, (borrowBal + flashAmts[_token]), address(this)); // TODO: fail because of not enough withdrawing capacity?
IERC20(_token).approve(address(aave), borrowBal);
aave.repay(_token, borrowBal, 2, address(this));
} else {
aave.withdraw(_token, (supplyBal + flashAmts[_token]), address(this)); // TODO: fail because of not enough withdrawing capacity?
IERC20(_token).approve(address(aave), supplyBal);
aave.repay(_token, supplyBal, 2, address(this));
}
}
}
// TODO: emit event
}
}
contract AaveV2Migrator is MigrateResolver {
using SafeERC20 for IERC20;
uint private lastStateId;
function _migratePosition(AaveData memory _data) internal {
AaveData memory data = remapTokens(_data); // converting L1 token addresses to L2 addresses
address dsa = _data.targetDsa;
uint[] memory supplyAmts = _data.supplyAmts;
uint[] memory borrowAmts = _data.borrowAmts;
address[] memory supplyTokens = _data.supplyTokens;
address[] memory borrowTokens = _data.borrowTokens;
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
transferAtokens(aave, dsa, supplyTokens, supplyAmts);
// Have to borrow from user's account & transfer
borrowAndTransferSpells(aave, dsa, borrowTokens, borrowAmts);
isPositionSafe();
// TODO: emit event
}
function onStateReceive(uint256 stateId, bytes calldata receivedData) external {
require(stateId > lastStateId, "wrong-data");
lastStateId = stateId;
// TODO: what's the best way to store user's data to create position later.
// Can't do it via any address as user can migrate 2 times
positions[stateId] = receivedData;
// TODO: add event
}
function migrate(uint _id) external {
bytes memory _data = positions[_id];
require(_data.length != 0, "already-migrated");
AaveData memory data = abi.decode(_data, (AaveData));
_migratePosition(data);
delete positions[_id];
// TODO: add event
}
}

View File

@ -0,0 +1,43 @@
pragma solidity ^0.7.0;
import {
TokenMappingInterface,
AaveLendingPoolProviderInterface,
AaveDataProviderInterface,
IndexInterface
} from "./interfaces.sol";
contract Variables {
// This will be used to have debt/collateral ratio always 20% less than liquidation
// TODO: Is this number correct for it?
uint public safeRatioGap = 800000000000000000; // 20%? 2e17
// TODO: Add function for flash deposits and withdraw
mapping(address => mapping(address => uint)) flashDeposits; // Flash deposits of particular token
mapping(address => uint) flashAmts; // token amount for flashloan usage (these token will always stay raw in this contract)
// TODO: Replace this
TokenMappingInterface tokenMapping = TokenMappingInterface(address(2));
AaveLendingPoolProviderInterface constant internal aaveProvider = AaveLendingPoolProviderInterface(0xd05e3E715d945B59290df0ae8eF85c1BdB684744);
/**
* @dev Aave Data Provider
*/
// TODO: add L2 Data provider address
AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(address(0));
// dsa => position
mapping(uint => bytes) public positions;
mapping(address => mapping(address => uint)) public deposits;
// InstaIndex Address.
IndexInterface public constant instaIndex = IndexInterface(0xA9B99766E6C676Cf1975c0D3166F96C0848fF5ad);
// TODO: Set by construtor?
mapping(address => bool) public isSupportedToken;
address[] public supportedTokens;
}

View File

@ -0,0 +1,10 @@
pragma solidity ^0.7.0;
contract Events {
event LogAaveV2Migrate(
address indexed user,
address indexed targetDsa,
address[] supplyTokens,
address[] borrowTokens
);
}

View File

@ -0,0 +1,15 @@
pragma solidity ^0.7.0;
import { DSMath } from "../../common/math.sol";
import { Stores } from "../../common/stores-mainnet.sol";
import { AaveLendingPoolProviderInterface, AaveDataProviderInterface, AaveMigratorInterface } from "./interfaces.sol";
abstract contract Helpers is DSMath, Stores {
AaveMigratorInterface constant internal migrator = AaveMigratorInterface(address(2)); // Replace this (Migrator contract)
/**
* @dev Aave Data Provider
*/
AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d);
}

View File

@ -0,0 +1,86 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
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 getUserAccountData(address user) external view returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}
interface AaveLendingPoolProviderInterface {
function getLendingPool() external view returns (address);
}
// Aave Protocol Data Provider
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 getReserveConfigurationData(address asset) external view returns (
uint256 decimals,
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
uint256 reserveFactor,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive,
bool isFrozen
);
}
interface AaveAddressProviderRegistryInterface {
function getAddressesProvidersList() external view returns (address[] memory);
}
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, uint) external returns (bool);
function approve(address, uint256) external;
}
interface AaveMigratorInterface {
function migrate(AaveDataRaw memory _data) external;
function migrateWithFlash(AaveDataRaw memory _data, uint ethAmt) external;
}
struct AaveDataRaw {
address targetDsa;
uint[] supplyAmts;
uint[] variableBorrowAmts;
uint[] stableBorrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}

View File

@ -0,0 +1,55 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { TokenInterface, AccountInterface } from "../../common/interfaces.sol";
import { AaveInterface, ATokenInterface, AaveDataRaw } from "./interfaces.sol";
import { Helpers } from "./helpers.sol";
import { Events } from "./events.sol";
contract AaveMigrateResolver is Helpers, Events {
function migrate(
AaveDataRaw calldata _data,
uint ethAmt // if ethAmt is > 0 then use migrateWithflash
) external payable returns (string memory _eventName, bytes memory _eventParam) {
require(_data.supplyTokens.length > 0, "0-length-not-allowed");
require(_data.supplyTokens.length == _data.supplyAmts.length, "invalid-length");
require(_data.targetDsa != address(0), "invalid-address");
AaveDataRaw memory data;
data.borrowTokens = _data.borrowTokens;
data.stableBorrowAmts = _data.stableBorrowAmts;
data.supplyAmts = _data.supplyAmts;
data.supplyTokens = _data.supplyTokens;
data.targetDsa = _data.targetDsa;
data.variableBorrowAmts = _data.variableBorrowAmts;
for (uint i = 0; i < data.supplyTokens.length; i++) {
address _token = data.supplyTokens[i] == ethAddr ? wethAddr : data.supplyTokens[i];
data.supplyTokens[i] = _token;
(address _aToken, ,) = aaveData.getReserveTokensAddresses(_token);
ATokenInterface _aTokenContract = ATokenInterface(_aToken);
if (data.supplyAmts[i] == uint(-1)) {
data.supplyAmts[i] = _aTokenContract.balanceOf(address(this));
}
_aTokenContract.approve(address(migrator), data.supplyAmts[i]);
}
if (ethAmt > 0) {
migrator.migrateWithFlash(data, ethAmt);
} else {
migrator.migrate(data);
}
_eventName = "LogAaveV2Migrate(address,address,address[],address[])";
_eventParam = abi.encode(msg.sender, data.targetDsa, data.supplyTokens, data.borrowTokens);
}
}
contract AaveV2Migrator is AaveMigrateResolver {
string constant public name = "AaveV2PolygonMigrator-v1";
}

View File

@ -0,0 +1,26 @@
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
contract Events {
event LogDeposit(
address owner,
address[] tokens,
uint[] amts
);
event LogWithdraw(
address owner,
address[] tokens,
uint[] amts
);
event LogAaveV2Migrate(
address indexed user,
address indexed targetDsa,
address[] supplyTokens,
address[] borrowTokens,
uint[] supplyAmts,
uint[] variableBorrowAmts,
uint[] stableBorrowAmts
);
}

View File

@ -0,0 +1,149 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { DSMath } from "../../common/math.sol";
import { Stores } from "../../common/stores-mainnet.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Variables } from "./variables.sol";
import {
AaveLendingPoolProviderInterface,
AaveDataProviderInterface,
AaveInterface,
ATokenInterface,
StateSenderInterface,
AavePriceOracle,
ChainLinkInterface
} from "./interfaces.sol";
abstract contract Helpers is DSMath, Stores, Variables {
function _paybackBehalfOne(AaveInterface aave, address token, uint amt, uint rateMode, address user) private {
aave.repay(token, amt, rateMode, user);
}
function _PaybackStable(
uint _length,
AaveInterface aave,
address[] memory tokens,
uint256[] memory amts,
address user
) internal {
for (uint i = 0; i < _length; i++) {
if (amts[i] > 0) {
_paybackBehalfOne(aave, tokens[i], amts[i], 1, user);
}
}
}
function _PaybackVariable(
uint _length,
AaveInterface aave,
address[] memory tokens,
uint256[] memory amts,
address user
) internal {
for (uint i = 0; i < _length; i++) {
if (amts[i] > 0) {
_paybackBehalfOne(aave, tokens[i], amts[i], 2, user);
}
}
}
function _PaybackCalculate(AaveInterface aave, AaveDataRaw memory _data, address sourceDsa) internal returns (uint[] memory stableBorrow, uint[] memory variableBorrow, uint[] memory totalBorrow) {
for (uint i = 0; i < _data.borrowTokens.length; i++) {
require(isSupportedToken[_data.borrowTokens[i]], "token-not-enabled");
address _token = _data.borrowTokens[i] == ethAddr ? wethAddr : _data.borrowTokens[i];
_data.borrowTokens[i] = _token;
(
,
uint stableDebt,
uint variableDebt,
,,,,,
) = aaveData.getUserReserveData(_token, sourceDsa);
stableBorrow[i] = _data.stableBorrowAmts[i] == uint(-1) ? stableDebt : _data.stableBorrowAmts[i];
variableBorrow[i] = _data.variableBorrowAmts[i] == uint(-1) ? variableDebt : _data.variableBorrowAmts[i];
totalBorrow[i] = add(stableBorrow[i], variableBorrow[i]);
if (totalBorrow[i] > 0) {
IERC20(_token).approve(address(aave), totalBorrow[i]); // TODO: Approval is to Aave address of atokens address?
}
aave.borrow(_token, totalBorrow[i], 2, 3088, address(this)); // TODO: Borrowing debt to payback
}
}
function _getAtokens(address dsa, AaveInterface aave, address[] memory supplyTokens, uint[] memory supplyAmts) internal returns (uint[] memory finalAmts) {
for (uint i = 0; i < supplyTokens.length; i++) {
require(isSupportedToken[supplyTokens[i]], "token-not-enabled");
(address _aToken, ,) = aaveData.getReserveTokensAddresses(supplyTokens[i]);
ATokenInterface aTokenContract = ATokenInterface(_aToken);
uint _finalAmt;
if (supplyAmts[i] == uint(-1)) {
_finalAmt = aTokenContract.balanceOf(dsa);
} else {
_finalAmt = supplyAmts[i];
}
aTokenContract.transferFrom(dsa, address(this), finalAmts[i]);
_finalAmt = wmul(_finalAmt, fee);
finalAmts[i] = _finalAmt;
}
}
function isPositionSafe() internal returns (bool isOk) {
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
(,,,,,uint healthFactor) = aave.getUserAccountData(address(this));
// TODO: Check throughly minLimit = 100%/80% = 125% (20% gap initially)
uint minLimit = wdiv(1e18, safeRatioGap);
isOk = healthFactor > minLimit;
require(isOk, "position-at-risk");
}
function getTokensPrices(address[] memory tokens) internal view returns(uint[] memory tokenPricesInEth) {
tokenPricesInEth = AavePriceOracle(aaveProvider.getPriceOracle()).getAssetsPrices(tokens);
}
// Liquidation threshold
function getTokenLt(address[] memory tokens) internal view returns (uint[] memory decimals, uint[] memory tokenLts) {
for (uint i = 0; i < tokens.length; i++) {
(decimals[i],,tokenLts[i],,,,,,,) = aaveData.getReserveConfigurationData(tokens[i]);
}
}
function convertTo18(uint amount, uint decimal) internal returns (uint) {
return amount * (10 ** (18 - decimal)); // TODO: verify this
}
// TODO: need to verify this throughly
/*
* Checks the position to migrate should have a safe gap from liquidation
*/
function _checkRatio(AaveData memory data) public {
uint[] memory supplyTokenPrices = getTokensPrices(data.supplyTokens);
(uint[] memory supplyDecimals, uint[] memory supplyLts) = getTokenLt(data.supplyTokens);
uint[] memory borrowTokenPrices = getTokensPrices(data.borrowTokens);
(uint[] memory borrowDecimals,) = getTokenLt(data.borrowTokens);
uint netSupply;
uint netBorrow;
uint liquidation;
for (uint i = 0; i < data.supplyTokens.length; i++) {
uint _amt = wmul(convertTo18(data.supplyAmts[i], supplyDecimals[i]), supplyTokenPrices[i]);
netSupply += _amt;
liquidation += (_amt * supplyLts[i]) / 10000; // convert the number 8000 to 0.8
}
for (uint i = 0; i < data.borrowTokens.length; i++) {
uint _amt = wmul(convertTo18(data.borrowAmts[i], borrowDecimals[i]), borrowTokenPrices[i]);
netBorrow += _amt;
}
uint _dif = wmul(netSupply, sub(1e18, safeRatioGap));
require(netBorrow < sub(liquidation, _dif), "position-is-risky-to-migrate");
}
}

View File

@ -0,0 +1,98 @@
pragma solidity >=0.7.0;
pragma experimental ABIEncoderV2;
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 getUserAccountData(address user) external view returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}
interface AaveLendingPoolProviderInterface {
function getLendingPool() external view returns (address);
function getPriceOracle() external view returns (address);
}
// Aave Protocol Data Provider
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 getReserveConfigurationData(address asset) external view returns (
uint256 decimals,
uint256 ltv,
uint256 liquidationThreshold,
uint256 liquidationBonus,
uint256 reserveFactor,
bool usageAsCollateralEnabled,
bool borrowingEnabled,
bool stableBorrowRateEnabled,
bool isActive,
bool isFrozen
);
}
interface AaveAddressProviderRegistryInterface {
function getAddressesProvidersList() external view returns (address[] memory);
}
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, uint) external returns (bool);
function approve(address, uint256) external;
}
interface StateSenderInterface {
function syncState(address receiver, bytes calldata data) external;
function register(address sender, address receiver) external;
}
interface IndexInterface {
function master() external view returns (address);
}
interface FlashloanInterface {
function initiateFlashLoan(bytes memory data, uint ethAmt) external;
}
interface AavePriceOracle {
function getAssetPrice(address _asset) external view returns(uint256);
function getAssetsPrices(address[] calldata _assets) external view returns(uint256[] memory);
function getSourceOfAsset(address _asset) external view returns(uint256);
function getFallbackOracle() external view returns(uint256);
}
interface ChainLinkInterface {
function latestAnswer() external view returns (int256);
function decimals() external view returns (uint256);
}

View File

@ -0,0 +1,254 @@
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { TokenInterface } from "../../common/interfaces.sol";
import { Helpers } from "./helpers.sol";
import { AaveInterface, ATokenInterface, IndexInterface } from "./interfaces.sol";
import { Events } from "./events.sol";
contract LiquidityResolver is Helpers, Events {
using SafeERC20 for IERC20;
event variablesUpdate(uint _safeRatioGap, uint _fee);
function updateVariables(uint _safeRatioGap, uint _fee) public {
require(msg.sender == instaIndex.master(), "not-master");
safeRatioGap = _safeRatioGap;
fee = _fee;
emit variablesUpdate(safeRatioGap, fee);
}
function addTokenSupport(address[] memory _tokens) public {
require(msg.sender == instaIndex.master(), "not-master");
for (uint i = 0; i < _tokens.length; i++) {
isSupportedToken[_tokens[i]] = true;
}
supportedTokens = _tokens;
// TODO: add event
}
function spell(address _target, bytes memory _data) external {
require(msg.sender == instaIndex.master(), "not-master");
require(_target != address(0), "target-invalid");
assembly {
let succeeded := delegatecall(gas(), _target, add(_data, 0x20), mload(_data), 0, 0)
switch iszero(succeeded)
case 1 {
// throw if delegatecall failed
let size := returndatasize()
returndatacopy(0x00, 0x00, size)
revert(0x00, size)
}
}
}
function deposit(address[] calldata tokens, uint[] calldata amts) external payable {
uint _length = tokens.length;
require(_length == amts.length, "invalid-length");
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
uint[] memory _amts = new uint[](_length);
for (uint256 i = 0; i < _length; i++) {
require(isSupportedToken[tokens[i]], "token-not-enabled");
uint _amt;
address _token = tokens[i];
if (_token == ethAddr) {
require(msg.value == amts[i]);
_amt = msg.value;
TokenInterface(wethAddr).deposit{value: msg.value}();
aave.deposit(wethAddr, _amt, address(this), 3288);
} else {
IERC20 tokenContract = IERC20(_token);
_amt = amts[i] == uint(-1) ? tokenContract.balanceOf(msg.sender) : amts[i];
tokenContract.safeTransferFrom(msg.sender, address(this), _amt);
aave.deposit(_token, _amt, address(this), 3288);
}
_amts[i] = _amt;
deposits[msg.sender][_token] += _amt;
}
emit LogDeposit(msg.sender, tokens, _amts);
}
function withdraw(address[] calldata tokens, uint[] calldata amts) external {
uint _length = tokens.length;
require(_length == amts.length, "invalid-length");
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
uint[] memory _amts = new uint[](_length);
for (uint256 i = 0; i < _length; i++) {
require(isSupportedToken[tokens[i]], "token-not-enabled");
uint _amt = amts[i];
address _token = tokens[i];
uint maxAmt = deposits[msg.sender][_token];
if (_amt > maxAmt) {
_amt = maxAmt;
}
deposits[msg.sender][_token] = sub(maxAmt, _amt);
if (_token == ethAddr) {
TokenInterface _tokenContract = TokenInterface(wethAddr);
uint _ethBal = address(this).balance;
uint _tknBal = _tokenContract.balanceOf(address(this));
if ((_ethBal + _tknBal) < _amt) {
aave.withdraw(wethAddr, sub(_amt, (_tknBal + _ethBal)), address(this));
}
_tokenContract.withdraw((sub(_amt, _ethBal)));
msg.sender.call{value: _amt}("");
} else {
IERC20 _tokenContract = IERC20(_token);
uint _tknBal = _tokenContract.balanceOf(address(this));
if (_tknBal < _amt) {
aave.withdraw(_token, sub(_amt, _tknBal), address(this));
}
_tokenContract.safeTransfer(msg.sender, _amt);
}
_amts[i] = _amt;
}
isPositionSafe();
emit LogWithdraw(msg.sender, tokens, _amts);
}
/**
* @param _tokens - array of tokens to transfer to L2 receiver's contract
* @param _amts - array of token amounts to transfer to L2 receiver's contract
*/
function settle(address[] calldata _tokens, uint[] calldata _amts) external {
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
for (uint i = 0; i < supportedTokens.length; i++) {
address _token = supportedTokens[i];
if (_token == ethAddr) {
_token = wethAddr;
if (address(this).balance > 0) {
TokenInterface(wethAddr).deposit{value: address(this).balance}();
}
}
IERC20 _tokenContract = IERC20(_token);
uint _tokenBal = _tokenContract.balanceOf(address(this));
if (_tokenBal > 0) {
_tokenContract.approve(address(this), _tokenBal);
aave.deposit(_token, _tokenBal, address(this), 3288);
}
(
uint supplyBal,,
uint borrowBal,
,,,,,
) = aaveData.getUserReserveData(_token, address(this));
if (supplyBal != 0 && borrowBal != 0) {
if (supplyBal > borrowBal) {
aave.withdraw(_token, borrowBal, address(this)); // TODO: fail because of not enough withdrawing capacity?
IERC20(_token).approve(address(aave), borrowBal);
aave.repay(_token, borrowBal, 2, address(this));
} else {
aave.withdraw(_token, supplyBal, address(this)); // TODO: fail because of not enough withdrawing capacity?
IERC20(_token).approve(address(aave), supplyBal);
aave.repay(_token, supplyBal, 2, address(this));
}
}
}
for (uint i = 0; i < _tokens.length; i++) {
aave.withdraw(_tokens[i], _amts[i], address(this));
// TODO: transfer to polygon's receiver address "polygonReceiver"
isPositionSafe();
}
}
// TODO: emit event
}
contract MigrateResolver is LiquidityResolver {
using SafeERC20 for IERC20;
function _migrate(
AaveInterface aave,
AaveDataRaw memory _data,
address sourceDsa,
uint ethAmt
) internal {
require(_data.supplyTokens.length > 0, "0-length-not-allowed");
require(_data.targetDsa != address(0), "invalid-address");
require(_data.supplyTokens.length == _data.supplyAmts.length, "invalid-length");
require(
_data.borrowTokens.length == _data.variableBorrowAmts.length &&
_data.borrowTokens.length == _data.stableBorrowAmts.length,
"invalid-length"
);
if (ethAmt > 0) {
aave.deposit(wethAddr, ethAmt, address(this), 3288);
}
(uint[] memory stableBorrows, uint[] memory variableBorrows, uint[] memory totalBorrows) = _PaybackCalculate(aave, _data, sourceDsa);
_PaybackStable(_data.borrowTokens.length, aave, _data.borrowTokens, stableBorrows, sourceDsa);
_PaybackVariable(_data.borrowTokens.length, aave, _data.borrowTokens, variableBorrows, sourceDsa);
(uint[] memory totalSupplies) = _getAtokens(sourceDsa, aave, _data.supplyTokens, _data.supplyAmts);
// Aave on Polygon doesn't have stable borrowing so we'll borrow all the debt in variable
AaveData memory data;
data.borrowTokens = _data.borrowTokens;
data.borrowAmts = _data.stableBorrowAmts;
data.supplyAmts = totalSupplies;
data.supplyTokens = _data.supplyTokens;
data.targetDsa = _data.targetDsa;
data.borrowAmts = totalBorrows;
// Checks the amount that user is trying to migrate is 20% below the Liquidation
_checkRatio(data);
if (ethAmt > 0) {
aave.withdraw(wethAddr, ethAmt, address(this));
}
isPositionSafe();
stateSender.syncState(polygonReceiver, abi.encode(data));
emit LogAaveV2Migrate(
sourceDsa,
data.targetDsa,
data.supplyTokens,
data.borrowTokens,
totalSupplies,
variableBorrows,
stableBorrows
);
}
function migrate(AaveDataRaw calldata _data) external {
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
_migrate(aave, _data, msg.sender, 0);
}
function migrateFlashCallback(AaveDataRaw calldata _data, address dsa, uint ethAmt) external {
require(msg.sender == address(flashloanContract), "not-flashloan-contract"); // TODO: flash loan contract
AaveInterface aave = AaveInterface(aaveProvider.getLendingPool());
TokenInterface wethContract = TokenInterface(wethAddr);
wethContract.approve(address(aave), ethAmt);
_migrate(aave, _data, dsa, ethAmt);
wethContract.transfer(address(flashloanContract), ethAmt);
}
function migrateWithFlash(AaveDataRaw calldata _data, uint ethAmt) external {
bytes memory data = abi.encode(_data, msg.sender, ethAmt);
flashloanContract.initiateFlashLoan(data, ethAmt);
}
}

View File

@ -0,0 +1,71 @@
pragma solidity ^0.7.0;
import {
AaveLendingPoolProviderInterface,
AaveDataProviderInterface,
StateSenderInterface,
IndexInterface,
FlashloanInterface
} from "./interfaces.sol";
contract Variables {
struct AaveDataRaw {
address targetDsa;
uint[] supplyAmts;
uint[] variableBorrowAmts;
uint[] stableBorrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}
struct AaveData {
address targetDsa;
uint[] supplyAmts;
uint[] borrowAmts;
address[] supplyTokens;
address[] borrowTokens;
}
struct TokenPrice {
uint priceInEth;
uint priceInUsd;
}
/**
* @dev Aave referal code
*/
uint16 constant internal referralCode = 3228;
address constant internal polygonReceiver = address(0); // TODO: Replace this
FlashloanInterface constant internal flashloanContract = FlashloanInterface(address(0)); // TODO: Replace this
// This will be used to have debt/collateral ratio always 20% less than liquidation
// TODO: Is this number correct for it?
uint public safeRatioGap = 800000000000000000; // 20%?
uint public fee = 998000000000000000; // 0.2% (99.8%) on collateral? TODO: Is this right?
// TODO: Set by construtor?
mapping(address => bool) public isSupportedToken;
address[] public supportedTokens;
/**
* @dev Aave Provider
*/
AaveLendingPoolProviderInterface constant internal aaveProvider = AaveLendingPoolProviderInterface(0xB53C1a33016B2DC2fF3653530bfF1848a515c8c5);
/**
* @dev Aave Data Provider
*/
AaveDataProviderInterface constant internal aaveData = AaveDataProviderInterface(0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d);
/**
* @dev Polygon State Sync Contract
*/
StateSenderInterface constant internal stateSender = StateSenderInterface(0x28e4F3a7f651294B9564800b2D01f35189A5bFbE);
mapping(address => mapping(address => uint)) public deposits;
// InstaIndex Address.
IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723);
}

17764
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,10 @@
"chai": "^4.3.4",
"ethereum-waffle": "^3.3.0",
"ethers": "^5.1.0",
"hardhat": "^2.1.2"
"hardhat": "^2.1.2",
"solc": "0.7.0"
},
"dependencies": {
"@openzeppelin/contracts": "^3.4.0-solc-0.7"
}
}