removed PriceConstraint and implemented LineConstraint; MockDeploy copied/separated from Deploy
This commit is contained in:
@@ -11,7 +11,7 @@ anvil -f arbitrum_mock --chain-id 31337 &
|
||||
ANVIL_PID=$!
|
||||
sleep 2
|
||||
|
||||
forge script script/Deploy.sol -vvvv --fork-url http://localhost:8545 --broadcast
|
||||
forge script script/DeployMock.sol -vvvv --fork-url http://localhost:8545 --broadcast
|
||||
|
||||
trap_ctrlc() {
|
||||
echo exiting anvil
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
| Code | Name | Description |
|
||||
|------|---------------------------|------------------------------------------------------------------------|
|
||||
| OCOM | Invalid OCO Mode | The OCO mode provided to placeOrder() is invalid. |
|
||||
| UR | Unknown Route | The specified order route is invalid. |
|
||||
| NO | Not Open | Order status state is not OPEN |
|
||||
| UC | Unknown Constraint | The constraint specification did not have a recognized Constraint Mode |
|
||||
| TE | Too Early | Time constraint window hasn't opened yet |
|
||||
| TL | Too Late | Time constraint has expired the tranche |
|
||||
| L | Limit | Price limit constraint violation |
|
||||
| IIA | Insufficient Input Amount | Not enough input coin available in the vault (from Uniswap) |
|
||||
| TF | Tranche Filled | The tranche has no remaining amount available to execute. |
|
||||
| Code | Name | Description |
|
||||
|----------------------|---------------------------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| OCOM | Invalid OCO Mode | The OCO mode provided to placeOrder() is invalid. |
|
||||
| UR | Unknown Route | The specified order route is invalid. |
|
||||
| NO | Not Open | Order status state is not OPEN |
|
||||
| UC | Unknown Constraint | The constraint specification did not have a recognized Constraint Mode |
|
||||
| TE | Too Early | Time constraint window hasn't opened yet |
|
||||
| TL | Too Late | Time constraint has expired the tranche |
|
||||
| L | Limit | Price limit constraint violation |
|
||||
| IIA | Insufficient Input Amount | Not enough input coin available in the vault (from Uniswap) |
|
||||
| TF | Tranche Filled | The tranche has no remaining amount available to execute. |
|
||||
| Too little received | Too little received | Uniswap v3 error when min output amount is not filled. Can happen when a limit price is very near the current price. |
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
// pragma solidity =0.7.6;
|
||||
pragma solidity >=0.8.0;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
@@ -13,11 +12,9 @@ contract Deploy is Script {
|
||||
function run() external {
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
// Factory deployer = new Factory{salt:keccak256(abi.encode(1))}(); // version 1
|
||||
Factory deployer = new Factory(); // hardhat often breaks on the CREATE2 above :(
|
||||
Factory deployer = new Factory{salt:keccak256(abi.encode(1))}(); // version 1
|
||||
QueryHelper query = new QueryHelper();
|
||||
Dexorder dexorder = new Dexorder();
|
||||
// MockEnv mock = new MockEnv();
|
||||
vm.stopBroadcast();
|
||||
console2.log('Factory');
|
||||
console2.log(address(deployer));
|
||||
@@ -25,7 +22,5 @@ contract Deploy is Script {
|
||||
console2.log(address(query));
|
||||
console2.log('Dexorder');
|
||||
console2.log(address(dexorder));
|
||||
// console2.log('MockEnv'); // todo no mock in production deployment
|
||||
// console2.log(address(mock));
|
||||
}
|
||||
}
|
||||
|
||||
31
script/DeployMock.sol
Normal file
31
script/DeployMock.sol
Normal file
@@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: UNLICENSED
|
||||
pragma solidity >=0.8.0;
|
||||
|
||||
import "forge-std/Script.sol";
|
||||
import "forge-std/console2.sol";
|
||||
import "../src/QueryHelper.sol";
|
||||
import "../src/Factory.sol";
|
||||
import "../src/Dexorder.sol";
|
||||
import "../test/MockEnv.sol";
|
||||
|
||||
contract DeployMock is Script {
|
||||
function run() external {
|
||||
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
|
||||
vm.startBroadcast(deployerPrivateKey);
|
||||
// hardhat often breaks on the CREATE2 so we disable it for mock
|
||||
// Factory deployer = new Factory{salt:keccak256(abi.encode(1))}(); // version 1
|
||||
Factory deployer = new Factory();
|
||||
QueryHelper query = new QueryHelper();
|
||||
Dexorder dexorder = new Dexorder();
|
||||
MockEnv mock = new MockEnv();
|
||||
vm.stopBroadcast();
|
||||
console2.log('Factory');
|
||||
console2.log(address(deployer));
|
||||
console2.log('QueryHelper');
|
||||
console2.log(address(query));
|
||||
console2.log('Dexorder');
|
||||
console2.log(address(dexorder));
|
||||
console2.log('MockEnv');
|
||||
console2.log(address(mock));
|
||||
}
|
||||
}
|
||||
@@ -66,21 +66,13 @@ library OrderLib {
|
||||
|
||||
enum ConstraintMode {
|
||||
Time,
|
||||
Limit,
|
||||
Trailing,
|
||||
Barrier,
|
||||
Line
|
||||
Line,
|
||||
Barrier
|
||||
}
|
||||
|
||||
struct Constraint {
|
||||
ConstraintMode mode; // type information
|
||||
bytes constraint; // abi packed-encoded constraint struct: decode according to mode
|
||||
}
|
||||
|
||||
struct PriceConstraint {
|
||||
bool isAbove;
|
||||
bool isRatio;
|
||||
uint160 valueSqrtX96;
|
||||
bytes constraint; // abi-encoded constraint struct: decode according to mode
|
||||
}
|
||||
|
||||
struct LineConstraint {
|
||||
@@ -217,35 +209,36 @@ library OrderLib {
|
||||
if (time < block.timestamp)
|
||||
revert('TL'); // time late
|
||||
}
|
||||
else if (constraint.mode == ConstraintMode.Limit) {
|
||||
console2.log('limit constraint');
|
||||
if( sqrtPriceX96 == 0 ) {
|
||||
else if (constraint.mode == ConstraintMode.Line) {
|
||||
console2.log('line constraint');
|
||||
if( sqrtPriceX96 == 0 )
|
||||
(sqrtPriceX96,,,,,,) = IUniswapV3Pool(pool).slot0();
|
||||
}
|
||||
PriceConstraint memory pc = abi.decode(constraint.constraint, (PriceConstraint));
|
||||
LineConstraint memory lc = abi.decode(constraint.constraint, (LineConstraint));
|
||||
uint256 price = sqrtPriceX96;
|
||||
if( pc.isRatio )
|
||||
pc.valueSqrtX96 = uint160(price * pc.valueSqrtX96 / 2**96); // todo overflow check!
|
||||
if( pc.isAbove && price < pc.valueSqrtX96 || !pc.isAbove && price > pc.valueSqrtX96 )
|
||||
if( lc.isRatio )
|
||||
revert('ratio not implemented'); // todo the ratio must be computed when the order is placed
|
||||
// c.valueSqrtX96 = uint160(price * c.valueSqrtX96 / 2**96);
|
||||
int256 limit256 = int256(uint256(lc.valueSqrtX96));
|
||||
if( lc.slopeSqrtX96 != 0 ) {
|
||||
limit256 += int256(block.timestamp - lc.time) * lc.slopeSqrtX96 / 2**96;
|
||||
if( limit256 < 0 )
|
||||
limit256 = 0;
|
||||
}
|
||||
console2.log(limit256);
|
||||
uint160 limit = uint160(uint256(limit256));
|
||||
// use <= and >= here because trading AT the limit results in 0 volume. price must exceed the limit.
|
||||
if( lc.isAbove && price <= limit || !lc.isAbove && price >= limit )
|
||||
revert('L');
|
||||
if( sqrtPriceLimitX96 == 0 ||
|
||||
pc.isAbove && pc.valueSqrtX96 < sqrtPriceLimitX96 ||
|
||||
!pc.isAbove && pc.valueSqrtX96 > sqrtPriceLimitX96
|
||||
lc.isAbove && limit < sqrtPriceLimitX96 ||
|
||||
!lc.isAbove && limit > sqrtPriceLimitX96
|
||||
)
|
||||
sqrtPriceLimitX96 = pc.valueSqrtX96;
|
||||
sqrtPriceLimitX96 = limit;
|
||||
}
|
||||
else if (constraint.mode == ConstraintMode.Barrier) {
|
||||
console2.log('barrier constraint');
|
||||
revert('NI'); // not implemented
|
||||
}
|
||||
else if (constraint.mode == ConstraintMode.Trailing) {
|
||||
console2.log('trailing constraint');
|
||||
revert('NI'); // not implemented
|
||||
}
|
||||
else if (constraint.mode == ConstraintMode.Line) {
|
||||
console2.log('line constraint');
|
||||
revert('NI'); // not implemented
|
||||
}
|
||||
else // unknown constraint
|
||||
revert('UC'); // not implemented
|
||||
}
|
||||
|
||||
@@ -142,4 +142,23 @@ contract MockEnv {
|
||||
);
|
||||
return swapper.exactInputSingle(params);
|
||||
}
|
||||
|
||||
function price() public view returns (uint160 sqrtPrice) {
|
||||
(sqrtPrice,,,,,,) = pool.slot0();
|
||||
}
|
||||
|
||||
function swapToPrice(uint160 sqrtPriceLimitX96) public {
|
||||
console2.log('swapToPrice');
|
||||
console2.log(sqrtPriceLimitX96);
|
||||
uint160 curPrice = price();
|
||||
console2.log(curPrice);
|
||||
if( curPrice == sqrtPriceLimitX96 )
|
||||
return;
|
||||
MockERC20 inToken = curPrice > sqrtPriceLimitX96 ? MockERC20(token0) : MockERC20(token1);
|
||||
MockERC20 outToken = curPrice < sqrtPriceLimitX96 ? MockERC20(token0) : MockERC20(token1);
|
||||
// instead of calculating how much we need, we just mint an absurd amount
|
||||
uint256 aLot = 2**100;
|
||||
inToken.mint(address(this), aLot);
|
||||
swap(inToken, outToken, aLot, sqrtPriceLimitX96);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ contract TestOrder is MockEnv, Test {
|
||||
COIN.mint(address(vault), amount); // create COIN to sell
|
||||
OrderLib.SwapOrder memory order = OrderLib.SwapOrder(
|
||||
address(COIN), address(USD), // sell COIN for USD
|
||||
OrderLib.Route(OrderLib.Exchange.UniswapV3, 500), amount, true, false,
|
||||
OrderLib.Route(OrderLib.Exchange.UniswapV3, fee), amount, true, false,
|
||||
OrderLib.NO_CHAIN, tranches
|
||||
);
|
||||
uint64 orderIndex = vault.numSwapOrders();
|
||||
@@ -88,4 +88,38 @@ contract TestOrder is MockEnv, Test {
|
||||
console2.log('executed');
|
||||
}
|
||||
|
||||
|
||||
function testExecuteLimitOrder() public {
|
||||
// test selling token0 above a certain price
|
||||
OrderLib.Tranche[] memory tranches = new OrderLib.Tranche[](1);
|
||||
OrderLib.Constraint[] memory constraints1 = new OrderLib.Constraint[](1);
|
||||
uint160 limit = price() * 10001 / 10000; // 1bp above the current price
|
||||
bytes memory serialized = abi.encode( OrderLib.LineConstraint(true, false, 0, limit, 0) );
|
||||
constraints1[0] = OrderLib.Constraint(OrderLib.ConstraintMode.Line, serialized);
|
||||
tranches[0] = OrderLib.Tranche(type(uint16).max,constraints1);
|
||||
MockERC20 token = MockERC20(token0);
|
||||
uint256 amount = 3*10**token.decimals() / 10; // selling 0.3 token0
|
||||
token.mint(address(vault), amount);
|
||||
OrderLib.SwapOrder memory order = OrderLib.SwapOrder(
|
||||
token0, token1, // sell
|
||||
OrderLib.Route(OrderLib.Exchange.UniswapV3, fee), amount, true, false,
|
||||
OrderLib.NO_CHAIN, tranches
|
||||
);
|
||||
uint64 orderIndex = vault.numSwapOrders();
|
||||
vault.placeOrder(order);
|
||||
console2.log('placed order');
|
||||
console2.log(uint(orderIndex));
|
||||
|
||||
vm.expectRevert(bytes('L'));
|
||||
vault.execute(orderIndex, 0, OrderLib.PriceProof(0)); // should revert with code 'L'
|
||||
console2.log('successfully failed to execute below limit price');
|
||||
|
||||
swapToPrice(limit); // move price to exactly the limit
|
||||
vm.expectRevert(bytes('L')); // the limit is violated. no liquidity can be taken without moving the price.
|
||||
vault.execute(orderIndex, 0, OrderLib.PriceProof(0)); // should work now that the price is high enough
|
||||
|
||||
swapToPrice(limit*10001/10000); // move price to be 1bp abouve our limit
|
||||
console2.log('successfully executed at limit price');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user