diff --git a/contracts/test-utils/src/web3_wrapper.ts b/contracts/test-utils/src/web3_wrapper.ts index 10040f1996..99bd69e7f5 100644 --- a/contracts/test-utils/src/web3_wrapper.ts +++ b/contracts/test-utils/src/web3_wrapper.ts @@ -17,6 +17,7 @@ export const txDefaults = { export const providerConfigs: Web3Config = { total_accounts: constants.NUM_TEST_ACCOUNTS, shouldUseInProcessGanache: true, + shouldUseFakeGasEstimate: false, shouldAllowUnlimitedContractSize: true, hardfork: 'istanbul', gasLimit: 100e6, diff --git a/contracts/zero-ex/CHANGELOG.json b/contracts/zero-ex/CHANGELOG.json index 6b357dfcf8..b7479345c3 100644 --- a/contracts/zero-ex/CHANGELOG.json +++ b/contracts/zero-ex/CHANGELOG.json @@ -1,4 +1,13 @@ [ + { + "timestamp": 1676384530, + "version": "0.38.7", + "changes": [ + { + "note": "Add support for Clober to bridge adapter" + } + ] + }, { "timestamp": 1675210931, "version": "0.38.6", diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/ArbitrumBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/ArbitrumBridgeAdapter.sol index 70714bb565..de2c19f910 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/ArbitrumBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/ArbitrumBridgeAdapter.sol @@ -29,11 +29,13 @@ import "./mixins/MixinUniswapV3.sol"; import "./mixins/MixinUniswapV2.sol"; import "./mixins/MixinWOOFi.sol"; import "./mixins/MixinZeroExBridge.sol"; +import "./mixins/MixinClober.sol"; contract ArbitrumBridgeAdapter is AbstractBridgeAdapter(42161, "Arbitrum"), MixinAaveV3, MixinBalancerV2Batch, + MixinClober, MixinCurve, MixinCurveV2, MixinDodoV2, @@ -45,7 +47,7 @@ contract ArbitrumBridgeAdapter is MixinWOOFi, MixinZeroExBridge { - constructor(IEtherTokenV06 weth) public MixinCurve(weth) MixinAaveV3(true) {} + constructor(IEtherTokenV06 weth) public MixinClober(weth) MixinCurve(weth) MixinAaveV3(true) {} function _trade( BridgeOrder memory order, @@ -115,6 +117,11 @@ contract ArbitrumBridgeAdapter is return (0, true); } boughtAmount = _tradeWOOFi(sellToken, buyToken, sellAmount, order.bridgeData); + } else if (protocolId == BridgeProtocols.CLOBER) { + if (dryRun) { + return (0, true); + } + boughtAmount = _tradeClober(sellToken, buyToken, sellAmount, order.bridgeData); } emit BridgeFill(order.source, sellToken, buyToken, sellAmount, boughtAmount); diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol index f2c4ce73fd..7ebaeace8b 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/BridgeProtocols.sol @@ -54,4 +54,5 @@ library BridgeProtocols { uint128 internal constant SYNTHETIX = 30; uint128 internal constant WOOFI = 31; uint128 internal constant AAVEV3 = 32; + uint128 internal constant CLOBER = 33; } diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/EthereumBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/EthereumBridgeAdapter.sol index bb911510fd..78c79b210c 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/EthereumBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/EthereumBridgeAdapter.sol @@ -39,6 +39,7 @@ import "./mixins/MixinUniswap.sol"; import "./mixins/MixinUniswapV2.sol"; import "./mixins/MixinUniswapV3.sol"; import "./mixins/MixinZeroExBridge.sol"; +import "./mixins/MixinClober.sol"; contract EthereumBridgeAdapter is AbstractBridgeAdapter(1, "Ethereum"), @@ -47,6 +48,7 @@ contract EthereumBridgeAdapter is MixinBalancerV2Batch, MixinBancor, MixinBancorV3, + MixinClober, MixinCompound, MixinCurve, MixinCurveV2, @@ -71,6 +73,7 @@ contract EthereumBridgeAdapter is public MixinBancor(weth) MixinBancorV3(weth) + MixinClober(weth) MixinCompound(weth) MixinCurve(weth) MixinLido(weth) @@ -195,6 +198,11 @@ contract EthereumBridgeAdapter is return (0, true); } boughtAmount = _tradeZeroExBridge(sellToken, buyToken, sellAmount, order.bridgeData); + } else if (protocolId == BridgeProtocols.CLOBER) { + if (dryRun) { + return (0, true); + } + boughtAmount = _tradeClober(sellToken, buyToken, sellAmount, order.bridgeData); } emit BridgeFill(order.source, sellToken, buyToken, sellAmount, boughtAmount); diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/PolygonBridgeAdapter.sol b/contracts/zero-ex/contracts/src/transformers/bridges/PolygonBridgeAdapter.sol index 9ccb827ac3..9b43428a4f 100644 --- a/contracts/zero-ex/contracts/src/transformers/bridges/PolygonBridgeAdapter.sol +++ b/contracts/zero-ex/contracts/src/transformers/bridges/PolygonBridgeAdapter.sol @@ -32,12 +32,14 @@ import "./mixins/MixinUniswapV2.sol"; import "./mixins/MixinUniswapV3.sol"; import "./mixins/MixinWOOFi.sol"; import "./mixins/MixinZeroExBridge.sol"; +import "./mixins/MixinClober.sol"; contract PolygonBridgeAdapter is AbstractBridgeAdapter(137, "Polygon"), MixinAaveV3, MixinAaveV2, MixinBalancerV2Batch, + MixinClober, MixinCurve, MixinCurveV2, MixinDodo, @@ -51,7 +53,7 @@ contract PolygonBridgeAdapter is MixinWOOFi, MixinZeroExBridge { - constructor(IEtherTokenV06 weth) public MixinCurve(weth) MixinAaveV3(false) {} + constructor(IEtherTokenV06 weth) public MixinClober(weth) MixinCurve(weth) MixinAaveV3(false) {} function _trade( BridgeOrder memory order, @@ -136,6 +138,11 @@ contract PolygonBridgeAdapter is return (0, true); } boughtAmount = _tradeAaveV3(sellToken, buyToken, sellAmount, order.bridgeData); + } else if (protocolId == BridgeProtocols.CLOBER) { + if (dryRun) { + return (0, true); + } + boughtAmount = _tradeClober(sellToken, buyToken, sellAmount, order.bridgeData); } emit BridgeFill(order.source, sellToken, buyToken, sellAmount, boughtAmount); diff --git a/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinClober.sol b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinClober.sol new file mode 100644 index 0000000000..5edb46005d --- /dev/null +++ b/contracts/zero-ex/contracts/src/transformers/bridges/mixins/MixinClober.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: Apache-2.0 +/* + Copyright 2023 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.5; +pragma experimental ABIEncoderV2; + +import "@0x/contracts-erc20/contracts/src/v06/LibERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IERC20TokenV06.sol"; +import "@0x/contracts-erc20/contracts/src/v06/IEtherTokenV06.sol"; +import "@0x/contracts-utils/contracts/src/v06/LibSafeMathV06.sol"; + +interface ICloberOrderBook { + function quoteToRaw(uint256 quoteAmount, bool roundingUp) external view returns (uint64 rawAmount); +} + +interface ICloberRouter { + struct MarketOrderParams { + address market; + uint64 deadline; + address user; + uint16 limitPriceIndex; + uint64 rawAmount; + bool expendInput; + bool useNative; + uint256 baseAmount; + } + + function marketBid(MarketOrderParams calldata params) external payable; + + function marketAsk(MarketOrderParams calldata params) external payable; +} + +contract MixinClober { + using LibERC20TokenV06 for IERC20TokenV06; + using LibSafeMathV06 for uint256; + + IEtherTokenV06 private immutable WETH; + + constructor(IEtherTokenV06 weth) public { + WETH = weth; + } + + function _tradeClober( + IERC20TokenV06 sellToken, + IERC20TokenV06 buyToken, + uint256 sellAmount, + bytes memory bridgeData + ) internal returns (uint256 boughtAmount) { + (ICloberRouter router, ICloberOrderBook market, bool isTakingBid) = abi.decode( + bridgeData, + (ICloberRouter, ICloberOrderBook, bool) + ); + + // Grant the Clober router an allowance to sell the sell token. + sellToken.approveIfBelow(address(router), sellAmount); + + bool useNative = address(sellToken) == address(WETH) ? true : false; + uint256 beforeBalance = buyToken.balanceOf(address(this)); + if (isTakingBid) { + router.marketAsk( + ICloberRouter.MarketOrderParams({ + market: address(market), + deadline: uint64(block.timestamp + 100), + user: msg.sender, + limitPriceIndex: 0, + rawAmount: 0, + expendInput: true, + useNative: useNative, + baseAmount: sellAmount + }) + ); + } else { + router.marketBid( + ICloberRouter.MarketOrderParams({ + market: address(market), + deadline: uint64(block.timestamp + 100), + user: msg.sender, + limitPriceIndex: type(uint16).max, + rawAmount: market.quoteToRaw(sellAmount, true), + expendInput: true, + useNative: useNative, + baseAmount: 0 + }) + ); + } + return buyToken.balanceOf(address(this)).safeSub(beforeBalance); + } +} diff --git a/packages/protocol-utils/src/transformer_utils.ts b/packages/protocol-utils/src/transformer_utils.ts index dd7cc46389..ab5f6e81d9 100644 --- a/packages/protocol-utils/src/transformer_utils.ts +++ b/packages/protocol-utils/src/transformer_utils.ts @@ -163,6 +163,7 @@ export enum BridgeProtocol { Synthetix, WOOFi, AaveV3, + Clober } /**