TRANCHE EXECUTION WORKS
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
| Code | Name | Description |
|
||||
|-------|--------------------|------------------------------------------------------------------------|
|
||||
| UC | Unknown Constraint | The constraint specification did not have a recognized Constraint Mode |
|
||||
|------|---------------------------|------------------------------------------------------------------------|
|
||||
| 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. |
|
||||
|
||||
@@ -17,7 +17,7 @@ contract Deploy is Script {
|
||||
Factory deployer = new Factory(); // hardhat often breaks on the CREATE2 above :(
|
||||
QueryHelper query = new QueryHelper();
|
||||
Dexorder dexorder = new Dexorder();
|
||||
MockEnv mock = new MockEnv();
|
||||
// MockEnv mock = new MockEnv();
|
||||
vm.stopBroadcast();
|
||||
console2.log('Factory');
|
||||
console2.log(address(deployer));
|
||||
@@ -25,7 +25,7 @@ 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));
|
||||
// console2.log('MockEnv'); // todo no mock in production deployment
|
||||
// console2.log(address(mock));
|
||||
}
|
||||
}
|
||||
|
||||
123
src/OrderLib.sol
123
src/OrderLib.sol
@@ -192,7 +192,7 @@ library OrderLib {
|
||||
// TE current time is too early for this tranche
|
||||
// TL current time is too late for this tranche
|
||||
//
|
||||
function execute(OrdersInfo storage self, address owner, uint64 orderIndex, uint8 trancheIndex, PriceProof memory proof) internal {
|
||||
function execute(OrdersInfo storage self, address owner, uint64 orderIndex, uint8 trancheIndex, PriceProof memory ) internal {
|
||||
console2.log('execute');
|
||||
console2.log(address(this));
|
||||
console2.log(uint(orderIndex));
|
||||
@@ -205,67 +205,86 @@ library OrderLib {
|
||||
uint160 sqrtPriceLimitX96 = 0; // 0 means "not set yet" and 1 is the minimum value
|
||||
// todo other routes
|
||||
address pool = Constants.uniswapV3Factory.getPool(status.order.tokenIn, status.order.tokenOut, status.order.route.fee);
|
||||
// for (uint8 c = 0; c < tranche.constraints.length; c++) {
|
||||
// Constraint storage constraint = tranche.constraints[c];
|
||||
// if (constraint.mode == ConstraintMode.Time) {
|
||||
// TimeConstraint memory tc = abi.decode(constraint.constraint, (TimeConstraint));
|
||||
// uint32 time = tc.earliest.mode == TimeMode.Timestamp ? tc.earliest.time : status.start + tc.earliest.time;
|
||||
// if (time > block.timestamp)
|
||||
// revert('TE'); // time early
|
||||
// time = tc.latest.mode == TimeMode.Timestamp ? tc.latest.time : status.start + tc.latest.time;
|
||||
// if (time < block.timestamp)
|
||||
// revert('TL'); // time late
|
||||
// }
|
||||
// else if (constraint.mode == ConstraintMode.Limit) {
|
||||
// if( sqrtPriceX96 == 0 ) {
|
||||
// (sqrtPriceX96,,,,,,) = IUniswapV3Pool(pool).slot0();
|
||||
// }
|
||||
// PriceConstraint memory pc = abi.decode(constraint.constraint, (PriceConstraint));
|
||||
// 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 )
|
||||
// revert('L');
|
||||
// if( sqrtPriceLimitX96 == 0 ||
|
||||
// pc.isAbove && pc.valueSqrtX96 < sqrtPriceLimitX96 ||
|
||||
// !pc.isAbove && pc.valueSqrtX96 > sqrtPriceLimitX96
|
||||
// )
|
||||
// sqrtPriceLimitX96 = pc.valueSqrtX96;
|
||||
// }
|
||||
// else if (constraint.mode == ConstraintMode.Barrier) {
|
||||
// revert('NI'); // not implemented
|
||||
// }
|
||||
// else if (constraint.mode == ConstraintMode.Trailing) {
|
||||
// revert('NI'); // not implemented
|
||||
// }
|
||||
// else if (constraint.mode == ConstraintMode.Line) {
|
||||
// revert('NI'); // not implemented
|
||||
// }
|
||||
// else // unknown constraint
|
||||
// revert('NI'); // not implemented
|
||||
// }
|
||||
for (uint8 c = 0; c < tranche.constraints.length; c++) {
|
||||
Constraint storage constraint = tranche.constraints[c];
|
||||
if (constraint.mode == ConstraintMode.Time) {
|
||||
console2.log('time constraint');
|
||||
TimeConstraint memory tc = abi.decode(constraint.constraint, (TimeConstraint));
|
||||
uint32 time = tc.earliest.mode == TimeMode.Timestamp ? tc.earliest.time : status.start + tc.earliest.time;
|
||||
if (time > block.timestamp)
|
||||
revert('TE'); // time early
|
||||
time = tc.latest.mode == TimeMode.Timestamp ? tc.latest.time : status.start + tc.latest.time;
|
||||
if (time < block.timestamp)
|
||||
revert('TL'); // time late
|
||||
}
|
||||
else if (constraint.mode == ConstraintMode.Limit) {
|
||||
console2.log('limit constraint');
|
||||
if( sqrtPriceX96 == 0 ) {
|
||||
(sqrtPriceX96,,,,,,) = IUniswapV3Pool(pool).slot0();
|
||||
}
|
||||
PriceConstraint memory pc = abi.decode(constraint.constraint, (PriceConstraint));
|
||||
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 )
|
||||
revert('L');
|
||||
if( sqrtPriceLimitX96 == 0 ||
|
||||
pc.isAbove && pc.valueSqrtX96 < sqrtPriceLimitX96 ||
|
||||
!pc.isAbove && pc.valueSqrtX96 > sqrtPriceLimitX96
|
||||
)
|
||||
sqrtPriceLimitX96 = pc.valueSqrtX96;
|
||||
}
|
||||
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
|
||||
}
|
||||
console2.log('computing amount');
|
||||
console2.log(status.order.amount);
|
||||
console2.log(tranche.fraction);
|
||||
console2.log(status.order.amountIsInput);
|
||||
console2.log(status.filledIn);
|
||||
console2.log(status.filledOut);
|
||||
console2.log(status.trancheFilledIn[trancheIndex]);
|
||||
console2.log(status.trancheFilledOut[trancheIndex]);
|
||||
uint256 amount = status.order.amount * tranche.fraction / type(uint16).max // the most this tranche could do
|
||||
- (status.order.amountIsInput ? status.trancheFilledIn[trancheIndex] : status.trancheFilledOut[trancheIndex]); // minus tranche fills
|
||||
console2.log('amount');
|
||||
console2.log(amount);
|
||||
// order amount remaining
|
||||
require( (status.order.amountIsInput ? status.filledIn : status.filledOut) <= status.order.amount, 'OVERFILL' );
|
||||
uint256 remaining = status.order.amount - (status.order.amountIsInput ? status.filledIn : status.filledOut);
|
||||
console2.log('remaining');
|
||||
console2.log(remaining);
|
||||
if (amount > remaining) // not more than the order's overall remaining amount
|
||||
amount = remaining;
|
||||
require( amount > 0, 'TF' );
|
||||
console2.log(amount);
|
||||
address recipient = status.order.outputDirectlyToOwner ? owner : address(this);
|
||||
console2.log(recipient);
|
||||
uint256 amountIn;
|
||||
uint256 amountOut;
|
||||
// if( status.order.route.exchange == Exchange.UniswapV3 )
|
||||
if( status.order.route.exchange == Exchange.UniswapV3 )
|
||||
(amountIn, amountOut) = _do_execute_univ3(recipient, status.order, pool, amount, sqrtPriceLimitX96);
|
||||
// todo other routes
|
||||
// else
|
||||
// revert('UR'); // unknown route
|
||||
// status.filledIn += amountIn;
|
||||
// status.filledOut += amountOut;
|
||||
// status.trancheFilledIn[trancheIndex] += amountIn;
|
||||
// status.trancheFilledOut[trancheIndex] += amountOut;
|
||||
// emit DexorderSwapFilled(orderIndex, trancheIndex, amountIn, amountOut);
|
||||
// _checkCompleted(self, orderIndex, status);
|
||||
else
|
||||
revert('UR'); // unknown route
|
||||
status.filledIn += amountIn;
|
||||
status.filledOut += amountOut;
|
||||
status.trancheFilledIn[trancheIndex] += amountIn;
|
||||
status.trancheFilledOut[trancheIndex] += amountOut;
|
||||
emit DexorderSwapFilled(orderIndex, trancheIndex, amountIn, amountOut);
|
||||
_checkCompleted(self, orderIndex, status);
|
||||
}
|
||||
|
||||
|
||||
@@ -276,13 +295,11 @@ library OrderLib {
|
||||
console2.log('price limit');
|
||||
console2.log(uint(sqrtPriceLimitX96));
|
||||
if (order.amountIsInput) {
|
||||
amountIn = amount;
|
||||
amountOut = UniswapSwapper.swapExactInput(UniswapSwapper.SwapParams(
|
||||
(amountIn, amountOut) = UniswapSwapper.swapExactInput(UniswapSwapper.SwapParams(
|
||||
pool, order.tokenIn, order.tokenOut, recipient, order.route.fee, amount, sqrtPriceLimitX96));
|
||||
}
|
||||
else {
|
||||
amountOut = amount;
|
||||
amountIn = UniswapSwapper.swapExactOutput(UniswapSwapper.SwapParams(
|
||||
(amountIn, amountOut) = UniswapSwapper.swapExactOutput(UniswapSwapper.SwapParams(
|
||||
pool, order.tokenIn, order.tokenOut, recipient, order.route.fee, amount, sqrtPriceLimitX96));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,8 @@ contract QueryHelper {
|
||||
// here we find the highest liquidity pool for v2 and for v3
|
||||
uint24[4] memory fees = [uint24(100),500,3000,10000];
|
||||
uint24 uniswapV2Fee = 0;
|
||||
uint128 uniswapV2Liquidity = 0;
|
||||
address uniswapV2Pool = address(0);
|
||||
// uint128 uniswapV2Liquidity = 0;
|
||||
// address uniswapV2Pool = address(0);
|
||||
uint24 uniswapV3Fee = 0;
|
||||
uint128 uniswapV3Liquidity = 0;
|
||||
address uniswapV3Pool = address(0);
|
||||
|
||||
@@ -22,7 +22,7 @@ library UniswapSwapper {
|
||||
uint160 sqrtPriceLimitX96;
|
||||
}
|
||||
|
||||
function swapExactInput(SwapParams memory params) internal returns (uint256 amountOut)
|
||||
function swapExactInput(SwapParams memory params) internal returns (uint256 amountIn, uint256 amountOut)
|
||||
{
|
||||
// struct ExactInputSingleParams {
|
||||
// address tokenIn;
|
||||
@@ -44,23 +44,30 @@ library UniswapSwapper {
|
||||
console2.log(uint(params.sqrtPriceLimitX96));
|
||||
console2.log(address(Constants.uniswapV3SwapRouter));
|
||||
|
||||
TransferHelper.safeApprove(params.tokenIn, address(Constants.uniswapV3SwapRouter), params.amount);
|
||||
amountIn = params.amount;
|
||||
uint256 balance = IERC20(params.tokenIn).balanceOf(address(this));
|
||||
if( balance == 0 ) {
|
||||
// todo dust?
|
||||
revert('IIA');
|
||||
}
|
||||
if( balance < amountIn )
|
||||
amountIn = balance;
|
||||
|
||||
TransferHelper.safeApprove(params.tokenIn, address(Constants.uniswapV3SwapRouter), amountIn);
|
||||
// if (params.sqrtPriceLimitX96 == 0)
|
||||
// params.sqrtPriceLimitX96 = params.tokenIn < params.tokenOut ? TickMath.MIN_SQRT_RATIO+1 : TickMath.MAX_SQRT_RATIO-1;
|
||||
|
||||
console2.log('splx96');
|
||||
console2.log(uint(params.sqrtPriceLimitX96));
|
||||
|
||||
console2.log('swapping...');
|
||||
amountOut = Constants.uniswapV3SwapRouter.exactInputSingle(ISwapRouter.ExactInputSingleParams({
|
||||
tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: params.recipient,
|
||||
deadline: block.timestamp, amountIn: params.amount, amountOutMinimum: 1, sqrtPriceLimitX96: params.sqrtPriceLimitX96
|
||||
deadline: block.timestamp, amountIn: amountIn, amountOutMinimum: 1, sqrtPriceLimitX96: params.sqrtPriceLimitX96
|
||||
}));
|
||||
console2.log('swapped');
|
||||
console2.log(amountOut);
|
||||
TransferHelper.safeApprove(params.tokenIn, address(Constants.uniswapV3SwapRouter), 0);
|
||||
}
|
||||
|
||||
function swapExactOutput(SwapParams memory params) internal returns (uint256 amountIn)
|
||||
function swapExactOutput(SwapParams memory params) internal returns (uint256 amountIn, uint256 amountOut)
|
||||
{
|
||||
// TODO copy changes over from swapExactInput
|
||||
|
||||
@@ -74,8 +81,7 @@ library UniswapSwapper {
|
||||
// uint256 amountInMaximum;
|
||||
// uint160 sqrtPriceLimitX96;
|
||||
// }
|
||||
address t = address(this);
|
||||
uint256 balance = IERC20(params.tokenIn).balanceOf(t);
|
||||
uint256 balance = IERC20(params.tokenIn).balanceOf(address(this));
|
||||
if( balance == 0 ) {
|
||||
// todo dust?
|
||||
revert('IIA');
|
||||
@@ -99,14 +105,33 @@ library UniswapSwapper {
|
||||
// if (params.sqrtPriceLimitX96 == 0)
|
||||
// params.sqrtPriceLimitX96 = params.tokenIn < params.tokenOut ? TickMath.MIN_SQRT_RATIO+1 : TickMath.MAX_SQRT_RATIO-1;
|
||||
|
||||
amountIn = Constants.uniswapV3SwapRouter.exactOutputSingle(ISwapRouter.ExactOutputSingleParams({
|
||||
console2.log('swapping...');
|
||||
try Constants.uniswapV3SwapRouter.exactOutputSingle(ISwapRouter.ExactOutputSingleParams({
|
||||
tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: params.recipient,
|
||||
deadline: block.timestamp, amountOut: params.amount, amountInMaximum: maxAmountIn,
|
||||
sqrtPriceLimitX96: params.sqrtPriceLimitX96
|
||||
}));
|
||||
})) returns (uint256 amtIn) {
|
||||
amountIn = amtIn;
|
||||
amountOut = params.amount;
|
||||
}
|
||||
catch Error( string memory reason ) {
|
||||
// todo check reason before trying exactinput
|
||||
// if the input amount was insufficient, use exactInputSingle to spend whatever remains.
|
||||
try Constants.uniswapV3SwapRouter.exactInputSingle(ISwapRouter.ExactInputSingleParams({
|
||||
tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: params.recipient,
|
||||
deadline: block.timestamp, amountIn: maxAmountIn, amountOutMinimum: 1, sqrtPriceLimitX96: params.sqrtPriceLimitX96
|
||||
})) returns (uint256 amtOut) {
|
||||
amountIn = maxAmountIn;
|
||||
amountOut = amtOut;
|
||||
}
|
||||
catch Error( string memory ) {
|
||||
revert(reason); // revert on the original reason
|
||||
}
|
||||
}
|
||||
|
||||
console2.log('swapped');
|
||||
console2.log(amountIn);
|
||||
console2.log(amountOut);
|
||||
|
||||
TransferHelper.safeApprove(params.tokenIn, address(Constants.uniswapV3SwapRouter), 0);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ library VaultAddress {
|
||||
// keccak-256 hash of the Vault's bytecode (not the deployed bytecode but the initialization bytecode)
|
||||
// can paste into:
|
||||
// https://emn178.github.io/online-tools/keccak_256.html
|
||||
bytes32 public constant VAULT_INIT_CODE_HASH = 0x5548eccbb8c4c38711944ab3e79a6f320ae2eb4a6bb16c64215f13e7732f182c;
|
||||
bytes32 public constant VAULT_INIT_CODE_HASH = 0xe8ec1ead51d2900d63e5e0aa245485e9452d619fceb9ab914ae78cdb11c4ab70;
|
||||
|
||||
// the contract being constructed must not have any constructor arguments or the determinism will be broken. instead, use a callback to
|
||||
// get construction arguments
|
||||
|
||||
@@ -63,7 +63,6 @@ contract TestOrder is MockEnv, Test {
|
||||
vault.placeOrder(order);
|
||||
console2.log('placed order');
|
||||
console2.log(uint(orderIndex));
|
||||
string memory result;
|
||||
vault.execute(orderIndex, 0, OrderLib.PriceProof(0));
|
||||
console2.log('executed');
|
||||
}
|
||||
@@ -85,7 +84,6 @@ contract TestOrder is MockEnv, Test {
|
||||
vault.placeOrder(order);
|
||||
console2.log('placed order');
|
||||
console2.log(uint(orderIndex));
|
||||
string memory result;
|
||||
vault.execute(orderIndex, 0, OrderLib.PriceProof(0));
|
||||
console2.log('executed');
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ contract TestVault is Test {
|
||||
console2.log(address(vault));
|
||||
}
|
||||
|
||||
function testDeterministicAddress() public {
|
||||
function testDeterministicAddress() public view {
|
||||
console2.log(address(vault));
|
||||
address d = VaultAddress.computeAddress(address(factory), address(this));
|
||||
console2.log(d);
|
||||
|
||||
Reference in New Issue
Block a user