Skip to content

Commit

Permalink
remove swaps from queue after execution
Browse files Browse the repository at this point in the history
  • Loading branch information
pysel committed Nov 25, 2024
1 parent fec4fa3 commit 7aa02a2
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 25 deletions.
2 changes: 1 addition & 1 deletion lib/v4-periphery
Submodule v4-periphery updated 169 files
31 changes: 20 additions & 11 deletions src/ClvrHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ClvrIntentPool } from "./ClvrIntentPool.sol";
import { ClvrModel } from "./ClvrModel.sol";
import {PoolSwapTest} from "@uniswap/v4-core/src/test/PoolSwapTest.sol";
// import {console} from "forge-std/console.sol";
import {console} from "forge-std/console.sol";


contract ClvrHook is BaseHook {
Expand All @@ -31,6 +31,10 @@ contract ClvrHook is BaseHook {
using TransientStateLibrary for IPoolManager;
using CurrencySettler for Currency;

event BatchCompleted(PoolId poolId);

event SwapScheduled(PoolId poolId, address sender);

struct SwapParamsExtended {
address sender;
address recepient;
Expand Down Expand Up @@ -86,7 +90,6 @@ contract ClvrHook is BaseHook {
"Clvr Pools only work with exact input swaps"
);

PoolId poolId = PoolIdLibrary.toId(key);
address recepient = abi.decode(data, (address));

if (recepient == BATCH) {
Expand All @@ -99,6 +102,8 @@ contract ClvrHook is BaseHook {
);
}

PoolId poolId = PoolIdLibrary.toId(key);

// the hook takes the exact amount of the input currency to itself
Currency input = params.zeroForOne ? key.currency0 : key.currency1;
uint256 amountTaken = uint256(-params.amountSpecified);
Expand All @@ -113,6 +118,8 @@ contract ClvrHook is BaseHook {
// Store the swap params
swapParams[poolId].push(paramsE);

emit SwapScheduled(poolId, sender);

return (
BaseHook.beforeSwap.selector,
toBeforeSwapDelta(amountTaken.toInt128(), 0),
Expand All @@ -139,22 +146,16 @@ contract ClvrHook is BaseHook {
params = model.clvrReorder(
currentPrice,
params,
currentPrice * 1e18,
1e18 / currentPrice
currentPrice,
1e18
); // currentPrice hack to simulate token amounts

for (uint256 i = 1; i < params.length; i++) {
// PoolSwapTest.TestSettings memory testSettings =
// PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false});
// swapRouter.swap(key, params[i].params, testSettings, abi.encode(BATCH));
for (uint256 i = 0; i < params.length; ) {
poolManager.swap(key, params[i].params, abi.encode(BATCH));

int256 delta0 = poolManager.currencyDelta(address(this), key.currency0);
int256 delta1 = poolManager.currencyDelta(address(this), key.currency1);

// console.log(delta0);
// console.log(delta1);

if (delta0 < 0) {
key.currency0.settle(poolManager, address(this), uint256(-delta0), true);
}
Expand All @@ -170,8 +171,16 @@ contract ClvrHook is BaseHook {
if (delta1 > 0) {
key.currency1.take(poolManager, params[i].recepient, uint256(delta1), false);
}

unchecked {
i++;
}
}

delete swapParams[poolId];

emit BatchCompleted(poolId);

return BaseHook.beforeDonate.selector;
}

Expand Down
25 changes: 18 additions & 7 deletions src/ClvrModel.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ClvrHook } from "./ClvrHook.sol";
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";


// import { console } from "forge-std/console.sol";
import { console } from "forge-std/console.sol";

contract ClvrModel {
using ClvrLn for uint256;
Expand Down Expand Up @@ -37,19 +37,16 @@ contract ClvrModel {
) public returns (ClvrHook.SwapParamsExtended[] memory) {
o = addMockTrade(o);

// console.log("ENTERED FUNCTION");
set_reserve_x(reserve_x);
set_reserve_y(reserve_y);

int128 lnP0 = p0.lnU256().toInt128();
for (uint256 i = 1; i < o.length; i++) {
// console.log("ENTERED LOOP");
for (uint256 i = 1; i < o.length; ) {
uint256 candidateIndex = i;
int128 unsquaredCandidateValue = lnP0 - P(o, i).lnU256().toInt128();
int256 candidateValue = unsquaredCandidateValue ** 2 / 1e18;

for (uint256 j = i + 1; j < o.length; j++) {
// console.log("ENTERED INNER LOOP");
for (uint256 j = i + 1; j < o.length; ) {
swap(o, i, j);

int256 unsquaredValue = lnP0 - P(o, i).lnU256().toInt128();
Expand All @@ -61,14 +58,28 @@ contract ClvrModel {
}

swap(o, j, i);

unchecked {
j++;
}
}

if (candidateIndex != i) {
swap(o, i, candidateIndex);
}

unchecked {
i++;
}
}

// TODO: there must be a better way to do this
ClvrHook.SwapParamsExtended[] memory result = new ClvrHook.SwapParamsExtended[](o.length - 1);
for (uint256 i = 1; i < o.length; i++) {
result[i - 1] = o[i];
}

return o;
return result;
}

// adds a mock trade as a first entry of th array
Expand Down
97 changes: 92 additions & 5 deletions test/ClvrHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ contract ClvrHookTest is Test, Deployers, Fixtures {
using EasyPosm for IPositionManager;
using StateLibrary for IPoolManager;

bool DEBUG = true;

PoolId poolId;
ClvrHook hook;

address[5] users;
address[10] users;

uint256 tokenId;
int24 tickLower;
Expand Down Expand Up @@ -101,6 +103,11 @@ contract ClvrHookTest is Test, Deployers, Fixtures {
users[2] = makeAddr("Carol");
users[3] = makeAddr("3");
users[4] = makeAddr("4");
users[5] = makeAddr("5");
users[6] = makeAddr("6");
users[7] = makeAddr("7");
users[8] = makeAddr("8");
users[9] = makeAddr("9");
}

function dealCurrencyToUsers() internal {
Expand All @@ -111,7 +118,7 @@ contract ClvrHookTest is Test, Deployers, Fixtures {
}

function approveSwapRouter() internal {
for (uint256 i = 0; i < 5; i++) {
for (uint256 i = 0; i < users.length; i++) {
vm.startPrank(users[i]);
ERC20(Currency.unwrap(currency0)).approve(address(swapRouter), type(uint256).max);
ERC20(Currency.unwrap(currency1)).approve(address(swapRouter), type(uint256).max);
Expand All @@ -120,7 +127,7 @@ contract ClvrHookTest is Test, Deployers, Fixtures {
}

function approveHook() internal {
for (uint256 i = 0; i < 5; i++) {
for (uint256 i = 0; i < users.length; i++) {
vm.startPrank(users[i]);
ERC20(Currency.unwrap(currency0)).approve(address(hook), type(uint256).max);
ERC20(Currency.unwrap(currency1)).approve(address(hook), type(uint256).max);
Expand Down Expand Up @@ -149,8 +156,88 @@ contract ClvrHookTest is Test, Deployers, Fixtures {
require(currency0.balanceOf(sender) + uint256(-amount) == c0balance, "Currency0 balance should be decreased by amount");
require(currency1.balanceOf(sender) == c1balance, "Currency1 balance should not be changed");

donateRouter.donate(key, 0, 0, "");
executeBatch();

require(currency1.balanceOf(sender) > c1balance, "Swap should have increased currency1 balance");
}
}

function testHookMultipleSwaps() public {
dealCurrencyToUsers();
approveSwapRouter();
approveHook();

oneBatch();
}

function testHookMultipleBatches() public {
dealCurrencyToUsers();
approveSwapRouter();
approveHook();

uint256 batches = 10;

for (uint256 i = 0; i < batches; i++) {
oneBatch();
}
}

function oneBatch() internal {
uint256[] memory c0balances = new uint256[](users.length);
uint256[] memory c1balances = new uint256[](users.length);

for (uint256 i = 0; i < users.length; i++) {
c0balances[i] = currency0.balanceOf(users[i]);
c1balances[i] = currency1.balanceOf(users[i]);
}

sendSwaps();

// check that tokenIn balance has been taken from the user
// check that tokenOut balance is the same as the initial balance
for (uint256 i = 0; i < users.length; i++) {
bool zeroForOne = i % 2 == 0 ? true : false;
if (zeroForOne) {
require(currency0.balanceOf(users[i]) + 1e18 == c0balances[i], "Currency0 balance should be decreased by 1e18");
require(currency1.balanceOf(users[i]) == c1balances[i], "Currency1 balance should not be changed");
} else {
require(currency0.balanceOf(users[i]) == c0balances[i], "Currency0 balance should not be changed");
require(currency1.balanceOf(users[i]) + 1e18 == c1balances[i], "Currency1 balance should be decreased by 1e18");
}
}

uint256 gas = gasleft();
executeBatch();
if (DEBUG) {
console.log("Gas used in donate: ", gas - gasleft());
}

for (uint256 i = 0; i < users.length; i++) {
bool zeroForOne = i % 2 == 0 ? true : false;
if (zeroForOne) { // true -> getting token1 in exchange for token0, hence, token1 should increase
require(currency1.balanceOf(users[i]) > c1balances[i], "Currency1 balance should be increased");
} else {
require(currency0.balanceOf(users[i]) > c0balances[i], "Currency0 balance should be increased");
}
}
}

function sendSwaps() internal {
for (uint256 i = 0; i < users.length; i++) {
vm.startPrank(users[i], users[i]);

uint256 gas = gasleft();
// 0 -> buy quote, 1 -> sell quote, 2 -> buy quote, 3 -> sell quote, 4 -> buy quote
swap(key, i % 2 == 0 ? true : false, -1e18, abi.encode(users[i]));

if (DEBUG) {
console.log("Gas used in swap [", i, "]: ", gas - gasleft());
}

vm.stopPrank();
}
}

function executeBatch() internal {
donateRouter.donate(key, 0, 0, "");
}
}

0 comments on commit 7aa02a2

Please sign in to comment.