// SPDX-License-Identifier: Apache-2.0 /* Copyright 2021 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ pragma solidity ^0.6; pragma experimental ABIEncoderV2; import "./ApproximateBuys.sol"; import "./SamplerUtils.sol"; interface IDODOV2Registry { function getDODOPool(address baseToken, address quoteToken) external view returns (address[] memory machines); } interface IDODOV2Pool { function querySellBase(address trader, uint256 payBaseAmount) external view returns (uint256 receiveQuoteAmount, uint256 mtFee); function querySellQuote(address trader, uint256 payQuoteAmount) external view returns (uint256 receiveBaseAmount, uint256 mtFee); } contract DODOV2Sampler is SamplerUtils, ApproximateBuys { /// @dev Gas limit for DODO V2 calls. uint256 constant private DODO_V2_CALL_GAS = 300e3; // 300k /// @dev Sample sell quotes from DODO V2. /// @param registry Address of the registry to look up. /// @param offset offset index for the pool in the registry. /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param takerTokenAmounts Taker token sell amount for each sample. /// @return sellBase whether the bridge needs to sell the base token /// @return pool the DODO pool address /// @return makerTokenAmounts Maker amounts bought at each taker token /// amount. function sampleSellsFromDODOV2( address registry, uint256 offset, address takerToken, address makerToken, uint256[] memory takerTokenAmounts ) public view returns (bool sellBase, address pool, uint256[] memory makerTokenAmounts) { _assertValidPair(makerToken, takerToken); uint256 numSamples = takerTokenAmounts.length; makerTokenAmounts = new uint256[](numSamples); (pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken); if (pool == address(0)) { return (sellBase, pool, makerTokenAmounts); } for (uint256 i = 0; i < numSamples; i++) { uint256 buyAmount = _sampleSellForApproximateBuyFromDODOV2( abi.encode(takerToken, pool, sellBase), // taker token data abi.encode(makerToken, pool, sellBase), // maker token data takerTokenAmounts[i] ); makerTokenAmounts[i] = buyAmount; // Break early if there are 0 amounts if (makerTokenAmounts[i] == 0) { break; } } } /// @dev Sample buy quotes from DODO. /// @param registry Address of the registry to look up. /// @param offset offset index for the pool in the registry. /// @param takerToken Address of the taker token (what to sell). /// @param makerToken Address of the maker token (what to buy). /// @param makerTokenAmounts Maker token sell amount for each sample. /// @return sellBase whether the bridge needs to sell the base token /// @return pool the DODO pool address /// @return takerTokenAmounts Taker amounts sold at each maker token /// amount. function sampleBuysFromDODOV2( address registry, uint256 offset, address takerToken, address makerToken, uint256[] memory makerTokenAmounts ) public view returns (bool sellBase, address pool, uint256[] memory takerTokenAmounts) { _assertValidPair(makerToken, takerToken); (pool, sellBase) = _getNextDODOV2Pool(registry, offset, takerToken, makerToken); if (pool == address(0)) { return (sellBase, pool, takerTokenAmounts); } uint256 numSamples = makerTokenAmounts.length; takerTokenAmounts = new uint256[](numSamples); takerTokenAmounts = _sampleApproximateBuys( ApproximateBuyQuoteOpts({ makerTokenData: abi.encode(makerToken, pool, !sellBase), takerTokenData: abi.encode(takerToken, pool, sellBase), getSellQuoteCallback: _sampleSellForApproximateBuyFromDODOV2 }), makerTokenAmounts ); } function _sampleSellForApproximateBuyFromDODOV2( bytes memory takerTokenData, bytes memory /* makerTokenData */, uint256 sellAmount ) private view returns (uint256) { (address takerToken, address pool, bool sellBase) = abi.decode( takerTokenData, (address, address, bool) ); // We will get called to sell both the taker token and also to sell the maker token // since we use approximate buy for sell and buy functions if (sellBase) { try IDODOV2Pool(pool).querySellBase { gas: DODO_V2_CALL_GAS } (address(0), sellAmount) returns (uint256 amount, uint256) { return amount; } catch { return 0; } } else { try IDODOV2Pool(pool).querySellQuote { gas: DODO_V2_CALL_GAS } (address(0), sellAmount) returns (uint256 amount, uint256) { return amount; } catch { return 0; } } } function _getNextDODOV2Pool( address registry, uint256 offset, address takerToken, address makerToken ) internal view returns (address machine, bool sellBase) { // Query in base -> quote direction, if a pool is found then we are selling the base address[] memory machines = IDODOV2Registry(registry).getDODOPool(takerToken, makerToken); sellBase = true; if (machines.length == 0) { // Query in quote -> base direction, if a pool is found then we are selling the quote machines = IDODOV2Registry(registry).getDODOPool(makerToken, takerToken); sellBase = false; } if (offset >= machines.length) { return (address(0), false); } machine = machines[offset]; } }