Skip to content

Commit

Permalink
Merge pull request argentlabs#252 from argentlabs/paraswap-uniV3
Browse files Browse the repository at this point in the history
Paraswap UniV3 Filters
  • Loading branch information
olivdb authored Jun 14, 2021
2 parents 4f0a21f + 98915a0 commit 79b60e2
Show file tree
Hide file tree
Showing 17 changed files with 1,236 additions and 125 deletions.
26 changes: 23 additions & 3 deletions contracts/infrastructure/dapp/paraswap/ParaswapFilter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ interface IParaswap {
address[] path;
}

struct UniswapV3Data {
uint24 fee;
uint256 deadline;
uint160 sqrtPriceLimitX96;
}

function getUniswapProxy() external view returns (address);
}

Expand Down Expand Up @@ -117,6 +123,7 @@ contract ParaswapFilter is BaseFilter {
address public immutable zeroExV4Adapter;
address public immutable curveAdapter;
address public immutable wethAdapter;
address public immutable uniV3Adapter;
// The Dapp registry (used to authorise simpleSwap())
IAuthoriser public immutable authoriser;
// Uniswap Proxy used by Paraswap's AugustusSwapper contract
Expand All @@ -133,19 +140,21 @@ contract ParaswapFilter is BaseFilter {
address public immutable uniForkFactory1; // sushiswap
address public immutable uniForkFactory2; // linkswap
address public immutable uniForkFactory3; // defiswap
address public immutable uniV3Factory; // uniV3
bytes32 public immutable uniInitCode; // uniswap
bytes32 public immutable uniForkInitCode1; // sushiswap
bytes32 public immutable uniForkInitCode2; // linkswap
bytes32 public immutable uniForkInitCode3; // defiswap
bytes32 public immutable uniV3InitCode; // uniV3

constructor(
address _tokenRegistry,
IAuthoriser _authoriser,
address _augustus,
address _uniswapProxy,
address[3] memory _uniFactories,
bytes32[3] memory _uniInitCodes,
address[9] memory _adapters,
address[4] memory _uniFactories,
bytes32[4] memory _uniInitCodes,
address[10] memory _adapters,
address[] memory _targetExchanges,
address[] memory _marketMakers
) {
Expand All @@ -159,9 +168,11 @@ contract ParaswapFilter is BaseFilter {
uniForkFactory1 = _uniFactories[0];
uniForkFactory2 = _uniFactories[1];
uniForkFactory3 = _uniFactories[2];
uniV3Factory = _uniFactories[3];
uniForkInitCode1 = _uniInitCodes[0];
uniForkInitCode2 = _uniInitCodes[1];
uniForkInitCode3 = _uniInitCodes[2];
uniV3InitCode = _uniInitCodes[3];
uniV1Adapter = _adapters[0];
uniV2Adapter = _adapters[1];
sushiswapAdapter = _adapters[2];
Expand All @@ -171,6 +182,7 @@ contract ParaswapFilter is BaseFilter {
zeroExV4Adapter = _adapters[6];
curveAdapter = _adapters[7];
wethAdapter = _adapters[8];
uniV3Adapter = _adapters[9];
for(uint i = 0; i < _targetExchanges.length; i++) {
targetExchanges[_targetExchanges[i]] = true;
}
Expand Down Expand Up @@ -338,6 +350,9 @@ contract ParaswapFilter is BaseFilter {
if(_route.exchange == zeroExV2Adapter) {
return hasValidZeroExV2Route(_route.payload);
}
if(_route.exchange == uniV3Adapter) {
return hasValidUniV3Route(_route.payload, _fromToken, _toToken);
}
if(_route.exchange == curveAdapter) {
return true;
}
Expand All @@ -353,6 +368,11 @@ contract ParaswapFilter is BaseFilter {
return false;
}

function hasValidUniV3Route(bytes memory _payload, address _fromToken, address _toToken) internal view returns (bool) {
IParaswap.UniswapV3Data memory data = abi.decode(_payload, (IParaswap.UniswapV3Data));
return ParaswapUtils.hasValidUniV3Pool(_fromToken, _toToken, data.fee, tokenRegistry, uniV3Factory, uniV3InitCode, weth);
}

function hasValidUniV2Route(bytes memory _payload, address _factory, bytes32 _initCode) internal view returns (bool) {
IParaswap.UniswapV2Data memory data = abi.decode(_payload, (IParaswap.UniswapV2Data));
return ParaswapUtils.hasValidUniV2Path(data.path, tokenRegistry, _factory, _initCode, weth);
Expand Down
48 changes: 46 additions & 2 deletions contracts/infrastructure/dapp/paraswap/ParaswapUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,23 @@ library ParaswapUtils {
ZeroExV4Signature signature;
}

function hasValidUniV3Pool(
address _fromToken,
address _toToken,
uint24 _fee,
address _tokenRegistry,
address _factory,
bytes32 _initCode,
address _weth
)
internal
view
returns (bool)
{
address poolToken = uniV3PoolFor(_fromToken, _toToken, _fee, _factory, _initCode, _weth);
return hasTradableToken(_tokenRegistry, poolToken);
}

function hasValidUniV2Path(
address[] memory _path,
address _tokenRegistry,
Expand All @@ -81,12 +98,34 @@ library ParaswapUtils {
{
address[] memory lpTokens = new address[](_path.length - 1);
for(uint i = 0; i < lpTokens.length; i++) {
lpTokens[i] = pairFor(_path[i], _path[i+1], _factory, _initCode, _weth);
lpTokens[i] = uniV2PairFor(_path[i], _path[i+1], _factory, _initCode, _weth);
}
return hasTradableTokens(_tokenRegistry, lpTokens);
}

function pairFor(address _tokenA, address _tokenB, address _factory, bytes32 _initCode, address _weth) internal pure returns (address) {
function uniV3PoolFor(
address _tokenA,
address _tokenB,
uint24 _fee,
address _factory,
bytes32 _initCode,
address _weth
)
internal
pure
returns (address)
{
(address tokenA, address tokenB) = (_tokenA == ETH_TOKEN ? _weth : _tokenA, _tokenB == ETH_TOKEN ? _weth : _tokenB);
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
return(address(uint160(uint(keccak256(abi.encodePacked(
hex"ff",
_factory,
keccak256(abi.encode(token0, token1, _fee)),
_initCode
))))));
}

function uniV2PairFor(address _tokenA, address _tokenB, address _factory, bytes32 _initCode, address _weth) internal pure returns (address) {
(address tokenA, address tokenB) = (_tokenA == ETH_TOKEN ? _weth : _tokenA, _tokenB == ETH_TOKEN ? _weth : _tokenB);
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
return(address(uint160(uint(keccak256(abi.encodePacked(
Expand All @@ -100,5 +139,10 @@ library ParaswapUtils {
function hasTradableTokens(address _tokenRegistry, address[] memory _tokens) internal view returns (bool) {
(bool success, bytes memory res) = _tokenRegistry.staticcall(abi.encodeWithSignature("areTokensTradable(address[])", _tokens));
return success && abi.decode(res, (bool));

}
function hasTradableToken(address _tokenRegistry, address _token) internal view returns (bool) {
(bool success, bytes memory res) = _tokenRegistry.staticcall(abi.encodeWithSignature("isTokenTradable(address)", _token));
return success && abi.decode(res, (bool));
}
}
64 changes: 64 additions & 0 deletions contracts/infrastructure/dapp/paraswap/UniswapV3RouterFilter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright (C) 2021 Argent Labs Ltd. <https://argent.xyz>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.3;

import "../BaseFilter.sol";
import "./ParaswapUtils.sol";

/**
* @title UniswapV3RouterFilter
* @notice Filter used for calls to UniswapV3 router (aka "SwapRouter").
SwapRouter is deployed at 0xE592427A0AEce92De3Edee1F18E0157C05861564
* @author Olivier VDB - <olivier@argent.xyz>
*/
contract UniswapV3RouterFilter is BaseFilter {

bytes4 private constant SELL_SINGLE = bytes4(keccak256("exactInputSingle((address,address,uint24,address,uint256,uint256,uint256,uint160))"));
bytes4 private constant ERC20_APPROVE = bytes4(keccak256("approve(address,uint256)"));

// The token registry
address public immutable tokenRegistry;
// The UniV3 factory
address public immutable factory;
// The UniV3 pool initCode
bytes32 public immutable initCode;
// The WETH address
address public immutable weth;

constructor(address _tokenRegistry, address _factory, bytes32 _initCode, address _weth) {
tokenRegistry = _tokenRegistry;
factory = _factory;
initCode = _initCode;
weth = _weth;
}

function isValid(address _wallet, address _spender, address _to, bytes calldata _data) external view override returns (bool valid) {
// disable ETH transfer
if (_data.length < 4) {
return false;
}

bytes4 methodId = getMethod(_data);

if(methodId == SELL_SINGLE) {
(address tokenFrom, address tokenTo, uint24 fee, address recipient) = abi.decode(_data[4:], (address, address, uint24, address));
return ParaswapUtils.hasValidUniV3Pool(tokenFrom, tokenTo, fee, tokenRegistry, factory, initCode, weth) && recipient == _wallet;
}

return methodId == ERC20_APPROVE && _spender != _to;
}
}
5 changes: 3 additions & 2 deletions deployment/3_deploy_dapp_infrastructure.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ const main = async () => {
DappRegistryWrapper.address,
config.defi.paraswap.contract,
config.defi.paraswap.uniswapProxy,
config.defi.paraswap.uniswapForks.map((f) => f.factory),
config.defi.paraswap.uniswapForks.map((f) => f.initCode),
[...config.defi.paraswap.uniswapForks.map((f) => f.factory), config.defi.uniswap.factoryV3],
[...config.defi.paraswap.uniswapForks.map((f) => f.initCode), config.defi.uniswap.initCodeV3],
[
config.defi.paraswap.adapters.uniswap || ethers.constants.AddressZero,
config.defi.paraswap.adapters.uniswapV2 || ethers.constants.AddressZero,
Expand All @@ -97,6 +97,7 @@ const main = async () => {
config.defi.paraswap.adapters.zeroexV4 || ethers.constants.AddressZero,
config.defi.paraswap.adapters.curve || ethers.constants.AddressZero,
config.defi.paraswap.adapters.weth || ethers.constants.AddressZero,
config.defi.paraswap.adapters.uniswapV3 || ethers.constants.AddressZero,
],
[].concat(...Object.values(config.defi.paraswap.targetExchanges || {})), // flattened targetExchanges values
config.defi.paraswap.marketMakers || [],
Expand Down
35 changes: 35 additions & 0 deletions lib_0.7/paraswap/V4/lib/uniswapv3/ISwapRouterUniV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pragma solidity 0.7.5;
pragma abicoder v2;

interface ISwapRouterUniV3 {

struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}

struct ExactOutputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountOut;
uint256 amountInMaximum;
uint160 sqrtPriceLimitX96;
}

function exactInputSingle(ExactInputSingleParams calldata params)
external payable
returns (uint256 amountOut);

function exactOutputSingle(ExactOutputSingleParams calldata params)
external payable returns (uint256 amountIn);

}
Loading

0 comments on commit 79b60e2

Please sign in to comment.