Merge branch 'ah/finish-interfaces'
This commit is contained in:
@@ -2,9 +2,9 @@
|
|||||||
pragma solidity ^0.8.13;
|
pragma solidity ^0.8.13;
|
||||||
|
|
||||||
import "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
|
import "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
|
||||||
import "interfaces/IPairFunctionsTypes.sol";
|
import "interfaces/ISwapAdapterTypes.sol";
|
||||||
|
|
||||||
/// @title IPairFunctions
|
/// @title ISwapAdapterTypes
|
||||||
/// @dev Implement this interface to support propeller routing through your pairs.
|
/// @dev Implement this interface to support propeller routing through your pairs.
|
||||||
/// @dev Before implementing the interface we need to introduce three function for a
|
/// @dev Before implementing the interface we need to introduce three function for a
|
||||||
/// @dev given pair: The swap(x), gas(x) and price(x) functions:
|
/// @dev given pair: The swap(x), gas(x) and price(x) functions:
|
||||||
@@ -15,10 +15,10 @@ import "interfaces/IPairFunctionsTypes.sol";
|
|||||||
/// @dev Last but not least, the price function is the derivative of the swap
|
/// @dev Last but not least, the price function is the derivative of the swap
|
||||||
/// @dev function. It represents the best possible price a user can get from a
|
/// @dev function. It represents the best possible price a user can get from a
|
||||||
/// @dev pair after swapping x of the specified token.
|
/// @dev pair after swapping x of the specified token.
|
||||||
/// @dev During calls to price, swap and getLimits, the caller can be assumed to
|
/// @dev During calls to swap and getLimits, the caller can be assumed to
|
||||||
/// @dev have the required sell or buy token balance as well as unlimited approvals
|
/// @dev have the required sell or buy token balance as well as unlimited approvals
|
||||||
/// @dev to this contract.
|
/// @dev to this contract.
|
||||||
interface IPairFunctions is IPairFunctionTypes {
|
interface ISwapAdapter is ISwapAdapterTypes {
|
||||||
/// @notice Calculates pair prices for specified amounts (optional).
|
/// @notice Calculates pair prices for specified amounts (optional).
|
||||||
/// @dev The returned prices should include all dex fees, in case the fee
|
/// @dev The returned prices should include all dex fees, in case the fee
|
||||||
/// @dev is dynamic, the returned price is expected to include the minimum fee.
|
/// @dev is dynamic, the returned price is expected to include the minimum fee.
|
||||||
@@ -46,7 +46,11 @@ interface IPairFunctions is IPairFunctionTypes {
|
|||||||
/// @dev the swap and change the state of the evm accordingly.
|
/// @dev the swap and change the state of the evm accordingly.
|
||||||
/// @dev Please include a gas usage estimate for each amount. This can be achieved
|
/// @dev Please include a gas usage estimate for each amount. This can be achieved
|
||||||
/// @dev e.g. by using the `gasleft()` function.
|
/// @dev e.g. by using the `gasleft()` function.
|
||||||
/// @dev
|
/// @dev The return type trade, has a price attribute which should contain the
|
||||||
|
/// value of `price(specifiedAmount)`. As this is optional, defined via
|
||||||
|
/// `Capability.PriceFunction`, it is valid to return a zero value for this
|
||||||
|
/// price in that case it will be estimated numerically. To return zero use
|
||||||
|
/// Fraction(0, 1).
|
||||||
/// @param pairId The ID of the trading pair.
|
/// @param pairId The ID of the trading pair.
|
||||||
/// @param sellToken The token being sold.
|
/// @param sellToken The token being sold.
|
||||||
/// @param buyToken The token being bought.
|
/// @param buyToken The token being bought.
|
||||||
@@ -79,10 +83,6 @@ interface IPairFunctions is IPairFunctionTypes {
|
|||||||
external
|
external
|
||||||
returns (Capabilities[] memory);
|
returns (Capabilities[] memory);
|
||||||
|
|
||||||
/// @notice Minimum gas usage of exchange logic excluding transfers.
|
|
||||||
/// @return gasUsage the amount of gas used by the exchange logic
|
|
||||||
function minGasUsage() external view returns (uint256);
|
|
||||||
|
|
||||||
/// @notice Retrieves the tokens in the selected pair.
|
/// @notice Retrieves the tokens in the selected pair.
|
||||||
/// @dev Mainly used for testing as this is redundant with the required substreams
|
/// @dev Mainly used for testing as this is redundant with the required substreams
|
||||||
/// @dev implementation.
|
/// @dev implementation.
|
||||||
@@ -3,7 +3,7 @@ pragma solidity ^0.8.13;
|
|||||||
|
|
||||||
import "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
|
import "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
|
||||||
|
|
||||||
interface IPairFunctionTypes {
|
interface ISwapAdapterTypes {
|
||||||
/// @dev The SwapSide enum represents possible sides of a trade: Sell or Buy.
|
/// @dev The SwapSide enum represents possible sides of a trade: Sell or Buy.
|
||||||
/// @dev E.g. if SwapSide is Sell, the sell amount is interpreted to be fixed.
|
/// @dev E.g. if SwapSide is Sell, the sell amount is interpreted to be fixed.
|
||||||
enum SwapSide {
|
enum SwapSide {
|
||||||
@@ -12,9 +12,9 @@ interface IPairFunctionTypes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// @dev The Capabilities enum represents possible features of a trading pair.
|
/// @dev The Capabilities enum represents possible features of a trading pair.
|
||||||
enum Capabilities
|
enum Capabilities {
|
||||||
// Support SwapSide.Sell values (required)
|
Unset,
|
||||||
{
|
// Support SwapSide.Sell values (required)
|
||||||
SellSide,
|
SellSide,
|
||||||
// Support SwapSide.Buy values (optional)
|
// Support SwapSide.Buy values (optional)
|
||||||
BuySide,
|
BuySide,
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
// 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 "interfaces/IPairFunctions.sol";
|
import "interfaces/ISwapAdapter.sol";
|
||||||
|
|
||||||
contract UniswapV2PairFunctions is IPairFunctions {
|
contract UniswapV2SwapAdapter is ISwapAdapter {
|
||||||
IUniswapV2Factory immutable factory;
|
IUniswapV2Factory immutable factory;
|
||||||
|
|
||||||
constructor(address factory_) {
|
constructor(address factory_) {
|
||||||
@@ -36,9 +36,6 @@ contract UniswapV2PairFunctions is IPairFunctions {
|
|||||||
pure
|
pure
|
||||||
returns (Fraction memory)
|
returns (Fraction memory)
|
||||||
{
|
{
|
||||||
if (amountIn == 0) {
|
|
||||||
return Fraction(0, 0);
|
|
||||||
}
|
|
||||||
if (reserveIn == 0 || reserveOut == 0) {
|
if (reserveIn == 0 || reserveOut == 0) {
|
||||||
revert Unavailable("At least one reserve is zero!");
|
revert Unavailable("At least one reserve is zero!");
|
||||||
}
|
}
|
||||||
@@ -186,7 +183,7 @@ contract UniswapV2PairFunctions is IPairFunctions {
|
|||||||
override
|
override
|
||||||
returns (Capabilities[] memory capabilities)
|
returns (Capabilities[] memory capabilities)
|
||||||
{
|
{
|
||||||
capabilities = new Capabilities[](10);
|
capabilities = new Capabilities[](3);
|
||||||
capabilities[0] = Capabilities.SellSide;
|
capabilities[0] = Capabilities.SellSide;
|
||||||
capabilities[1] = Capabilities.BuySide;
|
capabilities[1] = Capabilities.BuySide;
|
||||||
capabilities[2] = Capabilities.PriceFunction;
|
capabilities[2] = Capabilities.PriceFunction;
|
||||||
@@ -219,10 +216,6 @@ contract UniswapV2PairFunctions is IPairFunctions {
|
|||||||
ids[i] = bytes20(factory.allPairs(offset + i));
|
ids[i] = bytes20(factory.allPairs(offset + i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function minGasUsage() external view returns (uint256) {
|
|
||||||
return 30000;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUniswapV2Pair {
|
interface IUniswapV2Pair {
|
||||||
36
evm/src/uniswap-v2/manifest.yaml
Normal file
36
evm/src/uniswap-v2/manifest.yaml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# information about the author helps us reach out in case of issues.
|
||||||
|
author:
|
||||||
|
name: Propellerheads.xyz
|
||||||
|
email: alan@propellerheads.xyz
|
||||||
|
|
||||||
|
# Protocol Constants
|
||||||
|
constants:
|
||||||
|
protocol_gas: 30000
|
||||||
|
# minimum capabilities we can expect, individual pools may extend these
|
||||||
|
capabilities:
|
||||||
|
- SellSide
|
||||||
|
- BuySide
|
||||||
|
- PriceFunction
|
||||||
|
|
||||||
|
# The file containing the adapter contract
|
||||||
|
contract: UniswapV2SwapAdapter.sol
|
||||||
|
|
||||||
|
# Deployment instances used to generate chain specific bytecode.
|
||||||
|
instances:
|
||||||
|
- chain:
|
||||||
|
name: mainnet
|
||||||
|
id: 0
|
||||||
|
arguments:
|
||||||
|
- "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"
|
||||||
|
|
||||||
|
# Specify some automatic test cases in case getPools and
|
||||||
|
# getTokens are not implemented.
|
||||||
|
tests:
|
||||||
|
instances:
|
||||||
|
- pair_id: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"
|
||||||
|
sell_token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
|
||||||
|
buy_token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
|
||||||
|
block: 17000000
|
||||||
|
chain:
|
||||||
|
id: 0
|
||||||
|
name: mainnet
|
||||||
@@ -3,11 +3,11 @@ pragma solidity ^0.8.13;
|
|||||||
|
|
||||||
import "forge-std/Test.sol";
|
import "forge-std/Test.sol";
|
||||||
import "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
|
import "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
|
||||||
import "src/uniswap-v2/UniswapV2PairFunctions.sol";
|
import "src/uniswap-v2/UniswapV2SwapAdapter.sol";
|
||||||
import "interfaces/IPairFunctionsTypes.sol";
|
import "interfaces/ISwapAdapterTypes.sol";
|
||||||
|
|
||||||
contract UniswapV2PairFunctionTest is Test, IPairFunctionTypes {
|
contract UniswapV2PairFunctionTest is Test, ISwapAdapterTypes {
|
||||||
UniswapV2PairFunctions pairFunctions;
|
UniswapV2SwapAdapter pairFunctions;
|
||||||
IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
|
IERC20 constant WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
|
||||||
IERC20 constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
|
IERC20 constant USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
|
||||||
address constant USDC_WETH_PAIR = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc;
|
address constant USDC_WETH_PAIR = 0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc;
|
||||||
@@ -16,10 +16,10 @@ contract UniswapV2PairFunctionTest is Test, IPairFunctionTypes {
|
|||||||
uint256 forkBlock = 17000000;
|
uint256 forkBlock = 17000000;
|
||||||
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
vm.createSelectFork(vm.rpcUrl("mainnet"), forkBlock);
|
||||||
pairFunctions = new
|
pairFunctions = new
|
||||||
UniswapV2PairFunctions(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
|
UniswapV2SwapAdapter(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testPriceFuzz(uint256 amount0, uint256 amount1) public view {
|
function testPriceFuzz(uint256 amount0, uint256 amount1) public {
|
||||||
bytes32 pair = bytes32(bytes20(USDC_WETH_PAIR));
|
bytes32 pair = bytes32(bytes20(USDC_WETH_PAIR));
|
||||||
uint256[] memory limits = pairFunctions.getLimits(pair, SwapSide.Sell);
|
uint256[] memory limits = pairFunctions.getLimits(pair, SwapSide.Sell);
|
||||||
vm.assume(amount0 < limits[0]);
|
vm.assume(amount0 < limits[0]);
|
||||||
@@ -29,7 +29,13 @@ contract UniswapV2PairFunctionTest is Test, IPairFunctionTypes {
|
|||||||
amounts[0] = amount0;
|
amounts[0] = amount0;
|
||||||
amounts[1] = amount1;
|
amounts[1] = amount1;
|
||||||
|
|
||||||
pairFunctions.price(pair, WETH, USDC, amounts);
|
Fraction[] memory prices =
|
||||||
|
pairFunctions.price(pair, WETH, USDC, amounts);
|
||||||
|
|
||||||
|
for (uint256 i = 0; i < prices.length; i++) {
|
||||||
|
assertGt(prices[i].nominator, 0);
|
||||||
|
assertGt(prices[i].denominator, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function testPriceDecreasing() public {
|
function testPriceDecreasing() public {
|
||||||
@@ -43,8 +49,10 @@ contract UniswapV2PairFunctionTest is Test, IPairFunctionTypes {
|
|||||||
Fraction[] memory prices =
|
Fraction[] memory prices =
|
||||||
pairFunctions.price(pair, WETH, USDC, amounts);
|
pairFunctions.price(pair, WETH, USDC, amounts);
|
||||||
|
|
||||||
for (uint256 i = 1; i < 99; i++) {
|
for (uint256 i = 0; i < 99; i++) {
|
||||||
assertEq(compareFractions(prices[i], prices[i + 1]), 1);
|
assertEq(compareFractions(prices[i], prices[i + 1]), 1);
|
||||||
|
assertGt(prices[i].denominator, 0);
|
||||||
|
assertGt(prices[i + 1].denominator, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,4 +117,16 @@ contract UniswapV2PairFunctionTest is Test, IPairFunctionTypes {
|
|||||||
function testSwapBuyIncreasing() public {
|
function testSwapBuyIncreasing() public {
|
||||||
executeIncreasingSwaps(SwapSide.Buy);
|
executeIncreasingSwaps(SwapSide.Buy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testGetCapabilities(bytes32 pair, address t0, address t1) public {
|
||||||
|
Capabilities[] memory res =
|
||||||
|
pairFunctions.getCapabilities(pair, IERC20(t0), IERC20(t1));
|
||||||
|
|
||||||
|
assertEq(res.length, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testGetLimits() public {
|
||||||
|
bytes32 pair = bytes32(bytes20(USDC_WETH_PAIR));
|
||||||
|
pairFunctions.getLimits(pair, SwapSide.Sell);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user