// SPDX-License-Identifier: agpl-3.0 pragma solidity ^0.6.8; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IOneSplit.sol"; import "../interfaces/IPriceOracleGetter.sol"; import "../interfaces/IExchangeAdapter.sol"; /// @title OneSplitAdapter /// @author Aave /// @notice Implements the logic to exchange assets through 1Split /// The hardcoded parameters are: /// 0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476: ONE_SPLIT. The address of the 1Split exchange /// 0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4: AAVE_PRICES_PROVIDER. Contract providing prices of the assets /// in the Aave protocol, in token/ETH. /// 512 : MULTI_PATH_ETH_FLAG. By using this flag on OneSplit, the swap sequence will introduce a step to ETH /// in the middle, resulting in a sequence like FROM-to-ETH -> ETH-to-TO. /// This is optimal for cases where the pair FROM/TO is not liquid enough in the /// underlying exchanges used by OneSplit, reducing this way the slippage. /// 10: SPLIT_PARTS. It defines in how many chunks the amount to swap will be splitted to then /// divide the chunks amongst the underlying exchanges. /// For example, using 10 as SPLIT_PARTS and having 4 underlying exchanges on 1Split, /// the division amongst could look like [4,4,0,2]. contract OneSplitAdapter is IExchangeAdapter { using SafeMath for uint256; event OneSplitAdapterSetup(address oneSplit, address priceOracle, uint256 splitParts); constructor() public { emit OneSplitAdapterSetup(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, 0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4, 10); } /// @notice "Infinite" approval for all the tokens initialized /// @param _tokens the list of token addresses to approve function approveExchange(IERC20[] calldata _tokens) external override { for (uint256 i = 0; i < _tokens.length; i++) { if (address(_tokens[i]) != EthAddressLib.ethAddress()) { _tokens[i].safeApprove(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, UintConstants.maxUintMinus1()); } } } /// @notice Exchanges _amount of _from token (or ETH) to _to token (or ETH) /// - Uses EthAddressLib.ethAddress() as the reference on 1Split of ETH /// @param _from The asset to exchange from /// @param _to The asset to exchange to /// @param _amount The amount to exchange /// @param _maxSlippage Max slippage acceptable, taken into account after the goodSwap() function exchange(address _from, address _to, uint256 _amount, uint256 _maxSlippage) external override returns(uint256) { uint256 _value = (_from == EthAddressLib.ethAddress()) ? _amount : 0; uint256 _fromAssetPriceInWei = IPriceOracleGetter(0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4).getAssetPrice(_from); uint256 _toAssetPriceInWei = IPriceOracleGetter(0x76B47460d7F7c5222cFb6b6A75615ab10895DDe4).getAssetPrice(_to); uint256 _toBalanceBefore = IERC20(_to).balanceOf(address(this)); IOneSplit(0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476).goodSwap{value: _value}( IERC20(_from), IERC20(_to), _amount, 0, 10, 512 ); uint256 _toReceivedAmount = IERC20(_to).balanceOf(address(this)).sub(_toBalanceBefore); require( (_toAssetPriceInWei.mul(_toReceivedAmount).mul(100)) .div(_fromAssetPriceInWei.mul(_amount)) >= (100 - _maxSlippage), "INVALID_SLIPPAGE" ); emit Exchange(_from, _to, 0x1814222fa8c8c1C1bf380e3BBFBd9De8657Da476, _amount, _toReceivedAmount); return _toReceivedAmount; } }