diff --git a/script/Deploy.sol b/script/Deploy.sol index 35f3495..d3014ae 100644 --- a/script/Deploy.sol +++ b/script/Deploy.sol @@ -5,6 +5,7 @@ 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 Deploy is Script { @@ -13,12 +14,15 @@ contract Deploy is Script { vm.startBroadcast(deployerPrivateKey); Factory deployer = new Factory{salt:keccak256(abi.encode(1))}(); QueryHelper query = new QueryHelper(); + Dexorder dexorder = new Dexorder(); MockEnv mock = new MockEnv(); vm.stopBroadcast(); console2.log('VaultDeployer'); console2.log(address(deployer)); console2.log('QueryHelper'); console2.log(address(query)); + console2.log('Dexorder'); + console2.log(address(dexorder)); console2.log('MockEnv'); // todo no mock in production deployment console2.log(address(mock)); } diff --git a/src/Constants.sol b/src/Constants.sol index 2de4a06..2af5841 100644 --- a/src/Constants.sol +++ b/src/Constants.sol @@ -17,6 +17,6 @@ library Constants { // Swap statuses - string internal constant SWAP_OK = ''; // fastest comparison + bytes32 internal constant SWAP_OK = ''; // fastest comparison // other errors may be passed through from Uniswap } diff --git a/src/Dexorder.sol b/src/Dexorder.sol new file mode 100644 index 0000000..ec32236 --- /dev/null +++ b/src/Dexorder.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity =0.7.6; +import "./OrderLib.sol"; +import "./Vault.sol"; +pragma abicoder v2; + +contract Dexorder { + // represents the Dexorder organization + + event DexorderExecutions(uint128 indexed id, bytes[] errors); + + struct ExecutionRequest { + address payable vault; + uint64 orderIndex; + uint8 trancheIndex; + OrderLib.PriceProof proof; + } + + + function execute( uint128 id, ExecutionRequest memory req ) public returns (bytes memory error) { + error = _execute(req); + bytes[] memory errors = new bytes[](1); + errors[0] = error; + emit DexorderExecutions(id, errors); + } + + + function execute( uint128 id, ExecutionRequest[] memory reqs ) public returns (bytes[] memory errors) { + errors = new bytes[](reqs.length); + for( uint8 i=0; i block.timestamp) - return 'TE'; // time early + revert('TE'); // time early time = tc.latest.mode == TimeMode.Timestamp ? tc.latest.time : status.start + tc.latest.time; if (time < block.timestamp) - return 'TL'; // time late + revert('TL'); // time late } else if (constraint.mode == ConstraintMode.Limit) { if( sqrtPriceX96 == 0 ) { @@ -233,20 +220,19 @@ library OrderLib { 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 ) - return 'L'; + revert('L'); } else if (constraint.mode == ConstraintMode.Barrier) { - return 'NI'; // not implemented + revert('NI'); // not implemented } else if (constraint.mode == ConstraintMode.Trailing) { - return 'NI'; // not implemented + revert('NI'); // not implemented } else if (constraint.mode == ConstraintMode.Line) { - return 'NI'; // not implemented + revert('NI'); // not implemented } - else - return 'NI'; // not implemented - // unknown constraint + else // unknown constraint + revert('NI'); // not implemented } uint256 amount = status.order.amount * tranche.fraction / type(uint16).max // the most this tranche could do - (status.order.amountIsInput ? status.trancheFilledIn[tranche_index] : status.trancheFilledOut[tranche_index]); // minus tranche fills @@ -254,42 +240,39 @@ library OrderLib { uint256 remaining = status.order.amount - (status.order.amountIsInput ? status.filledIn : status.filledOut); if (amount > remaining) // not more than the order's overall remaining amount amount = remaining; + address recipient = status.order.outputDirectlyToOwner ? owner : address(this); uint256 amountIn; uint256 amountOut; if( status.order.route.exchange == Exchange.UniswapV3 ) - (error, amountIn, amountOut) = _do_execute_univ3(status.order, pool, amount, sqrtPriceLimitX96); + (amountIn, amountOut) = _do_execute_univ3(recipient, status.order, pool, amount, sqrtPriceLimitX96); // todo other routes else - return 'UR'; // unknown route - if( bytes(error).length == 0 ) { - status.filledIn += amountIn; - status.filledOut += amountOut; - status.trancheFilledIn[tranche_index] += amountIn; - status.trancheFilledOut[tranche_index] += amountOut; - emit DexorderSwapFilled(orderIndex, tranche_index, amountIn, amountOut); - _checkCompleted(self, orderIndex, status); - } - return ''; // success is no error, said no one + revert('UR'); // unknown route + status.filledIn += amountIn; + status.filledOut += amountOut; + status.trancheFilledIn[tranche_index] += amountIn; + status.trancheFilledOut[tranche_index] += amountOut; + emit DexorderSwapFilled(orderIndex, tranche_index, amountIn, amountOut); + _checkCompleted(self, orderIndex, status); } - function _do_execute_univ3( SwapOrder storage order, address pool, uint256 amount, uint160 sqrtPriceLimitX96) private - returns (string memory error, uint256 amountIn, uint256 amountOut) + function _do_execute_univ3( address recipient, SwapOrder storage order, address pool, uint256 amount, uint160 sqrtPriceLimitX96) private + returns (uint256 amountIn, uint256 amountOut) { // todo refactor this signature to be more low-level, taking only the in/out amounts and limit prices. doesnt need self/status/index if (sqrtPriceLimitX96 == 0) // check pool inversion to see if the price should be high or low sqrtPriceLimitX96 = order.tokenIn < order.tokenOut ? 0 : type(uint160).max; - // todo swap direct to owner if (order.amountIsInput) { amountIn = amount; - (error, amountOut) = UniswapSwapper.swapExactInput(UniswapSwapper.SwapParams( - pool, order.tokenIn, order.tokenOut, order.route.fee, amount, sqrtPriceLimitX96)); + amountOut = UniswapSwapper.swapExactInput(UniswapSwapper.SwapParams( + pool, order.tokenIn, order.tokenOut, recipient, order.route.fee, amount, sqrtPriceLimitX96)); } else { amountOut = amount; - (error, amountIn) = UniswapSwapper.swapExactOutput(UniswapSwapper.SwapParams( - pool, order.tokenIn, order.tokenOut, order.route.fee, amount, sqrtPriceLimitX96)); + amountIn = UniswapSwapper.swapExactOutput(UniswapSwapper.SwapParams( + pool, order.tokenIn, order.tokenOut, recipient, order.route.fee, amount, sqrtPriceLimitX96)); } } diff --git a/src/UniswapSwapper.sol b/src/UniswapSwapper.sol index 37acf3a..934c407 100644 --- a/src/UniswapSwapper.sol +++ b/src/UniswapSwapper.sol @@ -12,12 +12,13 @@ library UniswapSwapper { address pool; address tokenIn; address tokenOut; + address recipient; uint24 fee; uint256 amount; uint160 sqrtPriceLimitX96; } - function swapExactInput(SwapParams memory params) internal returns (string memory error, uint256 amountOut) + function swapExactInput(SwapParams memory params) internal returns (uint256 amountOut) { // struct ExactInputSingleParams { // address tokenIn; @@ -29,22 +30,13 @@ library UniswapSwapper { // uint256 amountOutMinimum; // uint160 sqrtPriceLimitX96; // } - - try Constants.uniswapV3SwapRouter.exactInputSingle(ISwapRouter.ExactInputSingleParams({ - tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: address(this), // todo return directly to wallet? + return Constants.uniswapV3SwapRouter.exactInputSingle(ISwapRouter.ExactInputSingleParams({ + tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: params.recipient, deadline: block.timestamp, amountIn: params.amount, amountOutMinimum: 0, sqrtPriceLimitX96: params.sqrtPriceLimitX96 - })) returns (uint256 filledOut) { - amountOut = filledOut; - error = Constants.SWAP_OK; - } - catch Error(string memory reason) { - amountOut = 0; - error = reason; - } + })); } - - function swapExactOutput(SwapParams memory params) internal returns (string memory error, uint256 amountIn) + function swapExactOutput(SwapParams memory params) internal returns (uint256 amountIn) { // struct ExactOutputSingleParams { // address tokenIn; @@ -60,20 +52,13 @@ library UniswapSwapper { uint256 balance = IERC20(params.tokenIn).balanceOf(t); if( balance == 0 ) { // todo dust? - return ('IIA', 0); + revert('IIA'); } - try Constants.uniswapV3SwapRouter.exactOutputSingle(ISwapRouter.ExactOutputSingleParams({ - tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: t, // todo return directly to wallet? + return Constants.uniswapV3SwapRouter.exactOutputSingle(ISwapRouter.ExactOutputSingleParams({ + tokenIn: params.tokenIn, tokenOut: params.tokenOut, fee: params.fee, recipient: params.recipient, deadline: block.timestamp, amountOut: params.amount, amountInMaximum: balance, // todo use only the committed allocation? sqrtPriceLimitX96: params.sqrtPriceLimitX96 - })) returns (uint256 filledIn) { - amountIn = filledIn; - error = Constants.SWAP_OK; - } - catch Error(string memory reason) { - amountIn = 0; - error = reason; - } + })); } } diff --git a/src/Vault.sol b/src/Vault.sol index 613962f..d49aa58 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -10,6 +10,8 @@ import "forge-std/console2.sol"; contract Vault { + // represents the interests of its owner client + using OrderLib for OrderLib.OrdersInfo; uint8 public immutable version; @@ -67,9 +69,8 @@ contract Vault { } function execute(uint64 orderIndex, uint8 tranche_index, OrderLib.PriceProof memory proof) public - returns (string memory error) { - return orderList.execute(orderIndex, tranche_index, proof); + orderList.execute(owner, orderIndex, tranche_index, proof); } modifier onlyOwner() { diff --git a/src/VaultAddress.sol b/src/VaultAddress.sol index 18cc930..2591884 100644 --- a/src/VaultAddress.sol +++ b/src/VaultAddress.sol @@ -10,7 +10,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 internal constant VAULT_INIT_CODE_HASH = 0x3c95480d723e6cf3e130cfb53bfa2f15bd0bfc628246f77dfabe64ff943af969; + bytes32 internal constant VAULT_INIT_CODE_HASH = 0x94dd1e2fe4a67770f9295aff1b352fb0792647d2377cd96aa72e4c3a3222aad1; // the contract being constructed must not have any constructor arguments or the determinism will be broken. instead, use a callback to // get construction arguments