Tim's hardhat + Juan's solidity 0.8

This commit is contained in:
7400
2023-10-26 15:07:38 -07:00
10 changed files with 119 additions and 67 deletions

View File

@@ -9,7 +9,7 @@ forge build "$@" || exit 1
VAULT_INIT_CODE_HASH=$(cast keccak $(jq -r .bytecode.object < out/Vault.sol/Vault.json))
# put the hash value into the VaultAddress.sol source file
sed -i "s/bytes32 internal constant VAULT_INIT_CODE_HASH = .*;/bytes32 internal constant VAULT_INIT_CODE_HASH = $VAULT_INIT_CODE_HASH;/" src/VaultAddress.sol
sed -i "s/VAULT_INIT_CODE_HASH = .*;/VAULT_INIT_CODE_HASH = $VAULT_INIT_CODE_HASH;/" src/VaultAddress.sol
# generate a javascript file with the constant
mkdir gen &> /dev/null

View File

@@ -6,7 +6,7 @@
#cd ../contract
#./bin/build.sh
anvil -f arbitrum_mock --chain-id 1338 &
anvil -f arbitrum_mock --chain-id 31337 &
# todo check anvil result
ANVIL_PID=$!
sleep 2

View File

@@ -13,7 +13,8 @@ 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))}();
// Factory deployer = new Factory{salt:keccak256(abi.encode(1))}(); // version 1
Factory deployer = new Factory(); // hardhat often breaks on the CREATE2 above :(
QueryHelper query = new QueryHelper();
Dexorder dexorder = new Dexorder();
MockEnv mock = new MockEnv();

View File

@@ -9,7 +9,6 @@ pragma abicoder v2;
contract Factory is VaultDeployer {
address public admin;
constructor() {
admin = msg.sender;
}

View File

@@ -192,69 +192,80 @@ 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 tranche_index, PriceProof memory proof) internal {
function execute(OrdersInfo storage self, address owner, uint64 orderIndex, uint8 trancheIndex, PriceProof memory proof) internal {
console2.log('execute');
console2.log(address(this));
console2.log(uint(orderIndex));
console2.log(uint(trancheIndex));
SwapOrderStatus storage status = self.orders[orderIndex];
if (status.state != SwapOrderState.Open)
revert('NO'); // Not Open
Tranche storage tranche = status.order.tranches[tranche_index];
Tranche storage tranche = status.order.tranches[trancheIndex];
uint160 sqrtPriceX96 = 0;
uint160 sqrtPriceLimitX96 = 0;
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');
}
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) {
// 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
// }
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
- (status.order.amountIsInput ? status.trancheFilledIn[trancheIndex] : status.trancheFilledOut[trancheIndex]); // minus tranche fills
// order amount remaining
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;
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[tranche_index] += amountIn;
status.trancheFilledOut[tranche_index] += amountOut;
emit DexorderSwapFilled(orderIndex, tranche_index, 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);
}
@@ -262,9 +273,8 @@ library OrderLib {
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;
console2.log('price limit');
console2.log(uint(sqrtPriceLimitX96));
if (order.amountIsInput) {
amountIn = amount;
amountOut = UniswapSwapper.swapExactInput(UniswapSwapper.SwapParams(

View File

@@ -6,6 +6,7 @@ pragma abicoder v2;
import "./Constants.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "v3-periphery/libraries/TransferHelper.sol";
import "v3-core/contracts/libraries/TickMath.sol";
import "forge-std/console2.sol";
@@ -34,12 +35,20 @@ library UniswapSwapper {
// uint160 sqrtPriceLimitX96;
// }
console2.log('swapExactInput approve...');
TransferHelper.safeApprove(params.tokenIn, address(Constants.uniswapV3SwapRouter), params.amount);
console2.log(address(this));
console2.log(params.tokenIn);
console2.log(params.tokenOut);
console2.log(uint(params.fee));
console2.log(address(Constants.uniswapV3SwapRouter));
console2.log(address(params.recipient));
console2.log(params.amount);
console2.log(uint(params.sqrtPriceLimitX96));
console2.log(address(Constants.uniswapV3SwapRouter));
if (params.sqrtPriceLimitX96 == 0)
params.sqrtPriceLimitX96 = params.tokenIn < params.tokenOut ? TickMath.MIN_SQRT_RATIO+1 : TickMath.MAX_SQRT_RATIO-1;
TransferHelper.safeApprove(params.tokenIn, address(Constants.uniswapV3SwapRouter), params.amount);
console2.log('splx96');
console2.log(uint(params.sqrtPriceLimitX96));
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: 0, sqrtPriceLimitX96: params.sqrtPriceLimitX96
@@ -51,6 +60,8 @@ library UniswapSwapper {
function swapExactOutput(SwapParams memory params) internal returns (uint256 amountIn)
{
// TODO copy changes over from swapExactInput
// struct ExactOutputSingleParams {
// address tokenIn;
// address tokenOut;

View File

@@ -17,7 +17,7 @@ contract Vault {
uint8 public immutable version;
address public immutable owner;
OrderLib.OrdersInfo public orderList;
OrderLib.OrdersInfo public ordersInfo;
constructor()
{
@@ -55,23 +55,26 @@ contract Vault {
token.transfer(recipient, amount);
}
function numSwapOrders() external view returns (uint64 num) {
return uint64(ordersInfo.orders.length);
}
function placeOrder(OrderLib.SwapOrder memory order) public onlyOwner {
console2.log('Vault.placeOrder()');
orderList._placeOrder(order);
ordersInfo._placeOrder(order);
}
function placeOrders(OrderLib.SwapOrder[] memory orders, OrderLib.OcoMode ocoMode) public onlyOwner {
orderList._placeOrders(orders, ocoMode);
ordersInfo._placeOrders(orders, ocoMode);
}
function swapOrderStatus(uint64 orderIndex) public view returns (OrderLib.SwapOrderStatus memory status) {
return orderList.orders[orderIndex];
function swapOrderStatus(uint64 orderIndex) external view returns (OrderLib.SwapOrderStatus memory status) {
return ordersInfo.orders[orderIndex];
}
function execute(uint64 orderIndex, uint8 tranche_index, OrderLib.PriceProof memory proof) public
{
orderList.execute(owner, orderIndex, tranche_index, proof);
ordersInfo.execute(owner, orderIndex, tranche_index, proof);
}
modifier onlyOwner() {

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 internal constant VAULT_INIT_CODE_HASH = 0x060e1878e07cde3b1fe03b6a8929c62c502510ed5dda30b278951f11dfc03490;
bytes32 public constant VAULT_INIT_CODE_HASH = 0xaa3457854b70ea8d66f3b73269f0cf34c7e2212e4b4bd8176e8388ff223a2bd0;
// 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

@@ -30,9 +30,13 @@ contract MockEnv {
// the initial price is 1.000000, but since COIN has 18 decimals and USD only has 6, the raw pool price is 1e-12
// therefore the sqrt price is 1e-6
// 1000e12 liquidity is put into the pool at each tick spacing for 10 tick spacings to either side of $1
function init() internal {
function init() public {
COIN = new MockERC20('Mock Coin', 'MOCK', 18);
USD = new MockERC20('Universally Supported Dollars', 'USD', 6);
console2.log('COIN');
console2.log(address(COIN));
USD = new MockERC20('Universally Stable Denomination', 'USD', 6);
console2.log('USD');
console2.log(address(USD));
fee = 500;
inverted = address(COIN) > address(USD);
token0 = inverted ? address(USD) : address(COIN);
@@ -41,7 +45,8 @@ contract MockEnv {
console2.log('if this is the last line before a revert then make sure to run forge with --rpc-url');
// if this reverts here make sure Anvil is started and you are running forge with --rpc-url
pool = IUniswapV3Pool(nfpm.createAndInitializePoolIfNecessary(token0, token1, fee, initialPrice));
console2.log('created v3 pool successfully');
console2.log('v3 pool');
console2.log(address(pool));
int24 ts = pool.tickSpacing();
(, int24 lower, , , , ,) = pool.slot0();
int24 upper = lower;

View File

@@ -10,6 +10,8 @@ import "../src/Factory.sol";
import "../src/OrderLib.sol";
contract TestOrder is MockEnv, Test {
using OrderLib for OrderLib.OrdersInfo;
Factory public factory;
Vault public vault;
@@ -25,7 +27,7 @@ contract TestOrder is MockEnv, Test {
}
function testOrder() public {
function testPlaceOrder() public {
OrderLib.Tranche[] memory tranches = new OrderLib.Tranche[](3);
OrderLib.Constraint[] memory constraints1 = new OrderLib.Constraint[](1);
constraints1[0] = OrderLib.Constraint(OrderLib.ConstraintMode.Time, bytes(hex"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000046500"));
@@ -45,4 +47,25 @@ contract TestOrder is MockEnv, Test {
vault.placeOrder(order);
}
function testExecuteOrder() public {
OrderLib.Tranche[] memory tranches = new OrderLib.Tranche[](1);
OrderLib.Constraint[] memory constraints1 = new OrderLib.Constraint[](1);
constraints1[0] = OrderLib.Constraint(OrderLib.ConstraintMode.Time, bytes(hex"0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000046500"));
tranches[0] = OrderLib.Tranche(type(uint16).max,constraints1);
uint256 amount = 3*10**COIN.decimals() / 10; // 0.3 COIN
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.NO_CHAIN, tranches
);
uint64 orderIndex = vault.numSwapOrders();
vault.placeOrder(order);
console2.log('placed order');
console2.log(uint(orderIndex));
string memory result;
vault.execute(orderIndex, 0, OrderLib.PriceProof(0));
console2.log('executed');
}
}