Skip to content

Commit

Permalink
Merge pull request #176 from 0xDmtri/fix/div-zero
Browse files Browse the repository at this point in the history
Fix/div zero
  • Loading branch information
0xKitsune authored Jun 10, 2024
2 parents 0fdbba4 + 5b7bb2d commit 7d9980a
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 14 deletions.
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ contracts/out/
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
*.env

# Temp checkpoint
*.temp-checkpoint.json

# intellij
.idea/
*.idea/
1 change: 1 addition & 0 deletions contracts/foundry.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[profile.default]
optimizer = false
evm_version = "cancun"

remappings = [
"forge-std=lib/forge-std/src/",
Expand Down
25 changes: 17 additions & 8 deletions contracts/src/GetWethValueInPoolBatchRequest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -242,26 +242,35 @@ contract GetWethValueInPoolBatchRequest {

function fromSqrtX96(uint160 sqrtPriceX96, bool token0IsReserve0, address token0, address token1)
internal
view
returns (uint256 priceX128)
{
unchecked {
///@notice very weird edge case
if (sqrtPriceX96 == 0) {
return 0;
}

(uint8 token0Decimals, bool t0s) = getTokenDecimalsUnsafe(token0);
(uint8 token1Decimals, bool t1s) = getTokenDecimalsUnsafe(token1);

///@notice if either of tokens does not implement `decimals()` return price as 0
if (!(t0s && t1s)) {
return 0;
}

///@notice Cache the difference between the input and output token decimals. p=y/x ==> p*10**(x_decimals-y_decimals)>>Q192 will be the proper price in base 10.
int8 decimalShift = int8(IERC20(token0).decimals()) - int8(IERC20(token1).decimals());
int8 decimalShift = int8(token0Decimals) - int8(token1Decimals);
///@notice Square the sqrtPrice ratio and normalize the value based on decimalShift.
uint256 priceSquaredX96 = decimalShift < 0
? uint256(sqrtPriceX96) ** 2 / uint256(10) ** (uint8(-decimalShift))
: uint256(sqrtPriceX96) ** 2 * 10 ** uint8(decimalShift);

///@notice The first value is a Q96 representation of p_token0, the second is 128X fixed point representation of p_token1.
uint256 priceSquaredShiftQ96 = token0IsReserve0
? priceSquaredX96 / Q96
: (Q96 * 0xffffffffffffffffffffffffffffffff) / (priceSquaredX96 / Q96);
uint256 priceSquaredShiftQ96 =
token0IsReserve0 ? priceSquaredX96 << 128 / Q96 : type(uint256).max / (priceSquaredX96 << 128 / Q96);

///@notice Convert the first value to 128X fixed point by shifting it left 128 bits and normalizing the value by Q96.
priceX128 = token0IsReserve0
? (uint256(priceSquaredShiftQ96) * 0xffffffffffffffffffffffffffffffff) / Q96
: priceSquaredShiftQ96;
priceX128 = token0IsReserve0 ? priceSquaredShiftQ96 / Q96 : priceSquaredShiftQ96;
require(priceX128 <= type(uint256).max, "Overflow");
}
}
Expand Down
69 changes: 69 additions & 0 deletions contracts/test/GetWethValueInPoolBatchRequest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import "forge-std/Test.sol";
import "../src/GetWethValueInPoolBatchRequest.sol";

contract GetWethValueInPoolBatchRequestTest is Test {
address constant weth = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 constant wethInPoolThreshold = 0.01 ether;

function setUp() public {}

function testUniV3GoodLiquidity() public {
address[] memory pools = new address[](1);
pools[0] = address(0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);

address[] memory dexes = new address[](1);
dexes[0] = address(0x1F98431c8aD98523631AE4a59f267346ea31F984);

bool[] memory dexIsUniV3 = new bool[](1);
dexIsUniV3[0] = true;

GetWethValueInPoolBatchRequest data =
new GetWethValueInPoolBatchRequest(pools, dexes, dexIsUniV3, weth, wethInPoolThreshold);

uint256[] memory weth_values = abi.decode(address(data).code, (uint256[]));

// expecting weth value to be non 0
assert(weth_values[0] != 0);
}

function testUniV3VerySmallLiquidity() public {
address[] memory pools = new address[](1);
pools[0] = address(0x697C1CcA83174363e9B6758B8CD616474487C192);

address[] memory dexes = new address[](1);
dexes[0] = address(0x1F98431c8aD98523631AE4a59f267346ea31F984);

bool[] memory dexIsUniV3 = new bool[](1);
dexIsUniV3[0] = true;

GetWethValueInPoolBatchRequest data =
new GetWethValueInPoolBatchRequest(pools, dexes, dexIsUniV3, weth, wethInPoolThreshold);

uint256[] memory weth_values = abi.decode(address(data).code, (uint256[]));

// expecting weth value to be 0
assert(weth_values[0] == 0);
}

function testUniV3ZeroLiquidity() public {
address[] memory pools = new address[](1);
pools[0] = address(0xc53489F27F4d8A1cdceD3BFe397CAF628e8aBC13);

address[] memory dexes = new address[](1);
dexes[0] = address(0x1F98431c8aD98523631AE4a59f267346ea31F984);

bool[] memory dexIsUniV3 = new bool[](1);
dexIsUniV3[0] = true;

GetWethValueInPoolBatchRequest data =
new GetWethValueInPoolBatchRequest(pools, dexes, dexIsUniV3, weth, wethInPoolThreshold);

uint256[] memory weth_values = abi.decode(address(data).code, (uint256[]));

// expecting weth value to be non 0
assert(weth_values[0] == 0);
}
}

Large diffs are not rendered by default.

83 changes: 83 additions & 0 deletions src/filters/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,3 +206,86 @@ where

Ok(weth_value_in_pools)
}

#[cfg(test)]
mod test {

use alloy::{
primitives::{address, uint},
providers::ProviderBuilder,
rpc::client::WsConnect,
};
use std::{path::Path, sync::Arc};

use super::*;
use crate::amm::{
uniswap_v2::factory::UniswapV2Factory, uniswap_v3::factory::UniswapV3Factory,
};
use crate::sync::{checkpoint::sync_amms_from_checkpoint, sync_amms};

const WETH_VALUE_THREASHOLD: U256 = uint!(1_000_000_000_000_000_000_U256);
const MIN_TOKEN_PRICE_IN_WETH: U256 = uint!(0_U256);
const WETH_ADDRESS: Address = address!("C02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
const CHECKPOINT_PATH: &str = ".temp-checkpoint.json";

#[tokio::test]
#[ignore] // Ignoring to not throttle the Provider on workflows
async fn test_weth_value_filter() {
let ipc_endpoint = std::env::var("WS").unwrap();
let ws = WsConnect::new(ipc_endpoint.to_owned());
let provider = Arc::new(ProviderBuilder::new().on_ws(ws).await.unwrap());

let factories = vec![
// Add Uniswap V2
Factory::UniswapV2Factory(UniswapV2Factory::new(
address!("5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f"),
10000835,
300,
)),
// Add Uniswap v3
Factory::UniswapV3Factory(UniswapV3Factory::new(
address!("1F98431c8aD98523631AE4a59f267346ea31F984"),
12369621,
)),
];

let checkpoint_exists = Path::new(CHECKPOINT_PATH).exists();

// sync all markets
let markets = if checkpoint_exists {
tracing::info!("Syncing pools from checkpoint");
let (_, markets) = sync_amms_from_checkpoint(CHECKPOINT_PATH, 500, provider.clone())
.await
.unwrap();

markets
} else {
tracing::info!("Syncing pools from inception");
let (markets, _) = sync_amms(
factories.clone(),
provider.clone(),
Some(CHECKPOINT_PATH),
500,
)
.await
.unwrap();

markets
};

filter_amms_below_weth_threshold(
markets,
&factories,
WETH_ADDRESS,
WETH_VALUE_THREASHOLD,
MIN_TOKEN_PRICE_IN_WETH,
500,
provider.clone(),
)
.await
.unwrap();
}

#[tokio::test]
async fn test_usd_value_filter() {}
}

0 comments on commit 7d9980a

Please sign in to comment.