diff --git a/contracts/arbitrum/connectors/0x/events.sol b/contracts/arbitrum/connectors/0x/events.sol new file mode 100644 index 00000000..84f4e543 --- /dev/null +++ b/contracts/arbitrum/connectors/0x/events.sol @@ -0,0 +1,13 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogSwap( + address indexed buyToken, + address indexed sellToken, + uint256 buyAmt, + uint256 sellAmt, + uint256 getId, + uint256 setId + ); +} diff --git a/contracts/arbitrum/connectors/0x/helpers.sol b/contracts/arbitrum/connectors/0x/helpers.sol new file mode 100644 index 00000000..f8b409b3 --- /dev/null +++ b/contracts/arbitrum/connectors/0x/helpers.sol @@ -0,0 +1,66 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import {TokenInterface} from "../../common/interfaces.sol"; +import {DSMath} from "../../common/math.sol"; +import {Basic} from "../../common/basic.sol"; +import {ZeroExData} from "./interface.sol"; + +contract Helpers is DSMath, Basic { + /** + * @dev 0x Address + */ + address internal constant zeroExAddr = + 0xDef1C0ded9bec7F1a1670819833240f027b25EfF; + + function _swapHelper(ZeroExData memory zeroExData, uint256 ethAmt) + internal + returns (uint256 buyAmt) + { + TokenInterface buyToken = zeroExData.buyToken; + (uint256 _buyDec, uint256 _sellDec) = getTokensDec( + buyToken, + zeroExData.sellToken + ); + uint256 _sellAmt18 = convertTo18(_sellDec, zeroExData._sellAmt); + uint256 _slippageAmt = convert18ToDec( + _buyDec, + wmul(zeroExData.unitAmt, _sellAmt18) + ); + + uint256 initalBal = getTokenBal(buyToken); + + // solium-disable-next-line security/no-call-value + (bool success, ) = zeroExAddr.call{value: ethAmt}(zeroExData.callData); + if (!success) revert("0x-swap-failed"); + + uint256 finalBal = getTokenBal(buyToken); + + buyAmt = sub(finalBal, initalBal); + + require(_slippageAmt <= buyAmt, "Too much slippage"); + } + + function _swap(ZeroExData memory zeroExData, uint256 setId) + internal + returns (ZeroExData memory) + { + TokenInterface _sellAddr = zeroExData.sellToken; + + uint256 ethAmt; + if (address(_sellAddr) == ethAddr) { + ethAmt = zeroExData._sellAmt; + } else { + approve( + TokenInterface(_sellAddr), + zeroExAddr, + zeroExData._sellAmt + ); + } + + zeroExData._buyAmt = _swapHelper(zeroExData, ethAmt); + setUint(setId, zeroExData._buyAmt); + + return zeroExData; + } +} diff --git a/contracts/arbitrum/connectors/0x/interface.sol b/contracts/arbitrum/connectors/0x/interface.sol new file mode 100644 index 00000000..2db561ef --- /dev/null +++ b/contracts/arbitrum/connectors/0x/interface.sol @@ -0,0 +1,13 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import {TokenInterface} from "../../common/interfaces.sol"; + +struct ZeroExData { + TokenInterface sellToken; + TokenInterface buyToken; + uint256 _sellAmt; + uint256 _buyAmt; + uint256 unitAmt; + bytes callData; +} diff --git a/contracts/arbitrum/connectors/0x/main.sol b/contracts/arbitrum/connectors/0x/main.sol new file mode 100644 index 00000000..03a66b33 --- /dev/null +++ b/contracts/arbitrum/connectors/0x/main.sol @@ -0,0 +1,64 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +/** + * @title 0x. + * @dev On-chain DEX Aggregator. + */ + +import {TokenInterface, MemoryInterface} from "../../common/interfaces.sol"; +import {Stores} from "../../common/stores.sol"; +import {ZeroExData} from "./interface.sol"; +import {Helpers} from "./helpers.sol"; +import {Events} from "./events.sol"; + +abstract contract ZeroEx is Helpers { + /** + * @notice Swap tokens on 0x + * @dev Sell ETH/ERC20_Token using 0x. + * @param buyAddr The address of the token to buy.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAddr The address of the token to sell.(For ETH: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param sellAmt The amount of the token to sell. + * @param unitAmt The amount of buyAmt/sellAmt with slippage. + * @param callData Data from 0x API. + * @param setId ID stores the amount of token brought. + */ + function swap( + address buyAddr, + address sellAddr, + uint256 sellAmt, + uint256 unitAmt, + bytes calldata callData, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + ZeroExData memory zeroExData = ZeroExData({ + buyToken: TokenInterface(buyAddr), + sellToken: TokenInterface(sellAddr), + unitAmt: unitAmt, + callData: callData, + _sellAmt: sellAmt, + _buyAmt: 0 + }); + + zeroExData = _swap(zeroExData, setId); + + _eventName = "LogSwap(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + buyAddr, + sellAddr, + zeroExData._buyAmt, + zeroExData._sellAmt, + 0, + setId + ); + } +} + +contract ConnectV2ZeroExArbitrum is ZeroEx { + string public name = "0x-V4"; +}