From e90507f82f78f08e5277be12a41e6b3425255c1e Mon Sep 17 00:00:00 2001 From: bhavik-m Date: Sun, 27 Feb 2022 19:03:55 +0530 Subject: [PATCH 1/7] added connector on arb --- .../uniswap/v3_auto_router/events.sol | 12 +++ .../uniswap/v3_auto_router/helpers.sol | 80 +++++++++++++++++++ .../uniswap/v3_auto_router/interface.sol | 12 +++ .../uniswap/v3_auto_router/main.sol | 64 +++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 contracts/arbitrum/connectors/uniswap/v3_auto_router/events.sol create mode 100644 contracts/arbitrum/connectors/uniswap/v3_auto_router/helpers.sol create mode 100644 contracts/arbitrum/connectors/uniswap/v3_auto_router/interface.sol create mode 100644 contracts/arbitrum/connectors/uniswap/v3_auto_router/main.sol diff --git a/contracts/arbitrum/connectors/uniswap/v3_auto_router/events.sol b/contracts/arbitrum/connectors/uniswap/v3_auto_router/events.sol new file mode 100644 index 00000000..12a4fd3c --- /dev/null +++ b/contracts/arbitrum/connectors/uniswap/v3_auto_router/events.sol @@ -0,0 +1,12 @@ +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/uniswap/v3_auto_router/helpers.sol b/contracts/arbitrum/connectors/uniswap/v3_auto_router/helpers.sol new file mode 100644 index 00000000..3e73bc68 --- /dev/null +++ b/contracts/arbitrum/connectors/uniswap/v3_auto_router/helpers.sol @@ -0,0 +1,80 @@ +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../../common/interfaces.sol"; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { SwapData } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev UniswapV3 Swap Router Address + */ + address internal constant V3_SWAP_ROUTER_ADDRESS = + 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45; + + /** + * @dev UniswapV3 swapHelper + * @param swapData - Struct defined in interfaces.sol + */ + function _swapHelper(SwapData memory swapData) + internal + returns (uint256 buyAmt) + { + (uint256 _buyDec, uint256 _sellDec) = getTokensDec( + swapData.buyToken, + swapData.sellToken + ); + uint256 _sellAmt18 = convertTo18(_sellDec, swapData._sellAmt); + uint256 _slippageAmt = convert18ToDec( + _buyDec, + wmul(swapData.unitAmt, _sellAmt18) + ); + + uint256 initalBal = getTokenBal(swapData.buyToken); + + // solium-disable-next-line security/no-call-value + (bool success, ) = V3_SWAP_ROUTER_ADDRESS.call(swapData.callData); + if (!success) revert("uniswapV3-swap-failed"); + + uint256 finalBal = getTokenBal(swapData.buyToken); + + buyAmt = sub(finalBal, initalBal); + require(_slippageAmt <= buyAmt, "Too much slippage"); + } + + /** + * @dev Gets the swapping data from auto router sdk + * @param swapData Struct with multiple swap data defined in interfaces.sol + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function _swap(SwapData memory swapData, uint256 setId) + internal + returns (SwapData memory) + { + bool isEthSellToken = address(swapData.sellToken) == ethAddr; + bool isEthBuyToken = address(swapData.buyToken) == ethAddr; + + swapData.sellToken = isEthSellToken + ? TokenInterface(wethAddr) + : swapData.sellToken; + swapData.buyToken = isEthBuyToken + ? TokenInterface(wethAddr) + : swapData.buyToken; + + convertEthToWeth(isEthSellToken, swapData.sellToken, swapData._sellAmt); + + approve( + TokenInterface(swapData.sellToken), + V3_SWAP_ROUTER_ADDRESS, + swapData._sellAmt + ); + + swapData._buyAmt = _swapHelper(swapData); + + convertWethToEth(isEthBuyToken, swapData.buyToken, swapData._buyAmt); + + setUint(setId, swapData._buyAmt); + + return swapData; + } +} diff --git a/contracts/arbitrum/connectors/uniswap/v3_auto_router/interface.sol b/contracts/arbitrum/connectors/uniswap/v3_auto_router/interface.sol new file mode 100644 index 00000000..3482cdd3 --- /dev/null +++ b/contracts/arbitrum/connectors/uniswap/v3_auto_router/interface.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../../common/interfaces.sol"; + +struct SwapData { + TokenInterface sellToken; + TokenInterface buyToken; + uint256 _sellAmt; + uint256 _buyAmt; + uint256 unitAmt; + bytes callData; +} diff --git a/contracts/arbitrum/connectors/uniswap/v3_auto_router/main.sol b/contracts/arbitrum/connectors/uniswap/v3_auto_router/main.sol new file mode 100644 index 00000000..5677b6f5 --- /dev/null +++ b/contracts/arbitrum/connectors/uniswap/v3_auto_router/main.sol @@ -0,0 +1,64 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +/** + * @title UniswapV3_autoRouter. + * @dev DEX. + */ + +// import files from common directory +import { TokenInterface, MemoryInterface } from "../../../common/interfaces.sol"; +import { Stores } from "../../../common/stores.sol"; +import { SwapData } from "./interface.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract AutoRouter is Helpers, Events { + /** + * @dev Sell ETH/ERC20_Token using uniswap v3 auto router. + * @notice Swap tokens from getting an optimized trade routes + * @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 Uniswap V3 auto router SDK. + * @param setId ID stores the amount of token brought. + */ + function sell( + address buyAddr, + address sellAddr, + uint256 sellAmt, + uint256 unitAmt, + bytes calldata callData, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + SwapData memory swapData = SwapData({ + buyToken: TokenInterface(buyAddr), + sellToken: TokenInterface(sellAddr), + unitAmt: unitAmt, + callData: callData, + _sellAmt: sellAmt, + _buyAmt: 0 + }); + + swapData = _swap(swapData, setId); + + _eventName = "LogSwap(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + buyAddr, + sellAddr, + swapData._buyAmt, + swapData._sellAmt, + 0, + setId + ); + } +} + +contract ConnectV2UniswapV3AutoRouterArbitrum is AutoRouter { + string public name = "UniswapV3-Auto-Router-v1"; +} From 84a39b1582c9ffbeb889ab16294e2ecba4877256 Mon Sep 17 00:00:00 2001 From: bhavik-m Date: Sun, 27 Feb 2022 19:04:29 +0530 Subject: [PATCH 2/7] added connector on opt --- .../uniswap/v3_auto_router/events.sol | 12 +++ .../uniswap/v3_auto_router/helpers.sol | 80 +++++++++++++++++++ .../uniswap/v3_auto_router/interface.sol | 12 +++ .../uniswap/v3_auto_router/main.sol | 64 +++++++++++++++ 4 files changed, 168 insertions(+) create mode 100644 contracts/optimism/connectors/uniswap/v3_auto_router/events.sol create mode 100644 contracts/optimism/connectors/uniswap/v3_auto_router/helpers.sol create mode 100644 contracts/optimism/connectors/uniswap/v3_auto_router/interface.sol create mode 100644 contracts/optimism/connectors/uniswap/v3_auto_router/main.sol diff --git a/contracts/optimism/connectors/uniswap/v3_auto_router/events.sol b/contracts/optimism/connectors/uniswap/v3_auto_router/events.sol new file mode 100644 index 00000000..12a4fd3c --- /dev/null +++ b/contracts/optimism/connectors/uniswap/v3_auto_router/events.sol @@ -0,0 +1,12 @@ +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/optimism/connectors/uniswap/v3_auto_router/helpers.sol b/contracts/optimism/connectors/uniswap/v3_auto_router/helpers.sol new file mode 100644 index 00000000..3e73bc68 --- /dev/null +++ b/contracts/optimism/connectors/uniswap/v3_auto_router/helpers.sol @@ -0,0 +1,80 @@ +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../../common/interfaces.sol"; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { SwapData } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + /** + * @dev UniswapV3 Swap Router Address + */ + address internal constant V3_SWAP_ROUTER_ADDRESS = + 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45; + + /** + * @dev UniswapV3 swapHelper + * @param swapData - Struct defined in interfaces.sol + */ + function _swapHelper(SwapData memory swapData) + internal + returns (uint256 buyAmt) + { + (uint256 _buyDec, uint256 _sellDec) = getTokensDec( + swapData.buyToken, + swapData.sellToken + ); + uint256 _sellAmt18 = convertTo18(_sellDec, swapData._sellAmt); + uint256 _slippageAmt = convert18ToDec( + _buyDec, + wmul(swapData.unitAmt, _sellAmt18) + ); + + uint256 initalBal = getTokenBal(swapData.buyToken); + + // solium-disable-next-line security/no-call-value + (bool success, ) = V3_SWAP_ROUTER_ADDRESS.call(swapData.callData); + if (!success) revert("uniswapV3-swap-failed"); + + uint256 finalBal = getTokenBal(swapData.buyToken); + + buyAmt = sub(finalBal, initalBal); + require(_slippageAmt <= buyAmt, "Too much slippage"); + } + + /** + * @dev Gets the swapping data from auto router sdk + * @param swapData Struct with multiple swap data defined in interfaces.sol + * @param setId Set token amount at this ID in `InstaMemory` Contract. + */ + function _swap(SwapData memory swapData, uint256 setId) + internal + returns (SwapData memory) + { + bool isEthSellToken = address(swapData.sellToken) == ethAddr; + bool isEthBuyToken = address(swapData.buyToken) == ethAddr; + + swapData.sellToken = isEthSellToken + ? TokenInterface(wethAddr) + : swapData.sellToken; + swapData.buyToken = isEthBuyToken + ? TokenInterface(wethAddr) + : swapData.buyToken; + + convertEthToWeth(isEthSellToken, swapData.sellToken, swapData._sellAmt); + + approve( + TokenInterface(swapData.sellToken), + V3_SWAP_ROUTER_ADDRESS, + swapData._sellAmt + ); + + swapData._buyAmt = _swapHelper(swapData); + + convertWethToEth(isEthBuyToken, swapData.buyToken, swapData._buyAmt); + + setUint(setId, swapData._buyAmt); + + return swapData; + } +} diff --git a/contracts/optimism/connectors/uniswap/v3_auto_router/interface.sol b/contracts/optimism/connectors/uniswap/v3_auto_router/interface.sol new file mode 100644 index 00000000..3482cdd3 --- /dev/null +++ b/contracts/optimism/connectors/uniswap/v3_auto_router/interface.sol @@ -0,0 +1,12 @@ +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../../common/interfaces.sol"; + +struct SwapData { + TokenInterface sellToken; + TokenInterface buyToken; + uint256 _sellAmt; + uint256 _buyAmt; + uint256 unitAmt; + bytes callData; +} diff --git a/contracts/optimism/connectors/uniswap/v3_auto_router/main.sol b/contracts/optimism/connectors/uniswap/v3_auto_router/main.sol new file mode 100644 index 00000000..be7b8235 --- /dev/null +++ b/contracts/optimism/connectors/uniswap/v3_auto_router/main.sol @@ -0,0 +1,64 @@ +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +/** + * @title UniswapV3_autoRouter. + * @dev DEX. + */ + +// import files from common directory +import { TokenInterface, MemoryInterface } from "../../../common/interfaces.sol"; +import { Stores } from "../../../common/stores.sol"; +import { SwapData } from "./interface.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract AutoRouter is Helpers, Events { + /** + * @dev Sell ETH/ERC20_Token using uniswap v3 auto router. + * @notice Swap tokens from getting an optimized trade routes + * @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 Uniswap V3 auto router SDK. + * @param setId ID stores the amount of token brought. + */ + function sell( + address buyAddr, + address sellAddr, + uint256 sellAmt, + uint256 unitAmt, + bytes calldata callData, + uint256 setId + ) + external + payable + returns (string memory _eventName, bytes memory _eventParam) + { + SwapData memory swapData = SwapData({ + buyToken: TokenInterface(buyAddr), + sellToken: TokenInterface(sellAddr), + unitAmt: unitAmt, + callData: callData, + _sellAmt: sellAmt, + _buyAmt: 0 + }); + + swapData = _swap(swapData, setId); + + _eventName = "LogSwap(address,address,uint256,uint256,uint256,uint256)"; + _eventParam = abi.encode( + buyAddr, + sellAddr, + swapData._buyAmt, + swapData._sellAmt, + 0, + setId + ); + } +} + +contract ConnectV2UniswapV3AutoRouterOptimism is AutoRouter { + string public name = "UniswapV3-Auto-Router-v1"; +} From 1aa35df6b77778c5e831c9d1c79e64345a9a4626 Mon Sep 17 00:00:00 2001 From: bhavik-m Date: Sun, 27 Feb 2022 19:13:35 +0530 Subject: [PATCH 3/7] added opt scan api key support --- hardhat.config.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index d3bd78b9..1b5b30a4 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -13,6 +13,7 @@ import { HardhatUserConfig } from "hardhat/config"; import { NetworkUserConfig } from "hardhat/types"; import { utils } from "ethers"; import Web3 from "web3"; +import { network } from "hardhat"; dotenvConfig({ path: resolve(__dirname, "./.env") }); @@ -35,6 +36,7 @@ const PRIVATE_KEY = process.env.PRIVATE_KEY; const ETHERSCAN_API = process.env.ETHERSCAN_API_KEY; const POLYGONSCAN_API = process.env.POLYGON_API_KEY; const ARBISCAN_API = process.env.ARBISCAN_API_KEY; +const OPTIMISM_API = process.env.OPTIMISM_API_KEY; const SNOWTRACE_API = process.env.SNOWTRACE_API_KEY; const mnemonic = process.env.MNEMONIC ?? @@ -51,7 +53,7 @@ function createConfig(network: string) { return { url: getNetworkUrl(network), accounts: !!PRIVATE_KEY ? [`0x${PRIVATE_KEY}`] : { mnemonic }, - // gasPrice: 1000000, // 0.0001 GWEI + gasPrice: 1000000, // 0.0001 GWEI }; } @@ -71,6 +73,7 @@ function getScanApiKey(networkType: string) { if (networkType === "avalanche") return SNOWTRACE_API; else if (networkType === "polygon") return POLYGONSCAN_API; else if (networkType === "arbitrum") return ARBISCAN_API; + else if (networkType === "optimism") return OPTIMISM_API; else return ETHERSCAN_API; } @@ -122,8 +125,8 @@ const config: HardhatUserConfig = { sources: "./contracts", tests: "./test", }, - etherscan: { - apiKey: getScanApiKey(String(process.env.networkType)), + etherscan: { + apiKey: getScanApiKey(String(process.env.networkType)), }, typechain: { outDir: "typechain", From 6656bc74122c5990520f79b7281c7ca8f56b0967 Mon Sep 17 00:00:00 2001 From: Aleksandr S Date: Thu, 3 Mar 2022 17:09:43 +0300 Subject: [PATCH 4/7] use ts-node for check run --- package.json | 4 ++-- status-checks/check.ts | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 2e73b9a1..17f954a6 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "scripts": { "test": "hardhat run scripts/tests/global-test.ts", "coverage": "./node_modules/.bin/solidity-coverage", - "check": "node status-checks/huskyCheck.js", - "check-husky": "node status-checks/huskyCheck.js", + "check": "ts-node status-checks/huskyCheck.ts", + "check-husky": "ts-node status-checks/huskyCheck.ts", "deploy": "node scripts/deployConnectorsFromCmd.js", "test:runner": "hardhat run scripts/tests/run-tests.ts", "typechain": "hardhat typechain", diff --git a/status-checks/check.ts b/status-checks/check.ts index 371c3825..462c17e2 100644 --- a/status-checks/check.ts +++ b/status-checks/check.ts @@ -92,7 +92,7 @@ const checkEvents = async (connector: { const eventsPath = `${connector.path}/events.sol`; const mainPath = `${connector.path}/main.sol`; if (connector.events.length) { - const eventNames = []; + const eventNames:string[] = []; for (let i1 = 0; i1 < connector.mainEvents.length; i1++) { const mainEvent = connector.mainEvents[i1]; const name = mainEvent.split("(")[0]; @@ -131,7 +131,7 @@ const checkEvents = async (connector: { } } if (connector.mainEvents.length < connector.events.length) { - const deprecatedEvents = connector.events.filter((e) => { + const deprecatedEvents = connector.events.filter((e: string) => { let used = false; for (let index = 0; index < eventNames.length; index++) { if (e.split("(")[0].split(" ")[1] === eventNames[index]) @@ -157,7 +157,7 @@ const checkEvents = async (connector: { const getCommments = async (strs: string | any[]) => { try { const comments = []; - let type: string; + let type: string = ''; for (let index = strs.length - 1; index >= 0; index--) { const str = strs[index]; if (!type) { @@ -194,9 +194,9 @@ const parseCode = async (connector: { path: any; code?: any }) => { const eventsFirstLines = []; let func = []; let funcs = []; - let event = []; - let mainEvents = []; - let firstLine: number; + let event: string[] = []; + let mainEvents: string[] = []; + let firstLine: number = -1; let mainEventsLines = []; for (let index = 0; index < strs.length; index++) { const str = strs[index]; @@ -297,7 +297,7 @@ const parseCode = async (connector: { path: any; code?: any }) => { } }; -const checkComments = async (connector) => { +const checkComments = async (connector:any) => { try { const errors = []; for (let i1 = 0; i1 < connector.funcs.length; i1++) { From 9d3775565d0a49eded03c13337b551d0e598bb86 Mon Sep 17 00:00:00 2001 From: Aleksandr S Date: Thu, 3 Mar 2022 17:10:19 +0300 Subject: [PATCH 5/7] use comment:string --- status-checks/check.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/status-checks/check.ts b/status-checks/check.ts index 462c17e2..4d905f9d 100644 --- a/status-checks/check.ts +++ b/status-checks/check.ts @@ -317,7 +317,7 @@ const checkComments = async (connector:any) => { } const reqs = ["@dev", "@notice"]; for (let i3 = 0; i3 < reqs.length; i3++) { - if (!func.comments.some((comment) => comment.startsWith(reqs[i3]))) { + if (!func.comments.some((comment:string) => comment.startsWith(reqs[i3]))) { errors.push( `no ${reqs[i3]} for function ${func.name} at ${connector.path}/main.sol:${func.firstLine}` ); From bab582ed113263a27d1abafe91385f547badd8a9 Mon Sep 17 00:00:00 2001 From: Aleksandr S Date: Thu, 3 Mar 2022 17:15:04 +0300 Subject: [PATCH 6/7] workflow update --- .github/workflows/status.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/status.yml b/.github/workflows/status.yml index 33f4e990..c32c91f0 100644 --- a/.github/workflows/status.yml +++ b/.github/workflows/status.yml @@ -33,7 +33,7 @@ jobs: id: status_check run: | # Run status checks, Remove ANSI colors from the text - output=$(node ./status-checks | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g') + output=$(ts-node ./status-checks/index.ts | sed 's/\x1B\[[0-9;]\{1,\}[A-Za-z]//g') # Escape newlines so _all_ the output is included in the set-output output="${output//'%'/'%25'}" output="${output//$'\n'/'%0A'}" From 221d2caf0158e4d7749dc0cbdd88167ef4ea54d5 Mon Sep 17 00:00:00 2001 From: Aleksandr S Date: Thu, 3 Mar 2022 18:13:28 +0300 Subject: [PATCH 7/7] test commit --- status-checks/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/status-checks/index.ts b/status-checks/index.ts index e3c91c00..97170601 100644 --- a/status-checks/index.ts +++ b/status-checks/index.ts @@ -11,6 +11,7 @@ function getCurrentCommitSha() { .toString() .trim(); } + // The SHA provied by GITHUB_SHA is the merge (PR) commit. // We need to get the current commit sha ourself. const sha = getCurrentCommitSha();