diff --git a/src/abi/ramses-v3/RamsesV3Factory.abi.json b/src/abi/ramses-v3/RamsesV3Factory.abi.json new file mode 100644 index 000000000..4988bdf88 --- /dev/null +++ b/src/abi/ramses-v3/RamsesV3Factory.abi.json @@ -0,0 +1,39 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token1", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint24", + "name": "fee", + "type": "uint24" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "indexed": false, + "internalType": "address", + "name": "pool", + "type": "address" + } + ], + "name": "PoolCreated", + "type": "event" + } +] diff --git a/src/abi/ramses-v3/RamsesV3Pool.abi.json b/src/abi/ramses-v3/RamsesV3Pool.abi.json new file mode 100644 index 000000000..8b0da5a9b --- /dev/null +++ b/src/abi/ramses-v3/RamsesV3Pool.abi.json @@ -0,0 +1,212 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Burn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount0", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount1", + "type": "uint128" + } + ], + "name": "Collect", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Initialize", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickLower", + "type": "int24" + }, + { + "indexed": true, + "internalType": "int24", + "name": "tickUpper", + "type": "int24" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "amount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount0", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount1", + "type": "uint256" + } + ], + "name": "Mint", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount0", + "type": "int256" + }, + { + "indexed": false, + "internalType": "int256", + "name": "amount1", + "type": "int256" + }, + { + "indexed": false, + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "int24", + "name": "tick", + "type": "int24" + } + ], + "name": "Swap", + "type": "event" + }, + { + "inputs": [], + "name": "fee", + "outputs": [{ "internalType": "uint24", "name": "", "type": "uint24" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/abi/ramses-v3/RamsesV3StateMulticall.abi.json b/src/abi/ramses-v3/RamsesV3StateMulticall.abi.json new file mode 100644 index 000000000..c2b63b016 --- /dev/null +++ b/src/abi/ramses-v3/RamsesV3StateMulticall.abi.json @@ -0,0 +1,781 @@ +[ + { + "inputs": [ + { + "internalType": "contract IRamsesV3Factory", + "name": "factory", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "int16", + "name": "tickBitmapStart", + "type": "int16" + }, + { + "internalType": "int16", + "name": "tickBitmapEnd", + "type": "int16" + } + ], + "name": "getAdditionalBitmapWithTicks", + "outputs": [ + { + "components": [ + { + "internalType": "int16", + "name": "index", + "type": "int16" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickBitMapMappings[]", + "name": "tickBitmap", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "int24", + "name": "index", + "type": "int24" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickInfo", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickInfoMappings[]", + "name": "ticks", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IRamsesV3Factory", + "name": "factory", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "int16", + "name": "tickBitmapStart", + "type": "int16" + }, + { + "internalType": "int16", + "name": "tickBitmapEnd", + "type": "int16" + } + ], + "name": "getAdditionalBitmapWithoutTicks", + "outputs": [ + { + "components": [ + { + "internalType": "int16", + "name": "index", + "type": "int16" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickBitMapMappings[]", + "name": "tickBitmap", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IRamsesV3Factory", + "name": "factory", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "int16", + "name": "tickBitmapStart", + "type": "int16" + }, + { + "internalType": "int16", + "name": "tickBitmapEnd", + "type": "int16" + } + ], + "name": "getFullState", + "outputs": [ + { + "components": [ + { + "internalType": "contract IRamsesV3Pool", + "name": "pool", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockTimestamp", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint16", + "name": "observationIndex", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinality", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "feeProtocol", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.Slot0", + "name": "slot0", + "type": "tuple" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "maxLiquidityPerTick", + "type": "uint128" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "blockTimestamp", + "type": "uint32" + }, + { + "internalType": "int56", + "name": "tickCumulative", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.Observation", + "name": "observation", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int16", + "name": "index", + "type": "int16" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickBitMapMappings[]", + "name": "tickBitmap", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "int24", + "name": "index", + "type": "int24" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickInfo", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickInfoMappings[]", + "name": "ticks", + "type": "tuple[]" + } + ], + "internalType": "struct IRamsesV3StateMulticall.StateResult", + "name": "state", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IRamsesV3Factory", + "name": "factory", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "int16", + "name": "leftBitmapAmount", + "type": "int16" + }, + { + "internalType": "int16", + "name": "rightBitmapAmount", + "type": "int16" + } + ], + "name": "getFullStateWithRelativeBitmaps", + "outputs": [ + { + "components": [ + { + "internalType": "contract IRamsesV3Pool", + "name": "pool", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockTimestamp", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint16", + "name": "observationIndex", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinality", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "feeProtocol", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.Slot0", + "name": "slot0", + "type": "tuple" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "maxLiquidityPerTick", + "type": "uint128" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "blockTimestamp", + "type": "uint32" + }, + { + "internalType": "int56", + "name": "tickCumulative", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.Observation", + "name": "observation", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int16", + "name": "index", + "type": "int16" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickBitMapMappings[]", + "name": "tickBitmap", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "int24", + "name": "index", + "type": "int24" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickInfo", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickInfoMappings[]", + "name": "ticks", + "type": "tuple[]" + } + ], + "internalType": "struct IRamsesV3StateMulticall.StateResult", + "name": "state", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IRamsesV3Factory", + "name": "factory", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenIn", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenOut", + "type": "address" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "int16", + "name": "tickBitmapStart", + "type": "int16" + }, + { + "internalType": "int16", + "name": "tickBitmapEnd", + "type": "int16" + } + ], + "name": "getFullStateWithoutTicks", + "outputs": [ + { + "components": [ + { + "internalType": "contract IRamsesV3Pool", + "name": "pool", + "type": "address" + }, + { + "internalType": "uint256", + "name": "blockTimestamp", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint160", + "name": "sqrtPriceX96", + "type": "uint160" + }, + { + "internalType": "int24", + "name": "tick", + "type": "int24" + }, + { + "internalType": "uint16", + "name": "observationIndex", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinality", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "observationCardinalityNext", + "type": "uint16" + }, + { + "internalType": "uint8", + "name": "feeProtocol", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "unlocked", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.Slot0", + "name": "slot0", + "type": "tuple" + }, + { + "internalType": "uint128", + "name": "liquidity", + "type": "uint128" + }, + { + "internalType": "int24", + "name": "tickSpacing", + "type": "int24" + }, + { + "internalType": "uint128", + "name": "maxLiquidityPerTick", + "type": "uint128" + }, + { + "components": [ + { + "internalType": "uint32", + "name": "blockTimestamp", + "type": "uint32" + }, + { + "internalType": "int56", + "name": "tickCumulative", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityCumulativeX128", + "type": "uint160" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.Observation", + "name": "observation", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "int16", + "name": "index", + "type": "int16" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickBitMapMappings[]", + "name": "tickBitmap", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "int24", + "name": "index", + "type": "int24" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "liquidityGross", + "type": "uint128" + }, + { + "internalType": "int128", + "name": "liquidityNet", + "type": "int128" + }, + { + "internalType": "int56", + "name": "tickCumulativeOutside", + "type": "int56" + }, + { + "internalType": "uint160", + "name": "secondsPerLiquidityOutsideX128", + "type": "uint160" + }, + { + "internalType": "uint32", + "name": "secondsOutside", + "type": "uint32" + }, + { + "internalType": "bool", + "name": "initialized", + "type": "bool" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickInfo", + "name": "value", + "type": "tuple" + } + ], + "internalType": "struct IRamsesV3StateMulticall.TickInfoMappings[]", + "name": "ticks", + "type": "tuple[]" + } + ], + "internalType": "struct IRamsesV3StateMulticall.StateResult", + "name": "state", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/config.ts b/src/config.ts index 83de0d16b..3f40ee4d9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -489,6 +489,31 @@ const baseConfigs: { [network: number]: BaseConfig } = { uniswapV3EventLoggingSampleRate: 0, forceRpcFallbackDexs: [], }, + [Network.HYPEREVM]: { + network: Network.HYPEREVM, + networkName: 'HyperEVM', + isTestnet: false, + nativeTokenName: 'HYPE', + nativeTokenSymbol: 'HYPE', + wrappedNativeTokenAddress: '0x5555555555555555555555555555555555555555', + hasEIP1559: true, + augustusAddress: '0x0000000000000000000000000000000000000000', + tokenTransferProxyAddress: '0x0000000000000000000000000000000000000000', + multicallV2Address: '0xcA11bde05977b3631167028862bE2a173976CA11', + privateHttpProvider: process.env.HTTP_PROVIDER_999, + adapterAddresses: {}, + augustusRFQAddress: '0x0000000000000000000000000000000000000000', + augustusV6Address: '0x0000000000000000000000000000000000000000', + executorsAddresses: {}, + rpcPollingMaxAllowedStateDelayInBlocks: 0, + rpcPollingBlocksBackToTriggerUpdate: 0, + hashFlowDisabledMMs: [], + uniswapV3EventLoggingSampleRate: 0, + rfqConfigs: {}, + forceRpcFallbackDexs: [], + uniswapV2ExchangeRouterAddress: + '0x0000000000000000000000000000000000000000', + }, [Network.UNICHAIN]: { network: Network.UNICHAIN, networkName: 'Unichain', diff --git a/src/constants.ts b/src/constants.ts index 7997077a0..ddf5d867c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -42,6 +42,7 @@ export enum Network { UNICHAIN = 130, POLYGON = 137, SONIC = 146, + HYPEREVM = 999, BASE = 8453, PLASMA = 9745, ARBITRUM = 42161, diff --git a/src/dex/index.ts b/src/dex/index.ts index b41d8d34a..42d63be40 100644 --- a/src/dex/index.ts +++ b/src/dex/index.ts @@ -58,6 +58,7 @@ import { Bebop } from './bebop/bebop'; import { Swell } from './swell/swell'; import { PharaohV1 } from './solidly/forks-override/pharaohV1'; import { PharaohV3 } from './uniswap-v3/forks/pharaoh-v3/pharaoh-v3'; +import { RamsesV3 } from './uniswap-v3/forks/ramses-v3/ramses-v3'; import { EtherFi } from './etherfi'; import { Native } from './native/native'; import { Spark } from './spark/spark'; @@ -157,6 +158,7 @@ const Dexes = [ Swell, PharaohV1, PharaohV3, + RamsesV3, Spark, SparkPsm, AaveV3Stata, diff --git a/src/dex/uniswap-v3/config.ts b/src/dex/uniswap-v3/config.ts index a080b3973..e496b4320 100644 --- a/src/dex/uniswap-v3/config.ts +++ b/src/dex/uniswap-v3/config.ts @@ -3,6 +3,7 @@ import { DexConfigMap, AdapterMappings } from '../../types'; import { Network, SwapSide } from '../../constants'; import { Address } from '../../types'; import RamsesV2StateMulticallABI from '../../abi/RamsesV2StateMulticall.abi.json'; +import RamsesV3StateMulticallABI from '../../abi/ramses-v3/RamsesV3StateMulticall.abi.json'; import VelodromeSlipstreamMulticallABi from '../../abi/velodrome-slipstream/VelodromeSlipstreamStateMulticall.abi.json'; import { AbiItem } from 'web3-utils'; import { decodeStateMultiCallResultWithRelativeBitmaps as decodeStateMultiCallResultWithRelativeBitmapsForRamses } from './forks/ramses-v2/utils'; @@ -15,9 +16,13 @@ import { PangolinV3EventPool } from './forks/pangolin-v3/pangolin-v3-pool'; import { PharaohV3EventPool } from './forks/pharaoh-v3/pharaoh-v3-pool'; import PharaohV3MulticallABI from '../../abi/pharaoh-v3/PharaohV3StateMulticall.abi.json'; import { PharaohV3Factory } from './forks/pharaoh-v3/pharaoh-v3-factory'; +import { RamsesV3EventPool } from './forks/ramses-v3/ramses-v3-pool'; +import { RamsesV3Factory } from './forks/ramses-v3/ramses-v3-factory'; const SUPPORTED_FEES = [10000n, 3000n, 500n, 100n]; const RAMSES_FORKS_FEES = [...SUPPORTED_FEES, 50n, 250n]; +// RamsesV3: tickSpacing = fee, common tickSpacings from deployed pools +const RAMSES_V3_SUPPORTED_FEES = [50n, 10n, 5n, 1n]; const PANGOLIN_SUPPORTED_FEES = [8000n, 2500n, 500n, 100n]; const PHARAOH_V3_SUPPORTED_FEES = [20000n, 10000n, 3000n, 500n, 250n, 100n]; @@ -479,6 +484,83 @@ export const UniswapV3Config: DexConfigMap = { subgraphURL: 'EMnAvnfc1fwGSU6ToqYJCeEkXmSgmDmhwtyaha1tM5oi', }, }, + RamsesV3: { + [Network.ARBITRUM]: { + factory: '0xd0019e86edB35E1fedaaB03aED5c3c60f115d28b', + deployer: '0xb722efaAbe807FAeA16068f595EaA9aa1a62CECD', + quoter: '0x00d4FeA3Dd90C4480992f9c7Ea13b8a6A8F7E124', + router: '0x4730e03EB4a58A5e20244062D5f9A99bCf5770a6', + supportedFees: RAMSES_V3_SUPPORTED_FEES, + tickSpacings: [1n, 5n, 10n, 50n], + tickSpacingsToFees: { + '1': 1n, + '5': 5n, + '10': 10n, + '50': 50n, + }, + stateMulticall: '0x0588514800f67b400e83989a0170998bd9ab3c04', + stateMultiCallAbi: RamsesV3StateMulticallABI as AbiItem[], + uniswapMulticall: '0xbFbb2BCBc9dfFA029C27A249ae9BE031E1d83b1C', + chunksCount: 10, + initRetryFrequency: 10, + eventPoolImplementation: RamsesV3EventPool, + factoryImplementation: RamsesV3Factory, + initHash: + '0x892f127ed4b26ca352056c8fb54585a3268f76f97fdd84d5836ef4bda8d8c685', + subgraphURL: + 'https://arbitrumv2.kingdomsubgraph.com/subgraphs/name/ramses-pruned', + }, + [Network.HYPEREVM]: { + factory: '0x07E60782535752be279929e2DFfDd136Db2e6b45', + deployer: '0x301d2E3c7Db5904b3971cf9C36195e37c5a14873', + quoter: '0x403Bf94fe505cA0F0b1563C350B57dCeC8303ECd', + router: '0x76D91074B46fF76E04FE59a90526a40009943fd2', + supportedFees: RAMSES_V3_SUPPORTED_FEES, + tickSpacings: [1n, 5n, 10n, 50n], + tickSpacingsToFees: { + '1': 1n, + '5': 5n, + '10': 10n, + '50': 50n, + }, + stateMulticall: '0x118b6ff1d03149f958e525ac587ff577ddfafc0d', + stateMultiCallAbi: RamsesV3StateMulticallABI as AbiItem[], + uniswapMulticall: '0xD933929feBCe5494677EC22b7e7FaCA956311d37', + chunksCount: 10, + initRetryFrequency: 10, + eventPoolImplementation: RamsesV3EventPool, + factoryImplementation: RamsesV3Factory, + initHash: + '0x892f127ed4b26ca352056c8fb54585a3268f76f97fdd84d5836ef4bda8d8c685', + subgraphURL: + 'https://hyperevm.kingdomsubgraph.com/subgraphs/name/ramses-v3-pruned', + }, + [Network.POLYGON]: { + factory: '0x2Bef16A0081565E72100D73CBe19B1Bd2d802380', + deployer: '0x43B2Bf9f33036a02fC7A00935571c2A6b0108e66', + quoter: '0x3c4532424Eb018013595e4960Fd3de5397B6f571', + router: '0xdcD5F77697914E27f56FD263EF82923C8524AbAc', + supportedFees: RAMSES_V3_SUPPORTED_FEES, + tickSpacings: [1n, 5n, 10n, 50n], + tickSpacingsToFees: { + '1': 1n, + '5': 5n, + '10': 10n, + '50': 50n, + }, + stateMulticall: '0xd21eeb527414ff97208c7dfbc9c9eca520341a78', + stateMultiCallAbi: RamsesV3StateMulticallABI as AbiItem[], + uniswapMulticall: '0x25E0B133AD88B8cd60AFBD9DD7651FCC5FEc97bd', + chunksCount: 10, + initRetryFrequency: 10, + eventPoolImplementation: RamsesV3EventPool, + factoryImplementation: RamsesV3Factory, + initHash: + '0x892f127ed4b26ca352056c8fb54585a3268f76f97fdd84d5836ef4bda8d8c685', + subgraphURL: + 'https://polygon.kingdomsubgraph.com/subgraphs/name/ramses-pruned', + }, + }, Wagmi: { [Network.SONIC]: { factory: '0x56CFC796bC88C9c7e1b38C2b0aF9B7120B079aef', diff --git a/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3-factory.ts b/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3-factory.ts new file mode 100644 index 000000000..1227f5174 --- /dev/null +++ b/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3-factory.ts @@ -0,0 +1,16 @@ +import { UniswapV3Factory } from '../../uniswap-v3-factory'; +import { LogDescription } from 'ethers/lib/utils'; +import { Interface } from '@ethersproject/abi'; +import FactoryABI from '../../../../abi/ramses-v3/RamsesV3Factory.abi.json'; + +export class RamsesV3Factory extends UniswapV3Factory { + public readonly factoryIface = new Interface(FactoryABI); + + async handleNewPool(event: LogDescription) { + const token0 = event.args.token0.toLowerCase(); + const token1 = event.args.token1.toLowerCase(); + const tickSpacing = event.args.tickSpacing; + + await this.onPoolCreated({ token0, token1, fee: tickSpacing }); + } +} diff --git a/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3-pool.ts b/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3-pool.ts new file mode 100644 index 000000000..58c334a25 --- /dev/null +++ b/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3-pool.ts @@ -0,0 +1,183 @@ +import { UniswapV3EventPool } from '../../uniswap-v3-pool'; +import { MultiCallParams } from '../../../../lib/multi-wrapper'; +import { DecodedStateMultiCallResultWithRelativeBitmaps } from '../../types'; +import { uint256ToBigInt, uint24ToBigInt } from '../../../../lib/decoders'; +import { PoolState } from '../../types'; +import { decodeStateMultiCallResultWithRelativeBitmaps } from '../../utils'; +import { assert } from 'ts-essentials'; +import { _reduceTickBitmap, _reduceTicks } from '../../contract-math/utils'; +import { bigIntify } from '../../../../utils'; +import { TickBitMap } from '../../contract-math/TickBitMap'; +import { Interface } from 'ethers/lib/utils'; +import { ethers } from 'ethers'; +import { Address } from '../../../../types'; + +const POOL_FEE_ABI = ['function fee() view returns (uint24)']; + +export class RamsesV3EventPool extends UniswapV3EventPool { + private readonly poolFeeIface = new Interface(POOL_FEE_ABI); + + set poolAddress(address: Address) { + super.poolAddress = address; + this._stateRequestCallData = undefined; + } + + get poolAddress(): Address { + return super.poolAddress; + } + + protected _computePoolAddress( + token0: Address, + token1: Address, + fee: bigint, + ): Address { + if (token0 > token1) [token0, token1] = [token1, token0]; + + const encodedKey = ethers.utils.keccak256( + ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'int24'], + [token0, token1, BigInt.asUintN(24, this.tickSpacing!)], + ), + ); + + return ethers.utils.getCreate2Address( + this.deployer!, + encodedKey, + this.poolInitCodeHash, + ) as Address; + } + + protected _getStateRequestCallData() { + if (!this._stateRequestCallData) { + const callData: MultiCallParams< + bigint | DecodedStateMultiCallResultWithRelativeBitmaps + >[] = [ + { + target: this.token0, + callData: this.erc20Interface.encodeFunctionData('balanceOf', [ + this.poolAddress, + ]), + decodeFunction: uint256ToBigInt, + }, + { + target: this.token1, + callData: this.erc20Interface.encodeFunctionData('balanceOf', [ + this.poolAddress, + ]), + decodeFunction: uint256ToBigInt, + }, + { + target: this.poolAddress, + callData: this.poolFeeIface.encodeFunctionData('fee'), + decodeFunction: uint24ToBigInt, + }, + { + target: this.stateMultiContract.options.address, + callData: this.stateMultiContract.methods + .getFullStateWithRelativeBitmaps( + this.factoryAddress, + this.token0, + this.token1, + this.tickSpacing, + this.getBitmapRangeToRequest(), + this.getBitmapRangeToRequest(), + ) + .encodeABI(), + decodeFunction: + this.decodeStateMultiCallResultWithRelativeBitmaps !== undefined + ? this.decodeStateMultiCallResultWithRelativeBitmaps + : decodeStateMultiCallResultWithRelativeBitmaps, + }, + ]; + + this._stateRequestCallData = callData; + } + return this._stateRequestCallData; + } + + async generateState(blockNumber: number): Promise> { + const callData = this._getStateRequestCallData(); + + const [resBalance0, resBalance1, resFee, resState] = + await this.dexHelper.multiWrapper.tryAggregate< + bigint | DecodedStateMultiCallResultWithRelativeBitmaps + >( + false, + callData, + blockNumber, + this.dexHelper.multiWrapper.defaultBatchSize, + false, + ); + + assert(resState.success, 'Pool does not exist'); + + const [balance0, balance1, fee, _state] = [ + resBalance0.returnData, + resBalance1.returnData, + resFee.returnData, + resState.returnData, + ] as [ + bigint, + bigint, + bigint, + DecodedStateMultiCallResultWithRelativeBitmaps, + ]; + + this._assertActivePool(_state); + + const tickBitmap = {}; + const ticks = {}; + + _reduceTickBitmap(tickBitmap, _state.tickBitmap); + _reduceTicks(ticks, _state.ticks); + + const observations = { + [_state.slot0.observationIndex]: { + blockTimestamp: bigIntify(_state.observation.blockTimestamp), + tickCumulative: bigIntify(_state.observation.tickCumulative), + secondsPerLiquidityCumulativeX128: bigIntify( + _state.observation.secondsPerLiquidityCumulativeX128, + ), + initialized: _state.observation.initialized, + }, + }; + + const currentTick = bigIntify(_state.slot0.tick); + const tickSpacing = bigIntify(_state.tickSpacing); + + const startTickBitmap = TickBitMap.position(currentTick / tickSpacing)[0]; + const requestedRange = this.getBitmapRangeToRequest(); + + return { + networkId: this.dexHelper.config.data.network, + pool: _state.pool, + blockTimestamp: bigIntify(_state.blockTimestamp), + slot0: { + sqrtPriceX96: bigIntify(_state.slot0.sqrtPriceX96), + tick: currentTick, + observationIndex: +_state.slot0.observationIndex, + observationCardinality: +_state.slot0.observationCardinality, + observationCardinalityNext: +_state.slot0.observationCardinalityNext, + feeProtocol: bigIntify(_state.slot0.feeProtocol), + }, + liquidity: bigIntify(_state.liquidity), + fee, + tickSpacing, + maxLiquidityPerTick: bigIntify(_state.maxLiquidityPerTick), + tickBitmap, + ticks, + observations, + isValid: true, + startTickBitmap, + lowestKnownTick: + (BigInt.asIntN(24, startTickBitmap - requestedRange) << 8n) * + tickSpacing, + highestKnownTick: + ((BigInt.asIntN(24, startTickBitmap + requestedRange) << 8n) + + BigInt.asIntN(24, 255n)) * + tickSpacing, + balance0, + balance1, + }; + } +} diff --git a/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3.ts b/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3.ts new file mode 100644 index 000000000..465dcff61 --- /dev/null +++ b/src/dex/uniswap-v3/forks/ramses-v3/ramses-v3.ts @@ -0,0 +1,110 @@ +import { Network } from '../../../../constants'; +import { UniswapV3Config } from '../../config'; +import { getDexKeysWithNetwork } from '../../../../utils'; +import _ from 'lodash'; +import { VelodromeSlipstream } from '../velodrome-slipstream/velodrome-slipstream'; +import { Address } from '../../../../types'; +import { PoolLiquidity } from '../../../../types'; +import { MultiCallParams } from '../../../../lib/multi-wrapper'; +import { uint24ToBigInt } from '../../../../lib/decoders'; +import { Interface } from '@ethersproject/abi'; +import RamsesV3PoolABI from '../../../../abi/ramses-v3/RamsesV3Pool.abi.json'; +import { VelodromeSlipstreamEventPool } from '../velodrome-slipstream/velodrome-slipstream-pool'; + +export class RamsesV3 extends VelodromeSlipstream { + public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = + getDexKeysWithNetwork(_.pick(UniswapV3Config, ['RamsesV3'])); + + protected readonly poolIface = new Interface(RamsesV3PoolABI); + + protected buildFeeCallData( + pools: VelodromeSlipstreamEventPool[], + ): MultiCallParams[] { + return pools.map(pool => ({ + target: pool.poolAddress, + callData: this.poolIface.encodeFunctionData('fee', []), + decodeFunction: uint24ToBigInt, + })); + } + + async getTopPoolsForToken( + tokenAddress: Address, + limit: number, + ): Promise { + if (!this.config.subgraphURL) return []; + + const _tokenAddress = tokenAddress.toLowerCase(); + + const res = await this._querySubgraph( + `query ($token: Bytes!, $count: Int) { + pools0: clPools(first: $count, orderBy: totalValueLockedUSD, orderDirection: desc, where: {token0: $token}) { + id + token0 { + id + decimals + } + token1 { + id + decimals + } + totalValueLockedUSD + } + pools1: clPools(first: $count, orderBy: totalValueLockedUSD, orderDirection: desc, where: {token1: $token}) { + id + token0 { + id + decimals + } + token1 { + id + decimals + } + totalValueLockedUSD + } + }`, + { + token: _tokenAddress, + count: limit, + }, + ); + + if (!(res && res.pools0 && res.pools1)) { + this.logger.error( + `Error_${this.dexKey}_Subgraph: couldn't fetch the pools from the subgraph`, + ); + return []; + } + + const pools0 = _.map(res.pools0, (pool: any) => ({ + exchange: this.dexKey, + address: pool.id.toLowerCase(), + connectorTokens: [ + { + address: pool.token1.id.toLowerCase(), + decimals: parseInt(pool.token1.decimals), + }, + ], + liquidityUSD: parseFloat(pool.totalValueLockedUSD ?? 0), + })); + + const pools1 = _.map(res.pools1, (pool: any) => ({ + exchange: this.dexKey, + address: pool.id.toLowerCase(), + connectorTokens: [ + { + address: pool.token0.id.toLowerCase(), + decimals: parseInt(pool.token0.decimals), + }, + ], + liquidityUSD: parseFloat(pool.totalValueLockedUSD ?? 0), + })); + + const pools = _.slice( + _.sortBy(_.concat(pools0, pools1), [pool => -1 * pool.liquidityUSD]), + 0, + limit, + ); + + return pools; + } +} diff --git a/src/dex/uniswap-v3/uniswap-v3-e2e.test.ts b/src/dex/uniswap-v3/uniswap-v3-e2e.test.ts index 072ad62ab..c46b7934e 100644 --- a/src/dex/uniswap-v3/uniswap-v3-e2e.test.ts +++ b/src/dex/uniswap-v3/uniswap-v3-e2e.test.ts @@ -1129,4 +1129,84 @@ describe('UniswapV3 E2E', () => { ); }); }); + + describe('RamsesV3 E2E', () => { + const dexKey = 'RamsesV3'; + + describe('Arbitrum', () => { + const network = Network.ARBITRUM; + const provider = new StaticJsonRpcProvider( + generateConfig(network).privateHttpProvider, + network, + ); + const tokens = Tokens[network]; + const holders = Holders[network]; + + it(`SELL swapExactAmountIn USDC -> USDCe`, async () => { + await testE2E( + tokens['USDC'], + tokens['USDCe'], + holders['USDC'], + '1100000', + SwapSide.SELL, + dexKey, + ContractMethod.swapExactAmountIn, + network, + provider, + ); + }); + + it(`BUY swapExactAmountOut USDC -> USDCe`, async () => { + await testE2E( + tokens['USDC'], + tokens['USDCe'], + holders['USDC'], + '1000000', + SwapSide.BUY, + dexKey, + ContractMethod.swapExactAmountOut, + network, + provider, + ); + }); + }); + + describe('Polygon', () => { + const network = Network.POLYGON; + const provider = new StaticJsonRpcProvider( + generateConfig(network).privateHttpProvider, + network, + ); + const tokens = Tokens[network]; + const holders = Holders[network]; + + it(`SELL swapExactAmountIn USDCn -> USDT`, async () => { + await testE2E( + tokens['USDCn'], + tokens['USDT'], + holders['USDCn'], + '1100000', + SwapSide.SELL, + dexKey, + ContractMethod.swapExactAmountIn, + network, + provider, + ); + }); + + it(`BUY swapExactAmountOut USDCn -> USDT`, async () => { + await testE2E( + tokens['USDCn'], + tokens['USDT'], + holders['USDCn'], + '1000000', + SwapSide.BUY, + dexKey, + ContractMethod.swapExactAmountOut, + network, + provider, + ); + }); + }); + }); }); diff --git a/src/dex/uniswap-v3/uniswap-v3-integration.test.ts b/src/dex/uniswap-v3/uniswap-v3-integration.test.ts index 122948026..deb146f10 100644 --- a/src/dex/uniswap-v3/uniswap-v3-integration.test.ts +++ b/src/dex/uniswap-v3/uniswap-v3-integration.test.ts @@ -15,6 +15,7 @@ import { Address } from '@paraswap/core'; import { UniswapV3Config } from './config'; import { VelodromeSlipstream } from './forks/velodrome-slipstream/velodrome-slipstream'; import { PharaohV3 } from './forks/pharaoh-v3/pharaoh-v3'; +import { RamsesV3 } from './forks/ramses-v3/ramses-v3'; const network = Network.POLYGON; const TokenASymbol = 'USDC'; @@ -3189,3 +3190,469 @@ describe('PangolinV3', () => { checkPoolsLiquidity(poolLiquidity, TokenA.address, dexKey); }); }); + +describe('RamsesV3', () => { + const dexKey = 'RamsesV3'; + let blockNumber: number; + let ramsesV3: RamsesV3; + + describe('Arbitrum', () => { + const network = Network.ARBITRUM; + const dexHelper = new DummyDexHelper(network); + const TokenASymbol = 'WETH'; + const TokenA = Tokens[network][TokenASymbol]; + + const TokenBSymbol = 'USDC'; + const TokenB = Tokens[network][TokenBSymbol]; + + beforeEach(async () => { + blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); + ramsesV3 = new RamsesV3(network, dexKey, dexHelper); + }); + + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + const amounts = [ + 0n, + 100000000000000000n, + 200000000000000000n, + 300000000000000000n, + 400000000000000000n, + 500000000000000000n, + 600000000000000000n, + 700000000000000000n, + ]; + + const pools = await ramsesV3.getPoolIdentifiers( + TokenA, + TokenB, + SwapSide.SELL, + blockNumber, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Identifiers: `, + pools, + ); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await ramsesV3.getPricesVolume( + TokenA, + TokenB, + amounts, + SwapSide.SELL, + blockNumber, + pools, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Prices: `, + poolPrices, + ); + + expect(poolPrices).not.toBeNull(); + checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey); + + let falseChecksCounter = 0; + await Promise.all( + poolPrices!.map(async price => { + const fee = ramsesV3.eventPools[price.poolIdentifiers![0]]!.feeCode; + const res = await checkOnChainPricing( + dexHelper, + ramsesV3, + 'quoteExactInputSingle', + blockNumber, + '0x00d4FeA3Dd90C4480992f9c7Ea13b8a6A8F7E124', + price.prices, + TokenA.address, + TokenB.address, + fee, + amounts, + velodromeQuoterIface, + ); + if (res === false) falseChecksCounter++; + }), + ); + + expect(falseChecksCounter).toBeLessThan(poolPrices!.length); + }); + + it('getPoolIdentifiers and getPricesVolume BUY', async function () { + const amounts = [ + 0n, + 6000000n, + 12000000n, + 18000000n, + 24000000n, + 30000000n, + 36000000n, + 42000000n, + ]; + + const pools = await ramsesV3.getPoolIdentifiers( + TokenA, + TokenB, + SwapSide.BUY, + blockNumber, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Identifiers: `, + pools, + ); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await ramsesV3.getPricesVolume( + TokenA, + TokenB, + amounts, + SwapSide.BUY, + blockNumber, + pools, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Prices: `, + poolPrices, + ); + + expect(poolPrices).not.toBeNull(); + checkPoolPrices(poolPrices!, amounts, SwapSide.BUY, dexKey); + + let falseChecksCounter = 0; + await Promise.all( + poolPrices!.map(async price => { + const fee = ramsesV3.eventPools[price.poolIdentifiers![0]]!.feeCode; + const res = await checkOnChainPricing( + dexHelper, + ramsesV3, + 'quoteExactOutputSingle', + blockNumber, + '0x00d4FeA3Dd90C4480992f9c7Ea13b8a6A8F7E124', + price.prices, + TokenA.address, + TokenB.address, + fee, + amounts, + velodromeQuoterIface, + ); + if (res === false) falseChecksCounter++; + }), + ); + + expect(falseChecksCounter).toBeLessThan(poolPrices!.length); + }); + + it('getTopPoolsForToken', async function () { + const poolLiquidity = await ramsesV3.getTopPoolsForToken( + TokenB.address, + 10, + ); + console.log(`${TokenBSymbol} Top Pools:`, poolLiquidity); + + if (!ramsesV3.hasConstantPriceLargeAmounts) { + checkPoolsLiquidity(poolLiquidity, TokenB.address, dexKey); + } + }); + }); + + describe('Polygon', () => { + const network = Network.POLYGON; + const dexHelper = new DummyDexHelper(network); + const TokenASymbol = 'USDCn'; + const TokenA = Tokens[network][TokenASymbol]; + + const TokenBSymbol = 'USDT'; + const TokenB = Tokens[network][TokenBSymbol]; + + beforeEach(async () => { + blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); + ramsesV3 = new RamsesV3(network, dexKey, dexHelper); + }); + + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + const amounts = [ + 0n, + 6000000n, + 12000000n, + 18000000n, + 24000000n, + 30000000n, + 36000000n, + 42000000n, + ]; + + const pools = await ramsesV3.getPoolIdentifiers( + TokenA, + TokenB, + SwapSide.SELL, + blockNumber, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Identifiers: `, + pools, + ); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await ramsesV3.getPricesVolume( + TokenA, + TokenB, + amounts, + SwapSide.SELL, + blockNumber, + pools, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Prices: `, + poolPrices, + ); + + expect(poolPrices).not.toBeNull(); + checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey); + + let falseChecksCounter = 0; + await Promise.all( + poolPrices!.map(async price => { + const fee = ramsesV3.eventPools[price.poolIdentifiers![0]]!.feeCode; + const res = await checkOnChainPricing( + dexHelper, + ramsesV3, + 'quoteExactInputSingle', + blockNumber, + '0x3c4532424Eb018013595e4960Fd3de5397B6f571', + price.prices, + TokenA.address, + TokenB.address, + fee, + amounts, + velodromeQuoterIface, + ); + if (res === false) falseChecksCounter++; + }), + ); + + expect(falseChecksCounter).toBeLessThan(poolPrices!.length); + }); + + it('getPoolIdentifiers and getPricesVolume BUY', async function () { + const amounts = [ + 0n, + 6000000n, + 12000000n, + 18000000n, + 24000000n, + 30000000n, + 36000000n, + 42000000n, + ]; + + const pools = await ramsesV3.getPoolIdentifiers( + TokenA, + TokenB, + SwapSide.BUY, + blockNumber, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Identifiers: `, + pools, + ); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await ramsesV3.getPricesVolume( + TokenA, + TokenB, + amounts, + SwapSide.BUY, + blockNumber, + pools, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Prices: `, + poolPrices, + ); + + expect(poolPrices).not.toBeNull(); + checkPoolPrices(poolPrices!, amounts, SwapSide.BUY, dexKey); + + let falseChecksCounter = 0; + await Promise.all( + poolPrices!.map(async price => { + const fee = ramsesV3.eventPools[price.poolIdentifiers![0]]!.feeCode; + const res = await checkOnChainPricing( + dexHelper, + ramsesV3, + 'quoteExactOutputSingle', + blockNumber, + '0x3c4532424Eb018013595e4960Fd3de5397B6f571', + price.prices, + TokenA.address, + TokenB.address, + fee, + amounts, + velodromeQuoterIface, + ); + if (res === false) falseChecksCounter++; + }), + ); + + expect(falseChecksCounter).toBeLessThan(poolPrices!.length); + }); + + it('getTopPoolsForToken', async function () { + const poolLiquidity = await ramsesV3.getTopPoolsForToken( + TokenB.address, + 10, + ); + console.log(`${TokenBSymbol} Top Pools:`, poolLiquidity); + + if (!ramsesV3.hasConstantPriceLargeAmounts) { + checkPoolsLiquidity(poolLiquidity, TokenB.address, dexKey); + } + }); + }); + + describe('HyperEVM', () => { + const network = Network.HYPEREVM; + const dexHelper = new DummyDexHelper(network); + const TokenASymbol = 'WHYPE'; + const TokenA = Tokens[network][TokenASymbol]; + + const TokenBSymbol = 'USDT0'; + const TokenB = Tokens[network][TokenBSymbol]; + + beforeEach(async () => { + blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); + ramsesV3 = new RamsesV3(network, dexKey, dexHelper); + }); + + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + const amounts = [ + 0n, + 1000000000000000000n, + 2000000000000000000n, + 3000000000000000000n, + 4000000000000000000n, + 5000000000000000000n, + ]; + + const pools = await ramsesV3.getPoolIdentifiers( + TokenA, + TokenB, + SwapSide.SELL, + blockNumber, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Identifiers: `, + pools, + ); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await ramsesV3.getPricesVolume( + TokenA, + TokenB, + amounts, + SwapSide.SELL, + blockNumber, + pools, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Prices: `, + poolPrices, + ); + + expect(poolPrices).not.toBeNull(); + checkPoolPrices(poolPrices!, amounts, SwapSide.SELL, dexKey); + + let falseChecksCounter = 0; + await Promise.all( + poolPrices!.map(async price => { + const fee = ramsesV3.eventPools[price.poolIdentifiers![0]]!.feeCode; + const res = await checkOnChainPricing( + dexHelper, + ramsesV3, + 'quoteExactInputSingle', + blockNumber, + '0x403Bf94fe505cA0F0b1563C350B57dCeC8303ECd', + price.prices, + TokenA.address, + TokenB.address, + fee, + amounts, + velodromeQuoterIface, + ); + if (res === false) falseChecksCounter++; + }), + ); + + expect(falseChecksCounter).toBeLessThan(poolPrices!.length); + }); + + it('getPoolIdentifiers and getPricesVolume BUY', async function () { + const amounts = [0n, 1000000n, 2000000n, 3000000n, 4000000n, 5000000n]; + + const pools = await ramsesV3.getPoolIdentifiers( + TokenA, + TokenB, + SwapSide.BUY, + blockNumber, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Identifiers: `, + pools, + ); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await ramsesV3.getPricesVolume( + TokenA, + TokenB, + amounts, + SwapSide.BUY, + blockNumber, + pools, + ); + console.log( + `${TokenASymbol} <> ${TokenBSymbol} Pool Prices: `, + poolPrices, + ); + + expect(poolPrices).not.toBeNull(); + checkPoolPrices(poolPrices!, amounts, SwapSide.BUY, dexKey); + + let falseChecksCounter = 0; + await Promise.all( + poolPrices!.map(async price => { + const fee = ramsesV3.eventPools[price.poolIdentifiers![0]]!.feeCode; + const res = await checkOnChainPricing( + dexHelper, + ramsesV3, + 'quoteExactOutputSingle', + blockNumber, + '0x403Bf94fe505cA0F0b1563C350B57dCeC8303ECd', + price.prices, + TokenA.address, + TokenB.address, + fee, + amounts, + velodromeQuoterIface, + ); + if (res === false) falseChecksCounter++; + }), + ); + + expect(falseChecksCounter).toBeLessThan(poolPrices!.length); + }); + + it('getTopPoolsForToken', async function () { + const poolLiquidity = await ramsesV3.getTopPoolsForToken( + TokenB.address, + 10, + ); + console.log(`${TokenBSymbol} Top Pools:`, poolLiquidity); + + if (!ramsesV3.hasConstantPriceLargeAmounts) { + checkPoolsLiquidity(poolLiquidity, TokenB.address, dexKey); + } + }); + }); +}); diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index 4f6ebd996..ab4fface3 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -1930,6 +1930,16 @@ export const Tokens: { decimals: 18, }, }, + [Network.HYPEREVM]: { + WHYPE: { + address: '0x5555555555555555555555555555555555555555', + decimals: 18, + }, + USDT0: { + address: '0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb', + decimals: 6, + }, + }, [Network.SEPOLIA]: { ETH: { address: ETHER_ADDRESS, decimals: 18 }, WETH: { @@ -2414,6 +2424,7 @@ export const Holders: { [Network.UNICHAIN]: {}, [Network.SONIC]: {}, [Network.PLASMA]: {}, + [Network.HYPEREVM]: {}, }; export const NativeTokenSymbols: { [network: number]: string } = { @@ -2429,6 +2440,7 @@ export const NativeTokenSymbols: { [network: number]: string } = { [Network.SEPOLIA]: 'ETH', [Network.GNOSIS]: 'XDAI', [Network.UNICHAIN]: 'ETH', + [Network.HYPEREVM]: 'HYPE', }; export const WrappedNativeTokenSymbols: { [network: number]: string } = {