vault deployment, query helper, bug fixes

This commit is contained in:
Tim Olson
2023-10-06 19:48:39 -04:00
parent 2925a1cc0c
commit 52069cfe0b
14 changed files with 215 additions and 36 deletions

View File

@@ -5,6 +5,12 @@ import "v3-core/contracts/UniswapV3Factory.sol";
import "./VaultDeployer.sol";
pragma abicoder v2;
contract Factory is VaultDeployer {
// todo owner
address public admin;
constructor() {
admin = msg.sender;
}
}

View File

@@ -15,18 +15,29 @@ library OrderLib {
event DexorderSwapFilled (uint64 orderIndex, uint8 trancheIndex, uint256 amountIn, uint256 amountOut);
event DexorderCompleted (uint64 orderIndex);
event DexorderCompleted (uint64 orderIndex); // todo remove?
event DexorderError (uint64 orderIndex, string reason);
// todo If a tranche fails to fill, an order can stay Open forever without any active tranches. maybe replace state with a simple canceled flag
enum SwapOrderState {
Open, Canceled, Filled, Template
}
enum Exchange {
UniswapV2,
UniswapV3
}
struct Route {
Exchange exchange;
uint24 fee;
}
struct SwapOrder {
address tokenIn;
address tokenOut;
uint24 fee;
Route route;
uint256 amount;
bool amountIsInput;
bool outputDirectlyToOwner;
@@ -138,6 +149,8 @@ library OrderLib {
revert('OCOM');
for( uint8 o = 0; o < orders.length; o++ ) {
SwapOrder memory order = orders[o];
require(order.route.exchange == Exchange.UniswapV3, 'UR');
// todo more order validation
// we must explicitly copy into storage because Solidity doesn't implement copying the double-nested
// tranches constraints array :(
uint orderIndex = self.orders.length;
@@ -147,7 +160,7 @@ library OrderLib {
status.order.amountIsInput = order.amountIsInput;
status.order.tokenIn = order.tokenIn;
status.order.tokenOut = order.tokenOut;
status.order.fee = order.fee;
status.order.route = order.route;
status.order.chainOrder = order.chainOrder;
status.order.outputDirectlyToOwner = order.outputDirectlyToOwner;
for( uint t=0; t<order.tranches.length; t++ ) {
@@ -185,7 +198,8 @@ library OrderLib {
Tranche storage tranche = status.order.tranches[tranche_index];
uint160 sqrtPriceX96 = 0;
uint160 sqrtPriceLimitX96 = 0;
address pool = Constants.uniswapV3Factory.getPool(status.order.tokenIn, status.order.tokenOut, status.order.fee);
// 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) {
@@ -227,7 +241,11 @@ library OrderLib {
amount = remaining;
uint256 amountIn;
uint256 amountOut;
(error, amountIn, amountOut) = _do_execute_univ3(status.order, pool, amount, sqrtPriceLimitX96);
if( status.order.route.exchange == Exchange.UniswapV3 )
(error, amountIn, amountOut) = _do_execute_univ3(status.order, pool, amount, sqrtPriceLimitX96);
// todo other routes
else
error = 'UR'; // unknown route
if( bytes(error).length == 0 ) {
status.filledIn += amountIn;
status.filledOut += amountOut;
@@ -248,12 +266,12 @@ library OrderLib {
if (order.amountIsInput) {
amountIn = amount;
(error, amountOut) = UniswapSwapper.swapExactInput(UniswapSwapper.SwapParams(
pool, order.tokenIn, order.tokenOut, order.fee, amount, sqrtPriceLimitX96));
pool, order.tokenIn, order.tokenOut, order.route.fee, amount, sqrtPriceLimitX96));
}
else {
amountOut = amount;
(error, amountIn) = UniswapSwapper.swapExactOutput(UniswapSwapper.SwapParams(
pool, order.tokenIn, order.tokenOut, order.fee, amount, sqrtPriceLimitX96));
pool, order.tokenIn, order.tokenOut, order.route.fee, amount, sqrtPriceLimitX96));
}
}

86
src/QueryHelper.sol Normal file
View File

@@ -0,0 +1,86 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity =0.7.6;
pragma abicoder v2;
import "v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./OrderLib.sol";
import "./Vault.sol";
import "./VaultDeployer.sol";
import "./Factory.sol";
contract QueryHelper {
uint8 constant public version = 1;
uint8 constant public UNKNOWN_DECIMALS = type(uint8).max;
function getBalances( address vault, address[] memory tokens ) public view
returns (
uint256[] memory balances,
uint256[] memory decimals
) {
require(tokens.length < type(uint16).max);
balances = new uint256[](tokens.length);
decimals = new uint256[](tokens.length);
for( uint16 i=0; i < tokens.length; i++ ) {
try IERC20(tokens[i]).balanceOf(vault) returns (uint256 balance) {
balances[i] = balance;
}
catch {
balances[i] = 0;
}
try ERC20(tokens[i]).decimals() returns (uint8 dec) {
decimals[i] = dec;
}
catch {
decimals[i] = UNKNOWN_DECIMALS;
}
}
}
struct RoutesResult {
OrderLib.Exchange exchange;
uint24 fee;
address pool;
}
function getRoutes( address tokenA, address tokenB ) public view
returns(RoutesResult[] memory routes) {
// todo discover all supported pools
// 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);
uint24 uniswapV3Fee = 0;
uint128 uniswapV3Liquidity = 0;
address uniswapV3Pool = address(0);
for( uint8 f=0; f<4; f++ ) {
IUniswapV3Pool pool = IUniswapV3Pool(Constants.uniswapV3Factory.getPool(tokenA, tokenB, fees[f]));
try pool.liquidity() returns (uint128 liquidity) {
// todo v2
if( liquidity > uniswapV3Liquidity ) {
uniswapV3Fee = fees[f];
uniswapV3Liquidity = liquidity;
uniswapV3Pool = address(pool);
}
}
catch {
}
}
uint8 routesCount = uniswapV3Fee > 0 ? 1 : 0 + uniswapV2Fee > 0 ? 1 : 0;
routes = new QueryHelper.RoutesResult[](routesCount);
uint8 i = 0;
// todo v2
if( uniswapV3Fee > 0 )
routes[i++] = QueryHelper.RoutesResult(OrderLib.Exchange.UniswapV3, uniswapV3Fee, uniswapV3Pool);
}
function poolStatus(IUniswapV3Pool pool) public view
returns (
int24 tick,
uint128 liquidity
) {
(, tick,,,,,) = pool.slot0();
liquidity = pool.liquidity();
}
}

View File

@@ -9,20 +9,24 @@ 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 = 0xbf043f7035d5aa3be2b3c94df5b256fbe24675689327af4ab71c48194c463031;
bytes32 internal constant VAULT_INIT_CODE_HASH = 0xdc7f6d1305b2c9951dc98f6a745290e4f2b92bf65d346db11dd284dcfcd67aef;
// the contract being constructed must not have any constructor arguments or the determinism will be broken. instead, use a callback to
// get construction arguments
// Uniswap example
// https://github.com/Uniswap/v3-periphery/blob/6cce88e63e176af1ddb6cc56e029110289622317/contracts/libraries/PoolAddress.sol#L33C5-L47C6
function computeAddress(address factory, address owner) internal pure returns (address vault) {
return computeAddress(factory, owner, 0);
}
function computeAddress(address factory, address owner, uint8 num) internal pure returns (address vault) {
vault = address(
uint256(
keccak256(
abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encode(owner)),
keccak256(abi.encode(owner,num)),
VAULT_INIT_CODE_HASH
)
)

View File

@@ -10,14 +10,31 @@ contract VaultDeployer {
address owner;
}
event VaultCreated( address deployer, address owner );
event VaultCreated( address indexed owner, uint8 num );
Parameters public parameters;
function deployVault(address owner) public returns (address payable vault) {
parameters = Parameters(owner);
vault = address(new Vault{salt: keccak256(abi.encode(owner))}());
delete parameters;
emit VaultCreated( address(this), owner );
function deployVault() public returns (address payable vault) {
return _deployVault(msg.sender, 0);
}
function deployVault(uint8 num) public returns (address payable vault) {
return _deployVault(msg.sender, num);
}
function deployVault(address owner) public returns (address payable vault) {
return _deployVault(owner, 0);
}
function deployVault(address owner, uint8 num) public returns (address payable vault) {
return _deployVault(owner, num);
}
function _deployVault(address owner, uint8 num) internal returns (address payable vault) {
parameters = Parameters(owner);
vault = address(new Vault{salt: keccak256(abi.encode(owner,num))}());
delete parameters;
emit VaultCreated( owner, num );
}
}