This commit is contained in:
Tim Olson
2023-08-19 18:13:43 -04:00
commit e76bc70297
8 changed files with 167 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
cache/
out/
.idea/

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std

41
docs/order_types.md Normal file
View File

@@ -0,0 +1,41 @@
# Order Types
## TWAP
* time-to-expiry is required
* number of tranches > 1
* optional lower and upper price bounds
## DCA
* similar to a TWAP, but the tranches are divided by value instead of quantity
## Timed Entry
* specific time when an order is triggered
* can be market/limit TWAP, whatever
## Limit
## Stop
* enabled on a price condition instead of at a specific time
* how to enforce this in the contract? would need a price history of when the stop was touched. perhaps this is a use case for a "chain" order where one set of constraints doesn't invoke a trade but instead creates a subsequent order
## Ladder
* split into many traches across a range of prices
* required number of tranches
* required upper and lower bounds of the ladder
# Conditions
* current price above/below
* historical price touched above/below (e.g. trailing stop): keep an updated data structure of swing highs & lows. this structure must only be updated once before the relevant observation rolls off the back of the window. "management gas"
* volume above/below
* historical volume touched above/below: this could work like historical price swing high/lows if we first bucket the volumes into sizes <= the pool observation window
* per-swap slippage constraint
# Gas
* an amount for gas is reserved ahead of time
* excess gas is kept
* Dexible has a gas refund delay, saying:
> `get/setLockoutBlocks` Retrieves or sets the number of blocks a trader must wait before withdrawing their gas deposit. This is to prevent traders from front-running a relay that submitted an order in order to circumvent paying for the execution or forcing a failed txn.

9
foundry.toml Normal file
View File

@@ -0,0 +1,9 @@
[profile.default]
libs = ['lib']
remappings = [
'@openzeppelin/contracts/=lib/openzeppelin/contracts/',
'@uniswap/v3-core/=lib/uniswap/v3-core/',
'@uniswap/v3-periphery/=lib/uniswap/v3-periphery/',
]
optimizer=true
optimizer_runs=999999999

1
lib/forge-std Submodule

Submodule lib/forge-std added at 74cfb77e30

22
src/IERC20Metadata.sol Normal file
View File

@@ -0,0 +1,22 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// the version of OpenZeppelin required by Uniswap v3 doesn't have IERC20Metadata yet, so we copy it here.
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}

17
src/MockERC20.sol Normal file
View File

@@ -0,0 +1,17 @@
import "openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./IERC20Metadata.sol";
contract MockERC20 is ERC20, IERC20Metadata {
constructor(string name, string symbol, uint8 decimals)
ERC20(name, symbol)
{
_setupDecimals(decimals);
}
function mint(address account, uint256 amount) external {
_mint(account, amount);
}
}

71
test/TestSinglePool.sol Normal file
View File

@@ -0,0 +1,71 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
import "forge-std/console2.sol";
import "../src/MockERC20.sol";
import "uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import "uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "uniswap/v3-core/contracts/libraries/TickMath.sol";
import "uniswap/v3-periphery/contracts/interfaces/INonfungiblePositionManager.sol";
import "uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";
import "uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";
contract TestSinglePool {
IUniswapV3Factory public factory = IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984);
INonfungiblePositionManager public nfpm = INonfungiblePositionManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88);
ISwapRouter public swapper = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
IUniswapV3Pool public immutable pool;
uint24 public fee;
MockERC20 public WETH;
MockERC20 public USDC;
MockERC20 public token0; // either WETH or USDC depending on the order in the pool
MockERC20 public token1;
function setUp() public {
MockERC20 weth = MockERC20('Mock Wrapped Ethereum', 'WETH', 18);
MockERC20 usdc = MockERC20('Mock USD Coin', 'USDC', 6);
uint24 fee_ = 500;
fee = fee_;
WETH = weth;
USDC = usdc;
IUniswapV3Pool pool_ = UniswapV3Pool(factory.createPool(address(weth), address(usdc), fee_));
pool = pool_;
token0 = pool_.token0();
token1 = pool_.token1();
}
// struct MintParams {
// address token0;
// address token1;
// uint24 fee;
// int24 tickLower;
// int24 tickUpper;
// uint256 amount0Desired;
// uint256 amount1Desired;
// uint256 amount0Min;
// uint256 amount1Min;
// address recipient;
// uint256 deadline;
// }
function stake(uint160 liquidity, uint24 lower, uint24 upper) public {
uint160 sqrtRatioAX96 = TickMath.getSqrtRatioAtTick(lower);
uint160 sqrtRatioBX96 = TickMath.getSqrtRatioAtTick(upper);
(uint160 sqrtPriceX96, , , , , , ) = pool.slot0();
(uint256 amount0, uint256 amount1) = LiquidityAmounts.getAmountsForLiquidity(liquidity, sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96);
token0.mint(amount0);
token1.mint(amount1);
INonfungiblePositionManager.MintParams memory params = INonfungiblePositionManager.MintParams(
address(token0), address(token1), fee, lower, upper, amount0, amount1, 0, 0, msg.sender, block.timestamp
);
nfpm.mint(params);
}
function swap() public {
}
}