diff --git a/interfaces/IPairFunctions.sol b/interfaces/IPairFunctions.sol index a4317fc..da5824c 100644 --- a/interfaces/IPairFunctions.sol +++ b/interfaces/IPairFunctions.sol @@ -1,6 +1,135 @@ // SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.13; +import "openzeppelin-contracts/contracts/interfaces/IERC20.sol"; + +/// @title IPairFunctions +/// @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 given pair: The swap(x), gas(x) and price(x) functions: +/// @dev The swap function accepts some specified token amount: x and returns the +/// @dev amount y a user can get by swapping x through the venue. +/// @dev The gas function simply returns the estimated gas cost given a specified +/// @dev amount x. +/// @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 pair after swapping x of the specified token. +/// @dev During calls to price, swap and getLimits, the caller can be assumed to +/// @dev have the required sell or buy token balance as well as unlimited approvals +/// @dev to this contract. interface IPairFunctions { - function price() external returns (uint256); -} \ No newline at end of file + /// @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. + enum SwapSide { + Sell, + Buy + } + + /// @dev The Capabilities enum represents possible features of a trading pair. + enum Capabilities + // Support SwapSide.Sell values (required) + { + SellSide, + // Support SwapSide.Buy values (optional) + BuySide, + // Support evaluating the price function (optional) + PriceFunction, + // Support tokens that charge a fee on transfer (optional) + FeeOnTransfer, + // The pair does not suffer from price impact and mantains + // a constant price for increasingly larger speficied amounts. + // (optional) + ConstantPrice, + // Indicates that the pair does not read it's own token balances + // while swapping. (optional) + TokenBalanceIndependent + } + + /// @dev Representation used for rational numbers such as prices. + struct Fraction { + uint256 nominator; + uint256 denominator; + } + + /// @dev The Trade struct holds data about an executed trade. + struct Trade { + uint256 receivedAmount; // The amount received from the trade. + uint256 gasUsed; // The amount of gas used in the trade. + uint256 Fraction; // The price of the pair after the trade. + } + + /// @dev The Unavailable error is thrown when a pool or swap is not + /// @dev available for unexpected reason, e.g. because it was paused + /// @dev due to a bug. + error Unavailable(string reason); + + /// @dev The LimitExceeded error is thrown when a limit has been + /// @dev exceeded. E.g. the specified amount can't be traded safely. + error LimitExceeded(uint256 limit); + + /// @notice Calculates pair prices for specified amounts (optional). + /// @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 Ideally this method should be implemented, although it is optional as + /// @dev the price function can be numerically estimated from the swap function. + /// @dev In case it is not available it should be flagged via capabilities and + /// @dev calling it should revert using the `NotImplemented` error. In case implemented + /// @dev the method should ideally be view as this is usually more efficient + /// @dev and can be run in parallel. If necessary though, the method is allowed to + /// @dev be state changing this is still better than not providing a implementation + /// @dev all. + /// @param pairId The ID of the trading pair. + /// @param sellToken The token being sold. + /// @param buyToken The token being bought. + /// @param specifiedAmounts The specified amounts used for price calculation. + /// @return prices array of prices as fractions corresponding to the provided amounts. + function price(bytes32 pairId, IERC20 sellToken, IERC20 buyToken, uint256[] memory sellAmounts) + external + returns (Fraction[] prices); + + /// @notice Simulates swapping tokens on a given pair. + /// @dev This function should be state modifying meaning it should actually execute + /// @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 by using the `gasleft()` function. + /// @dev + /// @param pairId The ID of the trading pair. + /// @param sellToken The token being sold. + /// @param buyToken The token being bought. + /// @param side The side of the trade (Sell or Buy). + /// @param specifiedAmounts The amounts to be traded. + /// @return trades array of Trade structs representing each executed trade. + function swap(bytes32 pairId, IERC20 sellToken, IERC20 buyToken, SwapSide side, uint256[] memory specifiedAmounts) + external + returns (Trade[] trades); + + /// @notice Retrieves the limits for each token. + /// @dev Retrieve the maximum limits of a token that can be traded. The limit is reached + /// @dev when the change in the received amounts is zero or close to zero. If in doubt + /// @dev over estimate. The swap function should not error with `LimitExceeded` if + /// @dev called with amounts below the limit. + /// @param pairId The ID of the trading pair. + /// @return An array of limits. + function getLimits(bytes32 pairId, SwapSide side) external returns (uint256[]); + + /// @notice Retrieves the capabilities of the selected pair. + /// @param pairId The ID of the trading pair. + /// @return An array of Capabilities. + function getCapabilities(bytes32 pairId, IERC20 sellToken, IERC20 buyToken) external returns (Capabilities[]); + + /// @notice Retrieves the tokens in the selected pair. + /// @dev Mainly used for testing as this is redundant with the required substreams + /// @dev implementation. + /// @param pairId The ID of the trading pair. + /// @return tokens array of IERC20 contracts. + function getTokens(bytes32 pairId) external returns (IERC20[] tokens); + + /// @notice Retrieves a range of pool IDs. + /// @dev Mainly used for testing it is alright to not return all available pools here. + /// @dev Nevertheless this is useful to test against the substreams implementation. If + /// @dev implemented it safes time writing custom tests. + /// @param offset The starting index from which to retrieve pool IDs. + /// @param limit The maximum number of pool IDs to retrieve. + /// @return ids array of pool IDs. + function getPoolIds(uint256 offset, uint256 limit) external returns (bytes32[] ids); +} diff --git a/test/Counter.t.sol b/test/Counter.t.sol index 30235e8..0c4d469 100644 --- a/test/Counter.t.sol +++ b/test/Counter.t.sol @@ -2,23 +2,5 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; -import "../src/Counter.sol"; -contract CounterTest is Test { - Counter public counter; - - function setUp() public { - counter = new Counter(); - counter.setNumber(0); - } - - function testIncrement() public { - counter.increment(); - assertEq(counter.number(), 1); - } - - function testSetNumber(uint256 x) public { - counter.setNumber(x); - assertEq(counter.number(), x); - } -} +contract CounterTest is Test {}