Remove IERC20 from ISwapAdapter and use SafeERC20 for IERC20

This commit is contained in:
pistomat
2024-03-27 13:14:24 +01:00
parent c1143c2601
commit dfa731a93d
9 changed files with 173 additions and 165 deletions

View File

@@ -1,7 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import {IERC20, ISwapAdapter} from "src/interfaces/ISwapAdapter.sol"; import {ISwapAdapter} from "src/interfaces/ISwapAdapter.sol";
import {
IERC20,
SafeERC20
} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
// Maximum Swap In/Out Ratio - 0.3 // Maximum Swap In/Out Ratio - 0.3
// https://balancer.gitbook.io/balancer/core-concepts/protocol/limitations#v2-limits // https://balancer.gitbook.io/balancer/core-concepts/protocol/limitations#v2-limits
@@ -9,6 +13,8 @@ uint256 constant RESERVE_LIMIT_FACTOR = 4;
uint256 constant SWAP_DEADLINE_SEC = 1000; uint256 constant SWAP_DEADLINE_SEC = 1000;
contract BalancerV2SwapAdapter is ISwapAdapter { contract BalancerV2SwapAdapter is ISwapAdapter {
using SafeERC20 for IERC20;
IVault immutable vault; IVault immutable vault;
constructor(address payable vault_) { constructor(address payable vault_) {
@@ -27,8 +33,8 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
/// as a Fraction struct. /// as a Fraction struct.
function priceSingle( function priceSingle(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
uint256 sellAmount uint256 sellAmount
) public returns (Fraction memory calculatedPrice) { ) public returns (Fraction memory calculatedPrice) {
IVault.BatchSwapStep[] memory swapSteps = new IVault.BatchSwapStep[](1); IVault.BatchSwapStep[] memory swapSteps = new IVault.BatchSwapStep[](1);
@@ -40,8 +46,8 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
userData: "" userData: ""
}); });
address[] memory assets = new address[](2); address[] memory assets = new address[](2);
assets[0] = address(sellToken); assets[0] = sellToken;
assets[1] = address(buyToken); assets[1] = buyToken;
IVault.FundManagement memory funds = IVault.FundManagement({ IVault.FundManagement memory funds = IVault.FundManagement({
sender: msg.sender, sender: msg.sender,
fromInternalBalance: false, fromInternalBalance: false,
@@ -63,8 +69,8 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
function getSellAmount( function getSellAmount(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
uint256 buyAmount uint256 buyAmount
) public returns (uint256 sellAmount) { ) public returns (uint256 sellAmount) {
IVault.BatchSwapStep[] memory swapSteps = new IVault.BatchSwapStep[](1); IVault.BatchSwapStep[] memory swapSteps = new IVault.BatchSwapStep[](1);
@@ -76,8 +82,8 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
userData: "" userData: ""
}); });
address[] memory assets = new address[](2); address[] memory assets = new address[](2);
assets[0] = address(sellToken); assets[0] = sellToken;
assets[1] = address(buyToken); assets[1] = buyToken;
IVault.FundManagement memory funds = IVault.FundManagement({ IVault.FundManagement memory funds = IVault.FundManagement({
sender: msg.sender, sender: msg.sender,
fromInternalBalance: false, fromInternalBalance: false,
@@ -96,8 +102,8 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
function priceBatch( function priceBatch(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
uint256[] memory specifiedAmounts uint256[] memory specifiedAmounts
) external returns (Fraction[] memory calculatedPrices) { ) external returns (Fraction[] memory calculatedPrices) {
for (uint256 i = 0; i < specifiedAmounts.length; i++) { for (uint256 i = 0; i < specifiedAmounts.length; i++) {
@@ -106,7 +112,7 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
} }
} }
function price(bytes32, IERC20, IERC20, uint256[] memory) function price(bytes32, address, address, uint256[] memory)
external external
pure pure
override override
@@ -117,8 +123,8 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
function swap( function swap(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
OrderSide side, OrderSide side,
uint256 specifiedAmount uint256 specifiedAmount
) external override returns (Trade memory trade) { ) external override returns (Trade memory trade) {
@@ -136,16 +142,18 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
limit = type(uint256).max; limit = type(uint256).max;
} }
sellToken.transferFrom(msg.sender, address(this), sellAmount); IERC20(sellToken).safeTransferFrom(
sellToken.approve(address(vault), sellAmount); msg.sender, address(this), sellAmount
);
IERC20(sellToken).safeIncreaseAllowance(address(vault), sellAmount);
uint256 gasBefore = gasleft(); uint256 gasBefore = gasleft();
trade.calculatedAmount = vault.swap( trade.calculatedAmount = vault.swap(
IVault.SingleSwap({ IVault.SingleSwap({
poolId: poolId, poolId: poolId,
kind: kind, kind: kind,
assetIn: address(sellToken), assetIn: sellToken,
assetOut: address(buyToken), assetOut: buyToken,
amount: specifiedAmount, amount: specifiedAmount,
userData: "" userData: ""
}), }),
@@ -162,14 +170,14 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
trade.price = priceSingle(poolId, sellToken, buyToken, specifiedAmount); trade.price = priceSingle(poolId, sellToken, buyToken, specifiedAmount);
} }
function getLimits(bytes32 poolId, IERC20 sellToken, IERC20 buyToken) function getLimits(bytes32 poolId, address sellToken, address buyToken)
external external
view view
override override
returns (uint256[] memory limits) returns (uint256[] memory limits)
{ {
limits = new uint256[](2); limits = new uint256[](2);
(IERC20[] memory tokens, uint256[] memory balances,) = (address[] memory tokens, uint256[] memory balances,) =
vault.getPoolTokens(poolId); vault.getPoolTokens(poolId);
for (uint256 i = 0; i < tokens.length; i++) { for (uint256 i = 0; i < tokens.length; i++) {
@@ -182,7 +190,7 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
} }
} }
function getCapabilities(bytes32, IERC20, IERC20) function getCapabilities(bytes32, address, address)
external external
pure pure
override override
@@ -197,7 +205,7 @@ contract BalancerV2SwapAdapter is ISwapAdapter {
external external
view view
override override
returns (IERC20[] memory tokens) returns (address[] memory tokens)
{ {
(tokens,,) = vault.getPoolTokens(poolId); (tokens,,) = vault.getPoolTokens(poolId);
} }
@@ -472,7 +480,7 @@ interface IVault {
external external
view view
returns ( returns (
IERC20[] memory tokens, address[] memory tokens,
uint256[] memory balances, uint256[] memory balances,
uint256 lastChangeBlock uint256 lastChangeBlock
); );

View File

@@ -1,10 +1,13 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import {IERC20, ISwapAdapter} from "src/interfaces/ISwapAdapter.sol"; import {ISwapAdapter} from "src/interfaces/ISwapAdapter.sol";
import {ERC20} from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import {IERC20Metadata} from
import {SafeERC20} from "openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol";
"openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import {
IERC20,
SafeERC20
} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
/// @dev Integral submitted deadline of 3600 seconds (1 hour) to Paraswap, but /// @dev Integral submitted deadline of 3600 seconds (1 hour) to Paraswap, but
/// it is not strictly necessary to be this long /// it is not strictly necessary to be this long
@@ -38,24 +41,23 @@ contract IntegralSwapAdapter is ISwapAdapter {
/// values to make sure the return value is the expected from caller. /// values to make sure the return value is the expected from caller.
function price( function price(
bytes32, bytes32,
IERC20 _sellToken, address _sellToken,
IERC20 _buyToken, address _buyToken,
uint256[] memory _specifiedAmounts uint256[] memory _specifiedAmounts
) external view override returns (Fraction[] memory _prices) { ) external view override returns (Fraction[] memory _prices) {
_prices = new Fraction[](_specifiedAmounts.length); _prices = new Fraction[](_specifiedAmounts.length);
Fraction memory price = Fraction memory uniformPrice = getPriceAt(_sellToken, _buyToken);
getPriceAt(address(_sellToken), address(_buyToken));
for (uint256 i = 0; i < _specifiedAmounts.length; i++) { for (uint256 i = 0; i < _specifiedAmounts.length; i++) {
_prices[i] = price; _prices[i] = uniformPrice;
} }
} }
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter
function swap( function swap(
bytes32, bytes32,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
OrderSide side, OrderSide side,
uint256 specifiedAmount uint256 specifiedAmount
) external override returns (Trade memory trade) { ) external override returns (Trade memory trade) {
@@ -72,18 +74,18 @@ contract IntegralSwapAdapter is ISwapAdapter {
trade.calculatedAmount = buy(sellToken, buyToken, specifiedAmount); trade.calculatedAmount = buy(sellToken, buyToken, specifiedAmount);
} }
trade.gasUsed = gasBefore - gasleft(); trade.gasUsed = gasBefore - gasleft();
trade.price = getPriceAt(address(sellToken), address(buyToken)); trade.price = getPriceAt(sellToken, buyToken);
} }
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter
function getLimits(bytes32, IERC20 sellToken, IERC20 buyToken) function getLimits(bytes32, address sellToken, address buyToken)
external external
view view
override override
returns (uint256[] memory limits) returns (uint256[] memory limits)
{ {
(,,, uint256 limitMax0,, uint256 limitMax1) = (,,, uint256 limitMax0,, uint256 limitMax1) =
relayer.getPoolState(address(sellToken), address(buyToken)); relayer.getPoolState(sellToken, buyToken);
limits = new uint256[](2); limits = new uint256[](2);
limits[0] = limitMax0; limits[0] = limitMax0;
@@ -98,7 +100,7 @@ contract IntegralSwapAdapter is ISwapAdapter {
} }
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter
function getCapabilities(bytes32, IERC20, IERC20) function getCapabilities(bytes32, address, address)
external external
pure pure
override override
@@ -116,12 +118,12 @@ contract IntegralSwapAdapter is ISwapAdapter {
external external
view view
override override
returns (IERC20[] memory tokens) returns (address[] memory tokens)
{ {
tokens = new IERC20[](2); tokens = new address[](2);
ITwapPair pair = ITwapPair(address(bytes20(poolId))); ITwapPair pair = ITwapPair(address(bytes20(poolId)));
tokens[0] = IERC20(pair.token0()); tokens[0] = pair.token0();
tokens[1] = IERC20(pair.token1()); tokens[1] = pair.token1();
} }
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter
@@ -147,23 +149,22 @@ contract IntegralSwapAdapter is ISwapAdapter {
/// @param buyToken The address of the token being bought. /// @param buyToken The address of the token being bought.
/// @param amount The amount to be traded. /// @param amount The amount to be traded.
/// @return uint256 The amount of tokens received. /// @return uint256 The amount of tokens received.
function sell(IERC20 sellToken, IERC20 buyToken, uint256 amount) function sell(address sellToken, address buyToken, uint256 amount)
internal internal
returns (uint256) returns (uint256)
{ {
uint256 amountOut = uint256 amountOut = relayer.quoteSell(sellToken, buyToken, amount);
relayer.quoteSell(address(sellToken), address(buyToken), amount);
if (amountOut == 0) { if (amountOut == 0) {
revert Unavailable("AmountOut is zero!"); revert Unavailable("AmountOut is zero!");
} }
sellToken.safeTransferFrom(msg.sender, address(this), amount); IERC20(sellToken).safeTransferFrom(msg.sender, address(this), amount);
sellToken.safeIncreaseAllowance(address(relayer), amount); IERC20(sellToken).safeIncreaseAllowance(address(relayer), amount);
relayer.sell( relayer.sell(
ITwapRelayer.SellParams({ ITwapRelayer.SellParams({
tokenIn: address(sellToken), tokenIn: sellToken,
tokenOut: address(buyToken), tokenOut: buyToken,
wrapUnwrap: false, wrapUnwrap: false,
to: msg.sender, to: msg.sender,
submitDeadline: uint32(block.timestamp + SWAP_DEADLINE_SEC), submitDeadline: uint32(block.timestamp + SWAP_DEADLINE_SEC),
@@ -180,24 +181,22 @@ contract IntegralSwapAdapter is ISwapAdapter {
/// @param buyToken The address of the token being bought. /// @param buyToken The address of the token being bought.
/// @param amountBought The amount of buyToken tokens to buy. /// @param amountBought The amount of buyToken tokens to buy.
/// @return uint256 The amount of tokens received. /// @return uint256 The amount of tokens received.
function buy(IERC20 sellToken, IERC20 buyToken, uint256 amountBought) function buy(address sellToken, address buyToken, uint256 amountBought)
internal internal
returns (uint256) returns (uint256)
{ {
uint256 amountIn = relayer.quoteBuy( uint256 amountIn = relayer.quoteBuy(sellToken, buyToken, amountBought);
address(sellToken), address(buyToken), amountBought
);
if (amountIn == 0) { if (amountIn == 0) {
revert Unavailable("AmountIn is zero!"); revert Unavailable("AmountIn is zero!");
} }
sellToken.safeTransferFrom(msg.sender, address(this), amountIn); IERC20(sellToken).safeTransferFrom(msg.sender, address(this), amountIn);
sellToken.safeIncreaseAllowance(address(relayer), amountIn); IERC20(sellToken).safeIncreaseAllowance(address(relayer), amountIn);
relayer.buy( relayer.buy(
ITwapRelayer.BuyParams({ ITwapRelayer.BuyParams({
tokenIn: address(sellToken), tokenIn: sellToken,
tokenOut: address(buyToken), tokenOut: buyToken,
wrapUnwrap: false, wrapUnwrap: false,
to: msg.sender, to: msg.sender,
submitDeadline: uint32(block.timestamp + SWAP_DEADLINE_SEC), submitDeadline: uint32(block.timestamp + SWAP_DEADLINE_SEC),
@@ -217,20 +216,18 @@ contract IntegralSwapAdapter is ISwapAdapter {
view view
returns (Fraction memory) returns (Fraction memory)
{ {
uint256 priceWithoutFee = relayer.getPriceByTokenAddresses( uint256 priceWithoutFee =
address(sellToken), address(buyToken) relayer.getPriceByTokenAddresses(sellToken, buyToken);
);
ITwapFactory factory = ITwapFactory(relayer.factory()); ITwapFactory factory = ITwapFactory(relayer.factory());
address pairAddress = address pairAddress = factory.getPair(sellToken, buyToken);
factory.getPair(address(sellToken), address(buyToken));
// get swapFee formatted; swapFee is a constant // get swapFee formatted; swapFee is a constant
uint256 swapFeeFormatted = uint256 swapFeeFormatted =
(STANDARD_TOKEN_DECIMALS - relayer.swapFee(pairAddress)); (STANDARD_TOKEN_DECIMALS - relayer.swapFee(pairAddress));
// get token decimals // get token decimals
uint256 sellTokenDecimals = 10 ** ERC20(sellToken).decimals(); uint256 sellTokenDecimals = 10 ** IERC20Metadata(sellToken).decimals();
uint256 buyTokenDecimals = 10 ** ERC20(buyToken).decimals(); uint256 buyTokenDecimals = 10 ** IERC20Metadata(buyToken).decimals();
/** /**
* @dev * @dev

View File

@@ -1,7 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import {IERC20} from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import {ISwapAdapterTypes} from "src/interfaces/ISwapAdapterTypes.sol"; import {ISwapAdapterTypes} from "src/interfaces/ISwapAdapterTypes.sol";
/// @title ISwapAdapter /// @title ISwapAdapter
@@ -35,8 +34,8 @@ interface ISwapAdapter is ISwapAdapterTypes {
/// provided amounts. /// provided amounts.
function price( function price(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
uint256[] memory specifiedAmounts uint256[] memory specifiedAmounts
) external view returns (Fraction[] memory prices); ) external view returns (Fraction[] memory prices);
@@ -59,8 +58,8 @@ interface ISwapAdapter is ISwapAdapterTypes {
*/ */
function swap( function swap(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
OrderSide side, OrderSide side,
uint256 specifiedAmount uint256 specifiedAmount
) external returns (Trade memory trade); ) external returns (Trade memory trade);
@@ -76,25 +75,27 @@ interface ISwapAdapter is ISwapAdapterTypes {
/// @param sellToken The token being sold. /// @param sellToken The token being sold.
/// @param buyToken The token being bought. /// @param buyToken The token being bought.
/// @return limits An array of limits. /// @return limits An array of limits.
function getLimits(bytes32 poolId, IERC20 sellToken, IERC20 buyToken) function getLimits(bytes32 poolId, address sellToken, address buyToken)
external external
returns (uint256[] memory limits); returns (uint256[] memory limits);
/// @notice Retrieves the capabilities of the selected pool. /// @notice Retrieves the capabilities of the selected pool.
/// @param poolId The ID of the trading pool. /// @param poolId The ID of the trading pool.
/// @return capabilities An array of Capability. /// @return capabilities An array of Capability.
function getCapabilities(bytes32 poolId, IERC20 sellToken, IERC20 buyToken) function getCapabilities(
external bytes32 poolId,
returns (Capability[] memory capabilities); address sellToken,
address buyToken
) external returns (Capability[] memory capabilities);
/// @notice Retrieves the tokens in the selected pool. /// @notice Retrieves the tokens in the selected pool.
/// @dev Mainly used for testing as this is redundant with the required /// @dev Mainly used for testing as this is redundant with the required
/// substreams implementation. /// substreams implementation.
/// @param poolId The ID of the trading pool. /// @param poolId The ID of the trading pool.
/// @return tokens An array of IERC20 contracts. /// @return tokens An array of address contracts.
function getTokens(bytes32 poolId) function getTokens(bytes32 poolId)
external external
returns (IERC20[] memory tokens); returns (address[] memory tokens);
/// @notice Retrieves a range of pool IDs. /// @notice Retrieves a range of pool IDs.
/// @dev Mainly used for testing. It is alright to not return all available /// @dev Mainly used for testing. It is alright to not return all available

View File

@@ -1,8 +1,6 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import {IERC20} from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
interface ISwapAdapterTypes { interface ISwapAdapterTypes {
/// @dev The OrderSide enum represents possible sides of a trade: Sell or /// @dev The OrderSide enum represents possible sides of a trade: Sell or
/// Buy. E.g. if OrderSide is Sell, the sell amount is interpreted to be /// Buy. E.g. if OrderSide is Sell, the sell amount is interpreted to be

View File

@@ -1,7 +1,7 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import {IERC20, ISwapAdapter} from "src/interfaces/ISwapAdapter.sol"; import {ISwapAdapter} from "src/interfaces/ISwapAdapter.sol";
/// @title TemplateSwapAdapter /// @title TemplateSwapAdapter
/// @dev This is a template for a swap adapter. /// @dev This is a template for a swap adapter.
@@ -10,8 +10,8 @@ import {IERC20, ISwapAdapter} from "src/interfaces/ISwapAdapter.sol";
contract TemplateSwapAdapter is ISwapAdapter { contract TemplateSwapAdapter is ISwapAdapter {
function price( function price(
bytes32 _poolId, bytes32 _poolId,
IERC20 _sellToken, address _sellToken,
IERC20 _buyToken, address _buyToken,
uint256[] memory _specifiedAmounts uint256[] memory _specifiedAmounts
) external view override returns (Fraction[] memory _prices) { ) external view override returns (Fraction[] memory _prices) {
revert NotImplemented("TemplateSwapAdapter.price"); revert NotImplemented("TemplateSwapAdapter.price");
@@ -19,31 +19,32 @@ contract TemplateSwapAdapter is ISwapAdapter {
function swap( function swap(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
OrderSide side, OrderSide side,
uint256 specifiedAmount uint256 specifiedAmount
) external returns (Trade memory trade) { ) external returns (Trade memory trade) {
revert NotImplemented("TemplateSwapAdapter.swap"); revert NotImplemented("TemplateSwapAdapter.swap");
} }
function getLimits(bytes32 poolId, IERC20 sellToken, IERC20 buyToken) function getLimits(bytes32 poolId, address sellToken, address buyToken)
external external
returns (uint256[] memory limits) returns (uint256[] memory limits)
{ {
revert NotImplemented("TemplateSwapAdapter.getLimits"); revert NotImplemented("TemplateSwapAdapter.getLimits");
} }
function getCapabilities(bytes32 poolId, IERC20 sellToken, IERC20 buyToken) function getCapabilities(
external bytes32 poolId,
returns (Capability[] memory capabilities) address sellToken,
{ address buyToken
) external returns (Capability[] memory capabilities) {
revert NotImplemented("TemplateSwapAdapter.getCapabilities"); revert NotImplemented("TemplateSwapAdapter.getCapabilities");
} }
function getTokens(bytes32 poolId) function getTokens(bytes32 poolId)
external external
returns (IERC20[] memory tokens) returns (address[] memory tokens)
{ {
revert NotImplemented("TemplateSwapAdapter.getTokens"); revert NotImplemented("TemplateSwapAdapter.getTokens");
} }

View File

@@ -1,12 +1,18 @@
// SPDX-License-Identifier: AGPL-3.0-or-later // SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.13; pragma solidity ^0.8.13;
import {IERC20, ISwapAdapter} from "src/interfaces/ISwapAdapter.sol"; import {ISwapAdapter} from "src/interfaces/ISwapAdapter.sol";
import {
IERC20,
SafeERC20
} from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
// Uniswap handles arbirary amounts, but we limit the amount to 10x just in case // Uniswap handles arbirary amounts, but we limit the amount to 10x just in case
uint256 constant RESERVE_LIMIT_FACTOR = 10; uint256 constant RESERVE_LIMIT_FACTOR = 10;
contract UniswapV2SwapAdapter is ISwapAdapter { contract UniswapV2SwapAdapter is ISwapAdapter {
using SafeERC20 for IERC20;
IUniswapV2Factory immutable factory; IUniswapV2Factory immutable factory;
constructor(address factory_) { constructor(address factory_) {
@@ -16,8 +22,8 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter
function price( function price(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
uint256[] memory specifiedAmounts uint256[] memory specifiedAmounts
) external view override returns (Fraction[] memory prices) { ) external view override returns (Fraction[] memory prices) {
prices = new Fraction[](specifiedAmounts.length); prices = new Fraction[](specifiedAmounts.length);
@@ -60,8 +66,8 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter
function swap( function swap(
bytes32 poolId, bytes32 poolId,
IERC20 sellToken, address sellToken,
IERC20 buyToken, address buyToken,
OrderSide side, OrderSide side,
uint256 specifiedAmount uint256 specifiedAmount
) external override returns (Trade memory trade) { ) external override returns (Trade memory trade) {
@@ -104,7 +110,7 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
/// @return calculatedAmount The amount of tokens received. /// @return calculatedAmount The amount of tokens received.
function sell( function sell(
IUniswapV2Pair pair, IUniswapV2Pair pair,
IERC20 sellToken, address sellToken,
bool zero2one, bool zero2one,
uint112 reserveIn, uint112 reserveIn,
uint112 reserveOut, uint112 reserveOut,
@@ -113,8 +119,7 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
address swapper = msg.sender; address swapper = msg.sender;
uint256 amountOut = getAmountOut(amount, reserveIn, reserveOut); uint256 amountOut = getAmountOut(amount, reserveIn, reserveOut);
// TODO: use safeTransferFrom IERC20(sellToken).safeTransferFrom(swapper, address(pair), amount);
sellToken.transferFrom(swapper, address(pair), amount);
if (zero2one) { if (zero2one) {
pair.swap(0, amountOut, swapper, ""); pair.swap(0, amountOut, swapper, "");
} else { } else {
@@ -156,7 +161,7 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
/// @return calculatedAmount The amount of tokens sold. /// @return calculatedAmount The amount of tokens sold.
function buy( function buy(
IUniswapV2Pair pair, IUniswapV2Pair pair,
IERC20 sellToken, address sellToken,
bool zero2one, bool zero2one,
uint112 reserveIn, uint112 reserveIn,
uint112 reserveOut, uint112 reserveOut,
@@ -168,8 +173,8 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
if (amount == 0) { if (amount == 0) {
return 0; return 0;
} }
// TODO: use safeTransferFrom
sellToken.transferFrom(swapper, address(pair), amount); IERC20(sellToken).safeTransferFrom(swapper, address(pair), amount);
if (zero2one) { if (zero2one) {
pair.swap(0, amountOut, swapper, ""); pair.swap(0, amountOut, swapper, "");
} else { } else {
@@ -203,7 +208,7 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
} }
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter
function getLimits(bytes32 poolId, IERC20 sellToken, IERC20 buyToken) function getLimits(bytes32 poolId, address sellToken, address buyToken)
external external
view view
override override
@@ -222,7 +227,7 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
} }
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter
function getCapabilities(bytes32, IERC20, IERC20) function getCapabilities(bytes32, address, address)
external external
pure pure
override override
@@ -239,12 +244,12 @@ contract UniswapV2SwapAdapter is ISwapAdapter {
external external
view view
override override
returns (IERC20[] memory tokens) returns (address[] memory tokens)
{ {
tokens = new IERC20[](2); tokens = new address[](2);
IUniswapV2Pair pair = IUniswapV2Pair(address(bytes20(poolId))); IUniswapV2Pair pair = IUniswapV2Pair(address(bytes20(poolId)));
tokens[0] = IERC20(pair.token0()); tokens[0] = address(pair.token0());
tokens[1] = IERC20(pair.token1()); tokens[1] = address(pair.token1());
} }
/// @inheritdoc ISwapAdapter /// @inheritdoc ISwapAdapter

View File

@@ -17,8 +17,8 @@ contract BalancerV2SwapAdapterTest is Test, ISwapAdapterTypes {
IVault(payable(0xBA12222222228d8Ba445958a75a0704d566BF2C8)); IVault(payable(0xBA12222222228d8Ba445958a75a0704d566BF2C8));
BalancerV2SwapAdapter adapter; BalancerV2SwapAdapter adapter;
IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IERC20 constant BAL = IERC20(0xba100000625a3754423978a60c9317c58a424e3D); address constant BAL = 0xba100000625a3754423978a60c9317c58a424e3D;
address constant B_80BAL_20WETH = 0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56; address constant B_80BAL_20WETH = 0x5c6Ee304399DBdB9C8Ef030aB642B10820DB8F56;
bytes32 constant B_80BAL_20WETH_POOL_ID = bytes32 constant B_80BAL_20WETH_POOL_ID =
0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014; 0x5c6ee304399dbdb9c8ef030ab642b10820db8f56000200000000000000000014;
@@ -34,7 +34,7 @@ contract BalancerV2SwapAdapterTest is Test, ISwapAdapterTypes {
vm.label(address(balancerV2Vault), "IVault"); vm.label(address(balancerV2Vault), "IVault");
vm.label(address(adapter), "BalancerV2SwapAdapter"); vm.label(address(adapter), "BalancerV2SwapAdapter");
vm.label(address(WETH), "WETH"); vm.label(address(WETH), "WETH");
vm.label(address(BAL), "BAL"); vm.label(BAL, "BAL");
vm.label(address(B_80BAL_20WETH), "B_80BAL_20WETH"); vm.label(address(B_80BAL_20WETH), "B_80BAL_20WETH");
} }
@@ -97,17 +97,17 @@ contract BalancerV2SwapAdapterTest is Test, ISwapAdapterTypes {
// TODO calculate the amountIn by using price function as in // TODO calculate the amountIn by using price function as in
// testPriceDecreasing // testPriceDecreasing
deal(address(BAL), address(this), type(uint256).max); deal(BAL, address(this), type(uint256).max);
BAL.approve(address(adapter), type(uint256).max); IERC20(BAL).approve(address(adapter), type(uint256).max);
} else { } else {
vm.assume(specifiedAmount < limits[0]); vm.assume(specifiedAmount < limits[0]);
deal(address(BAL), address(this), specifiedAmount); deal(BAL, address(this), specifiedAmount);
BAL.approve(address(adapter), specifiedAmount); IERC20(BAL).approve(address(adapter), specifiedAmount);
} }
uint256 bal_balance = BAL.balanceOf(address(this)); uint256 bal_balance = IERC20(BAL).balanceOf(address(this));
uint256 weth_balance = WETH.balanceOf(address(this)); uint256 weth_balance = IERC20(WETH).balanceOf(address(this));
Trade memory trade = adapter.swap( Trade memory trade = adapter.swap(
B_80BAL_20WETH_POOL_ID, BAL, WETH, side, specifiedAmount B_80BAL_20WETH_POOL_ID, BAL, WETH, side, specifiedAmount
@@ -117,19 +117,20 @@ contract BalancerV2SwapAdapterTest is Test, ISwapAdapterTypes {
if (side == OrderSide.Buy) { if (side == OrderSide.Buy) {
assertEq( assertEq(
specifiedAmount, specifiedAmount,
WETH.balanceOf(address(this)) - weth_balance IERC20(WETH).balanceOf(address(this)) - weth_balance
); );
assertEq( assertEq(
trade.calculatedAmount, trade.calculatedAmount,
bal_balance - BAL.balanceOf(address(this)) bal_balance - IERC20(BAL).balanceOf(address(this))
); );
} else { } else {
assertEq( assertEq(
specifiedAmount, bal_balance - BAL.balanceOf(address(this)) specifiedAmount,
bal_balance - IERC20(BAL).balanceOf(address(this))
); );
assertEq( assertEq(
trade.calculatedAmount, trade.calculatedAmount,
WETH.balanceOf(address(this)) - weth_balance IERC20(WETH).balanceOf(address(this)) - weth_balance
); );
} }
} }
@@ -144,8 +145,8 @@ contract BalancerV2SwapAdapterTest is Test, ISwapAdapterTypes {
uint256 beforeSwap = vm.snapshot(); uint256 beforeSwap = vm.snapshot();
deal(address(BAL), address(this), amounts[i]); deal(BAL, address(this), amounts[i]);
BAL.approve(address(adapter), amounts[i]); IERC20(BAL).approve(address(adapter), amounts[i]);
trades[i] = adapter.swap( trades[i] = adapter.swap(
B_80BAL_20WETH_POOL_ID, BAL, WETH, OrderSide.Sell, amounts[i] B_80BAL_20WETH_POOL_ID, BAL, WETH, OrderSide.Sell, amounts[i]
); );
@@ -175,8 +176,8 @@ contract BalancerV2SwapAdapterTest is Test, ISwapAdapterTypes {
uint256 amountIn = uint256 amountIn =
(amounts[i] * price.denominator / price.numerator) * 2; (amounts[i] * price.denominator / price.numerator) * 2;
deal(address(BAL), address(this), amountIn); deal(BAL, address(this), amountIn);
BAL.approve(address(adapter), amountIn); IERC20(BAL).approve(address(adapter), amountIn);
trades[i] = adapter.swap( trades[i] = adapter.swap(
B_80BAL_20WETH_POOL_ID, BAL, WETH, OrderSide.Buy, amounts[i] B_80BAL_20WETH_POOL_ID, BAL, WETH, OrderSide.Buy, amounts[i]
); );
@@ -203,8 +204,7 @@ contract BalancerV2SwapAdapterTest is Test, ISwapAdapterTypes {
function testGetCapabilitiesFuzz(bytes32 pool, address t0, address t1) function testGetCapabilitiesFuzz(bytes32 pool, address t0, address t1)
public public
{ {
Capability[] memory res = Capability[] memory res = adapter.getCapabilities(pool, t0, t1);
adapter.getCapabilities(pool, IERC20(t0), IERC20(t1));
assertEq(res.length, 2); assertEq(res.length, 2);
assertEq(uint256(res[0]), uint256(Capability.SellOrder)); assertEq(uint256(res[0]), uint256(Capability.SellOrder));
@@ -212,10 +212,10 @@ contract BalancerV2SwapAdapterTest is Test, ISwapAdapterTypes {
} }
function testGetTokens() public { function testGetTokens() public {
IERC20[] memory tokens = adapter.getTokens(B_80BAL_20WETH_POOL_ID); address[] memory tokens = adapter.getTokens(B_80BAL_20WETH_POOL_ID);
assertEq(address(tokens[0]), address(BAL)); assertEq(tokens[0], BAL);
assertEq(address(tokens[1]), address(WETH)); assertEq(tokens[1], address(WETH));
} }
function testGetPoolIds() public { function testGetPoolIds() public {

View File

@@ -12,8 +12,8 @@ contract IntegralSwapAdapterTest is Test, ISwapAdapterTypes {
IntegralSwapAdapter adapter; IntegralSwapAdapter adapter;
ITwapRelayer relayer; ITwapRelayer relayer;
IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IERC20 constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address constant USDC_WETH_PAIR = 0x2fe16Dd18bba26e457B7dD2080d5674312b026a2; address constant USDC_WETH_PAIR = 0x2fe16Dd18bba26e457B7dD2080d5674312b026a2;
address constant relayerAddress = 0xd17b3c9784510E33cD5B87b490E79253BcD81e2E; address constant relayerAddress = 0xd17b3c9784510E33cD5B87b490E79253BcD81e2E;
@@ -26,7 +26,7 @@ contract IntegralSwapAdapterTest is Test, ISwapAdapterTypes {
relayer = ITwapRelayer(relayerAddress); relayer = ITwapRelayer(relayerAddress);
vm.label(address(WETH), "WETH"); vm.label(address(WETH), "WETH");
vm.label(address(USDC), "USDC"); vm.label(USDC, "USDC");
vm.label(address(USDC_WETH_PAIR), "USDC_WETH_PAIR"); vm.label(address(USDC_WETH_PAIR), "USDC_WETH_PAIR");
} }
@@ -66,8 +66,8 @@ contract IntegralSwapAdapterTest is Test, ISwapAdapterTypes {
limitsMin = getMinLimits(USDC, WETH); limitsMin = getMinLimits(USDC, WETH);
vm.assume(specifiedAmount > limitsMin[1] * 115 / 100); vm.assume(specifiedAmount > limitsMin[1] * 115 / 100);
deal(address(USDC), address(this), type(uint256).max); deal(USDC, address(this), type(uint256).max);
USDC.approve(address(adapter), type(uint256).max); IERC20(USDC).approve(address(adapter), type(uint256).max);
} else { } else {
limits = adapter.getLimits(pair, USDC, WETH); limits = adapter.getLimits(pair, USDC, WETH);
vm.assume(specifiedAmount < limits[0]); vm.assume(specifiedAmount < limits[0]);
@@ -75,12 +75,12 @@ contract IntegralSwapAdapterTest is Test, ISwapAdapterTypes {
limitsMin = getMinLimits(USDC, WETH); limitsMin = getMinLimits(USDC, WETH);
vm.assume(specifiedAmount > limitsMin[0] * 115 / 100); vm.assume(specifiedAmount > limitsMin[0] * 115 / 100);
deal(address(USDC), address(this), type(uint256).max); deal(USDC, address(this), type(uint256).max);
USDC.approve(address(adapter), specifiedAmount); IERC20(USDC).approve(address(adapter), specifiedAmount);
} }
uint256 usdc_balance_before = USDC.balanceOf(address(this)); uint256 usdc_balance_before = IERC20(USDC).balanceOf(address(this));
uint256 weth_balance_before = WETH.balanceOf(address(this)); uint256 weth_balance_before = IERC20(WETH).balanceOf(address(this));
Trade memory trade = Trade memory trade =
adapter.swap(pair, USDC, WETH, side, specifiedAmount); adapter.swap(pair, USDC, WETH, side, specifiedAmount);
@@ -89,22 +89,22 @@ contract IntegralSwapAdapterTest is Test, ISwapAdapterTypes {
if (side == OrderSide.Buy) { if (side == OrderSide.Buy) {
assertEq( assertEq(
specifiedAmount, specifiedAmount,
WETH.balanceOf(address(this)) - weth_balance_before IERC20(WETH).balanceOf(address(this)) - weth_balance_before
); );
assertEq( assertEq(
trade.calculatedAmount, trade.calculatedAmount,
usdc_balance_before - USDC.balanceOf(address(this)) usdc_balance_before - IERC20(USDC).balanceOf(address(this))
); );
} else { } else {
assertEq( assertEq(
specifiedAmount, specifiedAmount,
usdc_balance_before - USDC.balanceOf(address(this)) usdc_balance_before - IERC20(USDC).balanceOf(address(this))
); );
assertEq( assertEq(
trade.calculatedAmount, trade.calculatedAmount,
WETH.balanceOf(address(this)) - weth_balance_before IERC20(WETH).balanceOf(address(this)) - weth_balance_before
); );
} }
} }
@@ -135,8 +135,8 @@ contract IntegralSwapAdapterTest is Test, ISwapAdapterTypes {
for (uint256 i = 1; i < TEST_ITERATIONS; i++) { for (uint256 i = 1; i < TEST_ITERATIONS; i++) {
beforeSwap = vm.snapshot(); beforeSwap = vm.snapshot();
deal(address(USDC), address(this), amounts[i]); deal(USDC, address(this), amounts[i]);
USDC.approve(address(adapter), amounts[i]); IERC20(USDC).approve(address(adapter), amounts[i]);
trades[i] = adapter.swap(pair, USDC, WETH, side, amounts[i]); trades[i] = adapter.swap(pair, USDC, WETH, side, amounts[i]);
vm.revertTo(beforeSwap); vm.revertTo(beforeSwap);
@@ -152,15 +152,14 @@ contract IntegralSwapAdapterTest is Test, ISwapAdapterTypes {
function testGetCapabilitiesIntegral(bytes32 pair, address t0, address t1) function testGetCapabilitiesIntegral(bytes32 pair, address t0, address t1)
public public
{ {
Capability[] memory res = Capability[] memory res = adapter.getCapabilities(pair, t0, t1);
adapter.getCapabilities(pair, IERC20(t0), IERC20(t1));
assertEq(res.length, 4); assertEq(res.length, 4);
} }
function testGetTokensIntegral() public { function testGetTokensIntegral() public {
bytes32 pair = bytes32(bytes20(USDC_WETH_PAIR)); bytes32 pair = bytes32(bytes20(USDC_WETH_PAIR));
IERC20[] memory tokens = adapter.getTokens(pair); address[] memory tokens = adapter.getTokens(pair);
assertEq(tokens.length, 2); assertEq(tokens.length, 2);
} }
@@ -172,7 +171,7 @@ contract IntegralSwapAdapterTest is Test, ISwapAdapterTypes {
assertEq(limits.length, 2); assertEq(limits.length, 2);
} }
function getMinLimits(IERC20 sellToken, IERC20 buyToken) function getMinLimits(address sellToken, address buyToken)
public public
view view
returns (uint256[] memory limits) returns (uint256[] memory limits)

View File

@@ -11,8 +11,8 @@ contract UniswapV2PairFunctionTest is Test, ISwapAdapterTypes {
using FractionMath for Fraction; using FractionMath for Fraction;
UniswapV2SwapAdapter adapter; UniswapV2SwapAdapter adapter;
IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); address constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
IERC20 constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); address constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address constant USDC_WETH_PAIR = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc; address constant USDC_WETH_PAIR = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc;
uint256 constant TEST_ITERATIONS = 100; uint256 constant TEST_ITERATIONS = 100;
@@ -24,9 +24,9 @@ contract UniswapV2PairFunctionTest is Test, ISwapAdapterTypes {
new UniswapV2SwapAdapter(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); new UniswapV2SwapAdapter(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
vm.label(address(adapter), "UniswapV2SwapAdapter"); vm.label(address(adapter), "UniswapV2SwapAdapter");
vm.label(address(WETH), "WETH"); vm.label(WETH, "WETH");
vm.label(address(USDC), "USDC"); vm.label(USDC, "USDC");
vm.label(address(USDC_WETH_PAIR), "USDC_WETH_PAIR"); vm.label(USDC_WETH_PAIR, "USDC_WETH_PAIR");
} }
function testPriceFuzz(uint256 amount0, uint256 amount1) public { function testPriceFuzz(uint256 amount0, uint256 amount1) public {
@@ -75,17 +75,17 @@ contract UniswapV2PairFunctionTest is Test, ISwapAdapterTypes {
// TODO calculate the amountIn by using price function as in // TODO calculate the amountIn by using price function as in
// BalancerV2 testPriceDecreasing // BalancerV2 testPriceDecreasing
deal(address(USDC), address(this), type(uint256).max); deal(USDC, address(this), type(uint256).max);
USDC.approve(address(adapter), type(uint256).max); IERC20(USDC).approve(address(adapter), type(uint256).max);
} else { } else {
vm.assume(specifiedAmount < limits[0]); vm.assume(specifiedAmount < limits[0]);
deal(address(USDC), address(this), specifiedAmount); deal(USDC, address(this), specifiedAmount);
USDC.approve(address(adapter), specifiedAmount); IERC20(USDC).approve(address(adapter), specifiedAmount);
} }
uint256 usdc_balance = USDC.balanceOf(address(this)); uint256 usdc_balance = IERC20(USDC).balanceOf(address(this));
uint256 weth_balance = WETH.balanceOf(address(this)); uint256 weth_balance = IERC20(WETH).balanceOf(address(this));
Trade memory trade = Trade memory trade =
adapter.swap(pair, USDC, WETH, side, specifiedAmount); adapter.swap(pair, USDC, WETH, side, specifiedAmount);
@@ -94,20 +94,20 @@ contract UniswapV2PairFunctionTest is Test, ISwapAdapterTypes {
if (side == OrderSide.Buy) { if (side == OrderSide.Buy) {
assertEq( assertEq(
specifiedAmount, specifiedAmount,
WETH.balanceOf(address(this)) - weth_balance IERC20(WETH).balanceOf(address(this)) - weth_balance
); );
assertEq( assertEq(
trade.calculatedAmount, trade.calculatedAmount,
usdc_balance - USDC.balanceOf(address(this)) usdc_balance - IERC20(USDC).balanceOf(address(this))
); );
} else { } else {
assertEq( assertEq(
specifiedAmount, specifiedAmount,
usdc_balance - USDC.balanceOf(address(this)) usdc_balance - IERC20(USDC).balanceOf(address(this))
); );
assertEq( assertEq(
trade.calculatedAmount, trade.calculatedAmount,
WETH.balanceOf(address(this)) - weth_balance IERC20(WETH).balanceOf(address(this)) - weth_balance
); );
} }
} }
@@ -130,8 +130,8 @@ contract UniswapV2PairFunctionTest is Test, ISwapAdapterTypes {
for (uint256 i = 0; i < TEST_ITERATIONS; i++) { for (uint256 i = 0; i < TEST_ITERATIONS; i++) {
beforeSwap = vm.snapshot(); beforeSwap = vm.snapshot();
deal(address(USDC), address(this), amounts[i]); deal(USDC, address(this), amounts[i]);
USDC.approve(address(adapter), amounts[i]); IERC20(USDC).approve(address(adapter), amounts[i]);
trades[i] = adapter.swap(pair, USDC, WETH, side, amounts[i]); trades[i] = adapter.swap(pair, USDC, WETH, side, amounts[i]);
vm.revertTo(beforeSwap); vm.revertTo(beforeSwap);
@@ -149,8 +149,7 @@ contract UniswapV2PairFunctionTest is Test, ISwapAdapterTypes {
} }
function testGetCapabilities(bytes32 pair, address t0, address t1) public { function testGetCapabilities(bytes32 pair, address t0, address t1) public {
Capability[] memory res = Capability[] memory res = adapter.getCapabilities(pair, t0, t1);
adapter.getCapabilities(pair, IERC20(t0), IERC20(t1));
assertEq(res.length, 3); assertEq(res.length, 3);
} }