diff --git a/contracts/receivers2/aave-v2-receiver/events.sol b/contracts/receivers2/aave-v2-receiver/events.sol new file mode 100644 index 0000000..a60963f --- /dev/null +++ b/contracts/receivers2/aave-v2-receiver/events.sol @@ -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 + ); +} \ No newline at end of file diff --git a/contracts/receivers2/aave-v2-receiver/helpers.sol b/contracts/receivers2/aave-v2-receiver/helpers.sol new file mode 100644 index 0000000..5d6c5c8 --- /dev/null +++ b/contracts/receivers2/aave-v2-receiver/helpers.sol @@ -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; + } +} \ No newline at end of file diff --git a/contracts/receivers2/aave-v2-receiver/interfaces.sol b/contracts/receivers2/aave-v2-receiver/interfaces.sol new file mode 100644 index 0000000..1cccf5d --- /dev/null +++ b/contracts/receivers2/aave-v2-receiver/interfaces.sol @@ -0,0 +1,30 @@ +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); +} \ No newline at end of file diff --git a/contracts/receivers2/aave-v2-receiver/main.sol b/contracts/receivers2/aave-v2-receiver/main.sol new file mode 100644 index 0000000..676de0f --- /dev/null +++ b/contracts/receivers2/aave-v2-receiver/main.sol @@ -0,0 +1,136 @@ +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; + + // InstaIndex Address. + IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); + + 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 { + 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(AaveData memory data) 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 dsa, AaveData memory data) = abi.decode(receivedData, (address, AaveData)); + // positions[owner] = remapTokens(data); + + // if (canMigrate(owner)) { + _migratePosition(dsa, data); + // } + } + + // function migrate(address owner) external { + // require(canMigrate(owner), "not-enough-liquidity"); + + // _migratePosition(owner); + // } +} \ No newline at end of file diff --git a/contracts/senders2/aave-v2-migrator/helpers.sol b/contracts/senders2/aave-v2-migrator/helpers.sol index 4255030..406e2ee 100644 --- a/contracts/senders2/aave-v2-migrator/helpers.sol +++ b/contracts/senders2/aave-v2-migrator/helpers.sol @@ -109,11 +109,12 @@ abstract contract Helpers is DSMath, Stores { } } - function _getAtokens(AaveInterface aave, address[] memory supplyTokens, uint[] memory supplyAmts) internal returns (uint[] finalAmts) { + function _getAtokens(AaveInterface aave, address[] memory supplyTokens, uint[] memory supplyAmts, uint fee) internal returns (uint[] finalAmts) { for (uint i = 0; i < supplyTokens.length; i++) { (address _aToken, ,) = aaveData.getReserveTokensAddresses(supplyTokens[i]); ATokenInterface aTokenContract = ATokenInterface(_aToken); + // TODO: deduct the fee from finalAmt if (supplyAmts[i] == uint(-1)) { // TODO: get maximum balance and set the return variable } else { diff --git a/contracts/senders2/aave-v2-migrator/main.sol b/contracts/senders2/aave-v2-migrator/main.sol index 97f3d56..494f3ac 100644 --- a/contracts/senders2/aave-v2-migrator/main.sol +++ b/contracts/senders2/aave-v2-migrator/main.sol @@ -23,6 +23,13 @@ contract LiquidityResolver is Helpers, Events { // InstaIndex Address. IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); + function updateVariables(uint _safeRatioGap, uint _fee) public { + require(msg.sender == instaIndex.master(), "not-master"); + safeRatioGap = _safeRatioGap; + fee = _fee; + // TODO: Add event + } + function spell(address _target, bytes memory _data) external { require(msg.sender == instaIndex.master(), "not-master"); require(_target != address(0), "target-invalid"); @@ -111,7 +118,7 @@ contract LiquidityResolver is Helpers, Events { contract MigrateResolver is LiquidityResolver { using SafeERC20 for IERC20; - function _migrate(address l2DSA, AaveDataRaw memory _data, address sourceDsa) internal { + function _migrate(AaveDataRaw memory _data, address sourceDsa) 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"); @@ -131,7 +138,7 @@ contract MigrateResolver is LiquidityResolver { _PaybackStable(_data.borrowTokens.length, aave, _data.borrowTokens, stableBorrows, sourceDsa); _PaybackVariable(_data.borrowTokens.length, aave, _data.borrowTokens, variableBorrows, sourceDsa); - (uint[] totalSupplies) = _getAtokens(aave, _data.supplyTokens, _data.supplyAmts); + (uint[] totalSupplies) = _getAtokens(aave, _data.supplyTokens, _data.supplyAmts, fee); // Aave on Polygon doesn't have stable borrowing so we'll borrow all the debt in variable AaveData memory data; @@ -147,7 +154,7 @@ contract MigrateResolver is LiquidityResolver { bool isOk = _checkRatio(data, safeRatioGap); require(isOk, "position-risky-to-migrate"); - bytes memory positionData = abi.encode(l2DSA, data); // TODO: Can we do anything else to make the data more secure? (It's secure already) + bytes memory positionData = data; // TODO: Can we do anything else to make the data more secure? (It's secure already) stateSender.syncState(polygonReceiver, positionData); emit LogAaveV2Migrate( @@ -161,12 +168,12 @@ contract MigrateResolver is LiquidityResolver { ); } - function migrate(address l2DSA, AaveDataRaw calldata _data) external { - _migrate(l2DSA, _data, msg.sender); + function migrate(AaveDataRaw calldata _data) external { + _migrate(_data, msg.sender); } - function migrateWithFlash(address l2DSA, AaveDataRaw calldata _data, uint ethAmt) external { - bytes data = abi.encode(l2DSA, _data, msg.sender, ethAmt); + function migrateWithFlash(AaveDataRaw calldata _data, uint ethAmt) external { + bytes data = abi.encode(_data, msg.sender, ethAmt); // TODO: integrate dydx flashloan and borrow "ethAmt" and transfer ETH to this address