diff --git a/contracts/mainnet/connectors/morpho/events.sol b/contracts/mainnet/connectors/morpho/events.sol new file mode 100644 index 00000000..f8f29f7b --- /dev/null +++ b/contracts/mainnet/connectors/morpho/events.sol @@ -0,0 +1,48 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +contract Events { + event LogDeposit( + uint256 pool, + address tokenAddress, + address poolTokenAddress, + uint256 amount, + uint256 maxGasForMatching, + uint256 getId, + uint256 setId + ); + + event LogBorrow( + uint256 pool, + address poolTokenAddress, + uint256 amount, + uint256 maxGasForMatching, + uint256 getId, + uint256 setId + ); + + event LogWithdraw( + uint256 pool, + bool isETH, + address poolTokenAddress, + uint256 amt, + uint256 getId, + uint256 setId + ); + + event LogPayback( + uint256 pool, + bool isETH, + address poolTokenAddress, + uint256 amt, + uint256 getId, + uint256 setId + ); + + event LogClaimed( + uint256 pool, + address[] tokenAddresses, + bool tradeForMorphoToken + ); +} diff --git a/contracts/mainnet/connectors/morpho/helpers.sol b/contracts/mainnet/connectors/morpho/helpers.sol new file mode 100644 index 00000000..d99da296 --- /dev/null +++ b/contracts/mainnet/connectors/morpho/helpers.sol @@ -0,0 +1,19 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +import "./interface.sol"; +import "../../common/stores.sol"; +import "../../common/basic.sol"; +import "../../common/interfaces.sol"; + +abstract contract Helpers is Stores, Basic { + IMorphoCore public constant morphoCompound = + IMorphoCore(0x8888882f8f843896699869179fB6E4f7e3B58888); + IMorphoCore public constant morphoAave = + IMorphoCore(0x777777c9898D384F785Ee44Acfe945efDFf5f3E0); + + enum Underlying { + AAVEV2, + COMPOUNDV2 + } +} diff --git a/contracts/mainnet/connectors/morpho/interface.sol b/contracts/mainnet/connectors/morpho/interface.sol new file mode 100644 index 00000000..da192bb6 --- /dev/null +++ b/contracts/mainnet/connectors/morpho/interface.sol @@ -0,0 +1,42 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +interface IMorphoCore { + function supply( + address _poolTokenAddress, + address _onBehalf, + uint256 _amount, + uint256 _maxGasForMatching + ) external; + + function borrow( + address _poolTokenAddress, + uint256 _amount, + uint256 _maxGasForMatching + ) external; + + function withdraw( + address _poolTokenAddress, + uint256 _amount + ) external; + + function repay( + address _poolTokenAddress, + address _onBehalf, + uint256 _amount + ) external; + + function liquidate( + address _poolTokenBorrowedAddress, + address _poolTokenCollateralAddress, + address _borrower, + uint256 _amount + ) external; + + // (For AAVEV2: (aToken or variable debt token), COMPOUNDV2: cToken addresses) + function claimRewards( + address[] _tokenAddresses, + bool _tradeForMorphoToken, + ) external; +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/morpho/main.sol b/contracts/mainnet/connectors/morpho/main.sol new file mode 100644 index 00000000..cafcde5d --- /dev/null +++ b/contracts/mainnet/connectors/morpho/main.sol @@ -0,0 +1,178 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; +import './helpers.sol'; +import './events.sol'; +import './hardhat/console.sol'; + +abstract contract Morpho is Helpers, Events { + + function deposit ( + Underlying _pool, + address _tokenAddress, + address _poolTokenAddress, // if address weth (send eth) + uint256 _amount, // if max, check balance + uint256 _maxGasForMatching, + uint256 _getId, + uint256 _setId + ) external payable returns(string memory _eventName, bytes memory _eventParam) { + + require(_pool == Underlying.AAVEV2 || _pool == Underlying.COMPOUNDV2, 'protocol not supported'); + + uint256 _amt = getUint(_getId, _amount); + + bool _isETH ? _tokenAddress == ethAddr; + address _token = _isETH ? wethAddr : _tokenAddress + + TokenInterface _tokenContract = TokenInterface(_token); + + if(_amt == uint256(-1)) { + _amt = _isETH ? address(this).balance : _tokenContract.balanceOf(address(this)); + } + + if(_isETH) convertEthToWeth(_isETH, _tokenContract, _amt); + + _pool == Underlying.AAVEV2 + ? + approve(_tokenContract, morphoAave, _amt); + morphoAave.supply(_poolTokenAddress, address(this), _amt, _maxGasForMatching); + : + approve(_tokenContract, morphoCompound, _amt); + morphoCompound.supply(_poolTokenAddress, address(this), _amt, _maxGasForMatching); + + setUint(_setId, _amt); + + _eventName = "LogDeposit(uint256,address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + _pool, + _tokenAddress, + _poolTokenAddress, + _amt, + _maxGasForMatching, + _getId, + _setId + ); + } + + function borrow ( + Underlying _pool, + address _tokenAddress, + address _poolTokenAddress, //todo: dTokenaAddress? // if address weth (send eth) + uint256 _amount, + uint256 _maxGasForMatching + uint256 _getId, + uint256 _setId + ) external payable returns(string memory _eventName, bytes memory _eventParam) { + + require(_pool == Underlying.AAVEV2 || _pool == Underlying.COMPOUNDV2, 'protocol not supported'); + + uint256 _amt = getUint(_getId, _amount); + + bool _isETH ? _tokenAddress == ethAddr; + address _token = _isETH ? wethAddr : _tokenAddress; + + if(_pool == Underlying.AAVEV2) { + morphoAave.borrow(_poolTokenAddress, _amt, _maxGasForMatching); + } else { + morphoCompound.borrow(_poolTokenAddress, _amt, _maxGasForMatching); + } + + if(_isETH) convertWethToEth(_isETH, tokenInterface(_token), _amt); + + setUint(_setId, _amt); + + _eventName = "LogBorrow(uint256,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + _pool, + _poolTokenAddress, + _amt, + _maxGasForMatching, + _getId, + _setId + ); + } + + function withdraw ( + Underlying _pool, + address _tokenAddress, + address _poolTokenAddress, // if address weth (send eth) + uint256 _amount, // amount max + uint256 _getId, + uint256 _setId + ) external payable returns(string memory _eventName, bytes memory _eventParam) { + + require(_pool == Underlying.AAVEV2 || _pool == Underlying.COMPOUNDV2, 'protocol not supported'); + + uint256 _amt = getUint(_getId, _amount); + bool _isETH ? _tokenAddress == ethAddr; + address _token = _isETH ? wethAddr : _tokenAddress; + + if (_amt == uint256(-1)) _amt = _poolTokenAddress.balanceOf(address(this)); + + if(_pool == Underlying.AAVEV2) { + morphoAave.withdraw(_poolTokenAddress, _amt); + } else { + morphoCompound.withdraw(_poolTokenAddress, _amt); + } + + convertWethToEth(_isETH, TokenInterface(_token), _amt); + + setUint(_setId, _amt); + + _eventName = "LogWithdraw(uint256,bool,address,uint256,uint256,uint256)"; + _eventParam = abi.encode(_pool, _isETH, _poolTokenAddress, _amt, _getId, _setId); + + } + + function payback ( + Underlying _pool, + address _tokenAddress, + address _poolTokenAddress, // if address weth (send eth) + uint256 _amount, // max value + uint256 _getId, + uint256 _setId + ) external payable returns(string memory _eventName, bytes memory _eventParam) { + + require(_pool == Underlying.AAVEV2 || _pool == Underlying.COMPOUNDV2, 'protocol not supported'); + + bool _isETH ? _tokenAddress == ethAddr; + uint256 _amt = getUint(_getId, _amount); + address _token = _isETH ? wethAddr : _tokenAddress; + + if(_amt == uint256(-1)) { + _amt = _isETH ? _amt = address(this).balance : TokenInterface(_token).balanceOf(address(this)) + } + + if (_isETH) convertEthToWeth(_isETH, TokenInterface(_token), _amt); + + _pool == Underlying.AAVEV2 + ? morphoAave.repay(_poolTokenAddress, address(this), _amt) + : morphoCompound.repay(_poolTokenAddress, address(this), _amt); + + setUint(_setId, _amt); + + _eventName = "LogPayback(uint256,bool,address,uint256,uint256,uint256)"; + _eventParam = abi.encode(_pool, _isETH, _poolTokenAddress, _amt, _getId, _setId); + } + + function claim ( + Underlying _pool, + address[] _tokenAddresses, //todo: eth will be claimed as weth currently? + bool _tradeForMorphoToken + ) external payable returns(string memory _eventName, bytes memory _eventParam) { + + require(_pool == Underlying.AAVEV2 || _pool == Underlying.COMPOUNDV2, 'protocol not supported'); + + _pool == Underlying.AAVEV2 + ? morphoAave.claim(_tokenAddresses, _tradeForMorphoToken) + : morphoCompound.claim(_tokenAddresses, _tradeForMorphoToken); + + _eventName = "LogClaimed(uint256,address[],bool)"; + _eventParam = abi.encode(_pool, _tokenAddresses, _tradeForMorphoToken); + } + +} + +contract ConnectV2Morpho is Morpho { + string public constant name = "Morpho-v1.0"; +}