TRANCHE EXECUTION WORKS

This commit is contained in:
Tim Olson
2023-10-29 16:53:07 -04:00
parent e1eecad898
commit fd18cba58f
8 changed files with 124 additions and 78 deletions

View File

@@ -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. |
| 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. |

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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');
}

View File

@@ -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);