diff --git a/packages/sushi/package.json b/packages/sushi/package.json index f56eda3c8b..8c860761f0 100644 --- a/packages/sushi/package.json +++ b/packages/sushi/package.json @@ -187,7 +187,7 @@ "dev": "tsc -w", "generate": "tsx ./scripts/generate.ts", "prepublishOnly": "pnpm build", - "test": "vitest run -c ./test/vitest.config.ts", + "test": "vitest run -c ./test/vitest.config.ts --test-timeout 600000", "test:debug": "vitest --inspect-brk --no-threads run -c ./test/vitest.config.ts", "test:watch": "vitest dev -c ./test/vitest.config.ts" }, diff --git a/packages/sushi/src/router/data-fetcher.ts b/packages/sushi/src/router/data-fetcher.ts index 138895df2d..aab335a60f 100644 --- a/packages/sushi/src/router/data-fetcher.ts +++ b/packages/sushi/src/router/data-fetcher.ts @@ -7,7 +7,6 @@ import { BaseSwapProvider } from './liquidity-providers/BaseSwap.js' import { BiswapProvider } from './liquidity-providers/Biswap.js' import { BlastDEXProvider } from './liquidity-providers/BlastDEX.js' import { BlazeSwapProvider } from './liquidity-providers/BlazeSwap.js' -import { CamelotProvider } from './liquidity-providers/Camelot.js' import { CurveProvider } from './liquidity-providers/CurveProvider.js' import { DfynProvider } from './liquidity-providers/Dfyn.js' import { DovishV3Provider } from './liquidity-providers/DovishV3.js' @@ -160,7 +159,6 @@ export class DataFetcher { BiswapProvider, BlastDEXProvider, BlazeSwapProvider, - CamelotProvider, CurveProvider, DfynProvider, DovishV3Provider, diff --git a/packages/sushi/src/router/index.ts b/packages/sushi/src/router/index.ts index b932bd8576..17a5063f26 100644 --- a/packages/sushi/src/router/index.ts +++ b/packages/sushi/src/router/index.ts @@ -9,3 +9,4 @@ export * from './tines-to-route-processor-2.js' export * from './tines-to-route-processor-4.js' export * from './PoolBinarySerialization.js' export * from './Sankey.AnyChart.js' +export * from './rain/rain-data-fetcher.js' diff --git a/packages/sushi/src/router/liquidity-providers/ApeSwap.ts b/packages/sushi/src/router/liquidity-providers/ApeSwap.ts index 71d8d5674f..0f0d4f82ed 100644 --- a/packages/sushi/src/router/liquidity-providers/ApeSwap.ts +++ b/packages/sushi/src/router/liquidity-providers/ApeSwap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class ApeSwapProvider extends UniswapV2BaseProvider { +export class ApeSwapProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.ETHEREUM]: '0xBAe5dc9B19004883d0377419FeF3c2C8832d7d7B', diff --git a/packages/sushi/src/router/liquidity-providers/BaseSwap.ts b/packages/sushi/src/router/liquidity-providers/BaseSwap.ts index 3f3ce6591c..46deecc1a5 100644 --- a/packages/sushi/src/router/liquidity-providers/BaseSwap.ts +++ b/packages/sushi/src/router/liquidity-providers/BaseSwap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class BaseSwapProvider extends UniswapV2BaseProvider { +export class BaseSwapProvider extends RainUniswapV2BaseProvider { override fee = 0.0025 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/Biswap.ts b/packages/sushi/src/router/liquidity-providers/Biswap.ts index 55f89fd348..ab1693c5aa 100644 --- a/packages/sushi/src/router/liquidity-providers/Biswap.ts +++ b/packages/sushi/src/router/liquidity-providers/Biswap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class BiswapProvider extends UniswapV2BaseProvider { +export class BiswapProvider extends RainUniswapV2BaseProvider { override fee = 0.002 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/BlastDEX.ts b/packages/sushi/src/router/liquidity-providers/BlastDEX.ts index b8742e1324..31267e85d3 100644 --- a/packages/sushi/src/router/liquidity-providers/BlastDEX.ts +++ b/packages/sushi/src/router/liquidity-providers/BlastDEX.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class BlastDEXProvider extends UniswapV2BaseProvider { +export class BlastDEXProvider extends RainUniswapV2BaseProvider { override fee = 0.002 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/BlazeSwap.ts b/packages/sushi/src/router/liquidity-providers/BlazeSwap.ts index 1b2237bcab..ba69dc348f 100644 --- a/packages/sushi/src/router/liquidity-providers/BlazeSwap.ts +++ b/packages/sushi/src/router/liquidity-providers/BlazeSwap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class BlazeSwapProvider extends UniswapV2BaseProvider { +export class BlazeSwapProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.FLARE]: '0x440602f459D7Dd500a74528003e6A20A46d6e2A6', diff --git a/packages/sushi/src/router/liquidity-providers/Camelot.ts b/packages/sushi/src/router/liquidity-providers/Camelot.ts index 48d99dc102..3d5d055e89 100644 --- a/packages/sushi/src/router/liquidity-providers/Camelot.ts +++ b/packages/sushi/src/router/liquidity-providers/Camelot.ts @@ -3,8 +3,8 @@ import { ChainId } from '../../chain/index.js' import { DataFetcherOptions } from '../data-fetcher.js' import { memoizer } from '../memoizer.js' import { type PoolCode } from '../pool-codes/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' type IsStableSwap = | ( @@ -22,7 +22,7 @@ type IsStableSwap = )[] | undefined -export class CamelotProvider extends UniswapV2BaseProvider { +export class CamelotProvider extends RainUniswapV2BaseProvider { // Camelot has a slightly different getReserves() abi // so needs to be overriden override getReservesAbi = parseAbi([ @@ -48,7 +48,7 @@ export class CamelotProvider extends UniswapV2BaseProvider { } override async getReserves( - poolCodesToCreate: PoolCode[], + poolCodesToCreate: Address[], options?: DataFetcherOptions, ): Promise { const multicallMemoize = await memoizer.fn(this.client.multicall) @@ -90,9 +90,9 @@ export class CamelotProvider extends UniswapV2BaseProvider { allowFailure: true, blockNumber: options?.blockNumber, contracts: poolCodesToCreate.map( - (poolCode) => + (address) => ({ - address: poolCode.pool.address as Address, + address, chainId: this.chainId, abi: this.getReservesAbi, functionName: 'getReserves', @@ -123,9 +123,9 @@ export class CamelotProvider extends UniswapV2BaseProvider { allowFailure: true, blockNumber: options?.blockNumber, contracts: poolCodesToCreate.map( - (poolCode) => + (address) => ({ - address: poolCode.pool.address as Address, + address, chainId: this.chainId, abi: [ { diff --git a/packages/sushi/src/router/liquidity-providers/Dfyn.ts b/packages/sushi/src/router/liquidity-providers/Dfyn.ts index 64cba9ea96..15017f38ba 100644 --- a/packages/sushi/src/router/liquidity-providers/Dfyn.ts +++ b/packages/sushi/src/router/liquidity-providers/Dfyn.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class DfynProvider extends UniswapV2BaseProvider { +export class DfynProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.POLYGON]: '0xE7Fb3e833eFE5F9c441105EB65Ef8b261266423B', diff --git a/packages/sushi/src/router/liquidity-providers/DovishV3.ts b/packages/sushi/src/router/liquidity-providers/DovishV3.ts index 8484d61aeb..e886e3ea2e 100644 --- a/packages/sushi/src/router/liquidity-providers/DovishV3.ts +++ b/packages/sushi/src/router/liquidity-providers/DovishV3.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class DovishV3Provider extends UniswapV3BaseProvider { +export class DovishV3Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.POLYGON_ZKEVM]: '0xdE474Db1Fa59898BC91314328D29507AcD0D593c', diff --git a/packages/sushi/src/router/liquidity-providers/DyorV2.ts b/packages/sushi/src/router/liquidity-providers/DyorV2.ts index e5ef032432..a83e5db88a 100644 --- a/packages/sushi/src/router/liquidity-providers/DyorV2.ts +++ b/packages/sushi/src/router/liquidity-providers/DyorV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class DyorV2Provider extends UniswapV2BaseProvider { +export class DyorV2Provider extends RainUniswapV2BaseProvider { override fee = 0.003 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/Elk.ts b/packages/sushi/src/router/liquidity-providers/Elk.ts index 9af43c7cbb..a191985d1b 100644 --- a/packages/sushi/src/router/liquidity-providers/Elk.ts +++ b/packages/sushi/src/router/liquidity-providers/Elk.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class ElkProvider extends UniswapV2BaseProvider { +export class ElkProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.AVALANCHE]: '0x091d35d7F63487909C863001ddCA481c6De47091', diff --git a/packages/sushi/src/router/liquidity-providers/Enosys.ts b/packages/sushi/src/router/liquidity-providers/Enosys.ts index b35e10e000..ceea900f62 100644 --- a/packages/sushi/src/router/liquidity-providers/Enosys.ts +++ b/packages/sushi/src/router/liquidity-providers/Enosys.ts @@ -1,17 +1,17 @@ import { getCreate2Address } from '@ethersproject/address' import { Address, PublicClient, encodePacked, keccak256 } from 'viem' import { ChainId } from '../../chain/index.js' - import { Token } from '../../currency/Token.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { StaticPool, UniswapV2BaseProvider } from './UniswapV2Base.js' +import { StaticPool } from './UniswapV2Base.js' // Enosys has multiple initCodeHashes, so it is required to override the pool address // calculations methods to use all the available initCodeHashes to generate multiple // pool addresses for a pair and then the wrong ones will be filtered out automatically // on multicall, just the same as any other non existant calculated pool addresses -export class EnosysProvider extends UniswapV2BaseProvider { +export class EnosysProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.FLARE]: '0x28b70f6Ed97429E40FE9a9CD3EB8E86BCBA11dd4', @@ -31,7 +31,7 @@ export class EnosysProvider extends UniswapV2BaseProvider { return 'Enosys' } - // same as _getPoolAddress() in UniswapV2BaseProvider, but instead of + // same as _getPoolAddress() in RainUniswapV2BaseProvider, but instead of // returning only 1 pool address, it returns array of calculated pool // addressses by using all available initCodeHashes _getPoolAddresses(t1: Token, t2: Token): Address[] { @@ -51,7 +51,7 @@ export class EnosysProvider extends UniswapV2BaseProvider { ) } - // same as original getStaticPools() in UniswapV2BaseProvider, but + // same as original getStaticPools() in RainUniswapV2BaseProvider, but // just overriden to do flatMap() to flatten array of pool addresses // per token pair, since there will be multiple calculated pool addresses // per token pair as a result of having multiple initCodeHashes diff --git a/packages/sushi/src/router/liquidity-providers/GravityFinance.ts b/packages/sushi/src/router/liquidity-providers/GravityFinance.ts index eeff769637..6458fb9f21 100644 --- a/packages/sushi/src/router/liquidity-providers/GravityFinance.ts +++ b/packages/sushi/src/router/liquidity-providers/GravityFinance.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class GravityFinanceProvider extends UniswapV2BaseProvider { +export class GravityFinanceProvider extends RainUniswapV2BaseProvider { override fee = 0.0025 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/HoneySwap.ts b/packages/sushi/src/router/liquidity-providers/HoneySwap.ts index 7db919a9b8..a475653bd8 100644 --- a/packages/sushi/src/router/liquidity-providers/HoneySwap.ts +++ b/packages/sushi/src/router/liquidity-providers/HoneySwap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class HoneySwapProvider extends UniswapV2BaseProvider { +export class HoneySwapProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.GNOSIS]: '0xA818b4F111Ccac7AA31D0BCc0806d64F2E0737D7', diff --git a/packages/sushi/src/router/liquidity-providers/HyperBlast.ts b/packages/sushi/src/router/liquidity-providers/HyperBlast.ts index e4460650a0..77dcd86b96 100644 --- a/packages/sushi/src/router/liquidity-providers/HyperBlast.ts +++ b/packages/sushi/src/router/liquidity-providers/HyperBlast.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class HyperBlastProvider extends UniswapV2BaseProvider { +export class HyperBlastProvider extends RainUniswapV2BaseProvider { override fee = 0.003 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/JetSwap.ts b/packages/sushi/src/router/liquidity-providers/JetSwap.ts index 2ed2fbd0c7..c375bc003e 100644 --- a/packages/sushi/src/router/liquidity-providers/JetSwap.ts +++ b/packages/sushi/src/router/liquidity-providers/JetSwap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class JetSwapProvider extends UniswapV2BaseProvider { +export class JetSwapProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.POLYGON]: '0x668ad0ed2622C62E24f0d5ab6B6Ac1b9D2cD4AC7', diff --git a/packages/sushi/src/router/liquidity-providers/KinetixV2.ts b/packages/sushi/src/router/liquidity-providers/KinetixV2.ts index 1cf407d8b2..01abce8f32 100644 --- a/packages/sushi/src/router/liquidity-providers/KinetixV2.ts +++ b/packages/sushi/src/router/liquidity-providers/KinetixV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class KinetixV2Provider extends UniswapV2BaseProvider { +export class KinetixV2Provider extends RainUniswapV2BaseProvider { override fee = 0.003 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/KinetixV3.ts b/packages/sushi/src/router/liquidity-providers/KinetixV3.ts index b7591e9e68..850d3a968c 100644 --- a/packages/sushi/src/router/liquidity-providers/KinetixV3.ts +++ b/packages/sushi/src/router/liquidity-providers/KinetixV3.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class KinetixV3Provider extends UniswapV3BaseProvider { +export class KinetixV3Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.KAVA]: '0x2dBB6254231C5569B6A4313c6C1F5Fe1340b35C2', diff --git a/packages/sushi/src/router/liquidity-providers/LaserSwap.ts b/packages/sushi/src/router/liquidity-providers/LaserSwap.ts index a4e52dbc4a..957920587f 100644 --- a/packages/sushi/src/router/liquidity-providers/LaserSwap.ts +++ b/packages/sushi/src/router/liquidity-providers/LaserSwap.ts @@ -1,9 +1,9 @@ import { Address, PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class LaserSwapV2Provider extends UniswapV2BaseProvider { +export class LaserSwapV2Provider extends RainUniswapV2BaseProvider { override fee = 0.0025 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/LiquidityProvider.ts b/packages/sushi/src/router/liquidity-providers/LiquidityProvider.ts index 77c31da08f..23419a36e9 100644 --- a/packages/sushi/src/router/liquidity-providers/LiquidityProvider.ts +++ b/packages/sushi/src/router/liquidity-providers/LiquidityProvider.ts @@ -1,4 +1,4 @@ -import { PublicClient } from 'viem' +import { Log, PublicClient } from 'viem' import { ChainId, chainShortName } from '../../chain/index.js' import type { Token } from '../../currency/index.js' import { DataFetcherOptions } from '../data-fetcher.js' @@ -130,6 +130,10 @@ export abstract class LiquidityProvider { [t0.address.toLowerCase(), t1.address.toLowerCase()] .sort((first, second) => (first > second ? -1 : 1)) .join(':') + + // methods interface for event log handling + processLog(_log: Log) {} + async afterProcessLog(_untilBlock: bigint) {} } export const UniV2LiquidityProviders: LiquidityProviders[] = [ diff --git a/packages/sushi/src/router/liquidity-providers/LynexV1.ts b/packages/sushi/src/router/liquidity-providers/LynexV1.ts index b9d14e40a3..67bcf43e24 100644 --- a/packages/sushi/src/router/liquidity-providers/LynexV1.ts +++ b/packages/sushi/src/router/liquidity-providers/LynexV1.ts @@ -5,14 +5,15 @@ import { Token } from '../../currency/index.js' import { DataFetcherOptions } from '../data-fetcher.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' import { memoizer } from '../memoizer.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { StaticPool, UniswapV2BaseProvider } from './UniswapV2Base.js' +import { StaticPool } from './UniswapV2Base.js' const GetFeesAbi = parseAbi([ 'function getFee(bool _stable) public view returns(uint256)', ]) -export class LynexV1Provider extends UniswapV2BaseProvider { +export class LynexV1Provider extends RainUniswapV2BaseProvider { STABLE_FEE = 0.0001 VOLATILE_FEE = 0.0025 constructor(chainId: ChainId, web3Client: PublicClient) { @@ -115,7 +116,7 @@ export class LynexV1Provider extends UniswapV2BaseProvider { ] } - // same as original getStaticPools() in UniswapV2BaseProvider, but + // same as original getStaticPools() in RainUniswapV2BaseProvider, but // just overriden to do flatMap() to flatten array of pool addresses // per token pair, since LynexV1 also has bool variable in the pool address salt // also has 2 fees if the pair is `stable` or not diff --git a/packages/sushi/src/router/liquidity-providers/LynexV2.ts b/packages/sushi/src/router/liquidity-providers/LynexV2.ts index 50f815cf58..30125f3de5 100644 --- a/packages/sushi/src/router/liquidity-providers/LynexV2.ts +++ b/packages/sushi/src/router/liquidity-providers/LynexV2.ts @@ -1,6 +1,6 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' -import { AlgebraV1BaseProvider } from './AlgebraV1Base.js' +import { AlgebraV1BaseProvider } from '../rain/AlgebraV1Base.js' import { LiquidityProviders } from './LiquidityProvider.js' export class LynexV2Provider extends AlgebraV1BaseProvider { diff --git a/packages/sushi/src/router/liquidity-providers/MSwap.ts b/packages/sushi/src/router/liquidity-providers/MSwap.ts index 03b97498d6..aa8d6b01c4 100644 --- a/packages/sushi/src/router/liquidity-providers/MSwap.ts +++ b/packages/sushi/src/router/liquidity-providers/MSwap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class MSwapProvider extends UniswapV2BaseProvider { +export class MSwapProvider extends RainUniswapV2BaseProvider { override fee = 0.003 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/MonoSwapV2.ts b/packages/sushi/src/router/liquidity-providers/MonoSwapV2.ts index c59c7f9731..8ad0fa4da1 100644 --- a/packages/sushi/src/router/liquidity-providers/MonoSwapV2.ts +++ b/packages/sushi/src/router/liquidity-providers/MonoSwapV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class MonoswapV2Provider extends UniswapV2BaseProvider { +export class MonoswapV2Provider extends RainUniswapV2BaseProvider { override fee = 0.003 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/MonoSwapV3.ts b/packages/sushi/src/router/liquidity-providers/MonoSwapV3.ts index 6ebf4d9855..a4bc46b2a5 100644 --- a/packages/sushi/src/router/liquidity-providers/MonoSwapV3.ts +++ b/packages/sushi/src/router/liquidity-providers/MonoSwapV3.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class MonoswapV3Provider extends UniswapV3BaseProvider { +export class MonoswapV3Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.BLAST]: '0x48d0F09710794313f33619c95147F34458BF7C3b', diff --git a/packages/sushi/src/router/liquidity-providers/NetSwap.ts b/packages/sushi/src/router/liquidity-providers/NetSwap.ts index 68132ad64a..51d85408a2 100644 --- a/packages/sushi/src/router/liquidity-providers/NetSwap.ts +++ b/packages/sushi/src/router/liquidity-providers/NetSwap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class NetSwapProvider extends UniswapV2BaseProvider { +export class NetSwapProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.METIS]: '0x70f51d68D16e8f9e418441280342BD43AC9Dff9f', diff --git a/packages/sushi/src/router/liquidity-providers/PancakeSwapV2.ts b/packages/sushi/src/router/liquidity-providers/PancakeSwapV2.ts index 932153c887..950f2f51f5 100644 --- a/packages/sushi/src/router/liquidity-providers/PancakeSwapV2.ts +++ b/packages/sushi/src/router/liquidity-providers/PancakeSwapV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class PancakeSwapV2Provider extends UniswapV2BaseProvider { +export class PancakeSwapV2Provider extends RainUniswapV2BaseProvider { override fee = 0.0025 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/PancakeSwapV3.ts b/packages/sushi/src/router/liquidity-providers/PancakeSwapV3.ts index 8fc5620b6e..29f2e7d633 100644 --- a/packages/sushi/src/router/liquidity-providers/PancakeSwapV3.ts +++ b/packages/sushi/src/router/liquidity-providers/PancakeSwapV3.ts @@ -1,16 +1,44 @@ -import { Address, PublicClient } from 'viem' +import { Address, PublicClient, parseAbiItem } from 'viem' import { uniswapV3FactoryAbi } from '../../abi/uniswapV3FactoryAbi.js' import { ChainId } from '../../chain/index.js' import { PANCAKESWAP_V3_FEE_SPACING_MAP, PancakeSwapV3FeeAmount, } from '../../config/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class PancakeSwapV3Provider extends UniswapV3BaseProvider { +export const PancakeV3EventsAbi = [ + parseAbiItem( + 'event Mint(address sender, address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1)', + ), + parseAbiItem( + 'event Collect(address indexed owner, address recipient, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount0, uint128 amount1)', + ), + parseAbiItem( + 'event Burn(address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1)', + ), + parseAbiItem( + // For PancakeV3 + 'event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick, uint128 protocolFeesToken0, uint128 protocolFeesToken1)', + ), + parseAbiItem( + 'event Flash(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1)', + ), + parseAbiItem( + 'event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1)', + ), + parseAbiItem( + 'event PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool)', + ), +] + +export class PancakeSwapV3Provider extends RainUniswapV3BaseProvider { override FEE = PancakeSwapV3FeeAmount override TICK_SPACINGS = PANCAKESWAP_V3_FEE_SPACING_MAP + override eventsAbi = + PancakeV3EventsAbi as any as RainUniswapV3BaseProvider['eventsAbi'] + constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.ARBITRUM]: '0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9', diff --git a/packages/sushi/src/router/liquidity-providers/QuickSwapV2.ts b/packages/sushi/src/router/liquidity-providers/QuickSwapV2.ts index 01b23f7ce3..5f180c2c61 100644 --- a/packages/sushi/src/router/liquidity-providers/QuickSwapV2.ts +++ b/packages/sushi/src/router/liquidity-providers/QuickSwapV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class QuickSwapV2Provider extends UniswapV2BaseProvider { +export class QuickSwapV2Provider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.POLYGON]: '0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32', diff --git a/packages/sushi/src/router/liquidity-providers/QuickswapV3.ts b/packages/sushi/src/router/liquidity-providers/QuickswapV3.ts index 76b0a6e225..37515c5b49 100644 --- a/packages/sushi/src/router/liquidity-providers/QuickswapV3.ts +++ b/packages/sushi/src/router/liquidity-providers/QuickswapV3.ts @@ -1,6 +1,6 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' -import { AlgebraV1BaseProvider } from './AlgebraV1Base.js' +import { AlgebraV1BaseProvider } from '../rain/AlgebraV1Base.js' import { LiquidityProviders } from './LiquidityProvider.js' export class QuickSwapV3Provider extends AlgebraV1BaseProvider { diff --git a/packages/sushi/src/router/liquidity-providers/Solarbeam.ts b/packages/sushi/src/router/liquidity-providers/Solarbeam.ts index e8c179bf04..2e48b48ec9 100644 --- a/packages/sushi/src/router/liquidity-providers/Solarbeam.ts +++ b/packages/sushi/src/router/liquidity-providers/Solarbeam.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class SolarbeamProvider extends UniswapV2BaseProvider { +export class SolarbeamProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.MOONRIVER]: '0x049581aEB6Fe262727f290165C29BDAB065a1B68', diff --git a/packages/sushi/src/router/liquidity-providers/SparkDexV2.ts b/packages/sushi/src/router/liquidity-providers/SparkDexV2.ts index 7823c5b031..5822a417bc 100644 --- a/packages/sushi/src/router/liquidity-providers/SparkDexV2.ts +++ b/packages/sushi/src/router/liquidity-providers/SparkDexV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class SparkDexV2Provider extends UniswapV2BaseProvider { +export class SparkDexV2Provider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.FLARE]: '0x16b619B04c961E8f4F06C10B42FDAbb328980A89', diff --git a/packages/sushi/src/router/liquidity-providers/SparkDexV3.ts b/packages/sushi/src/router/liquidity-providers/SparkDexV3.ts index 3a20fce34e..5412e47794 100644 --- a/packages/sushi/src/router/liquidity-providers/SparkDexV3.ts +++ b/packages/sushi/src/router/liquidity-providers/SparkDexV3.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class SparkDexV3Provider extends UniswapV3BaseProvider { +export class SparkDexV3Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.FLARE]: '0xb3fB4f96175f6f9D716c17744e5A6d4BA9da8176', diff --git a/packages/sushi/src/router/liquidity-providers/SparkDexV3_1.ts b/packages/sushi/src/router/liquidity-providers/SparkDexV3_1.ts index ff10214707..0382e0f84f 100644 --- a/packages/sushi/src/router/liquidity-providers/SparkDexV3_1.ts +++ b/packages/sushi/src/router/liquidity-providers/SparkDexV3_1.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class SparkDexV3_1Provider extends UniswapV3BaseProvider { +export class SparkDexV3_1Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.FLARE]: '0x8A2578d23d4C532cC9A98FaD91C0523f5efDE652', diff --git a/packages/sushi/src/router/liquidity-providers/SpookySwapV2.ts b/packages/sushi/src/router/liquidity-providers/SpookySwapV2.ts index 0c94b57638..7ac270e0e3 100644 --- a/packages/sushi/src/router/liquidity-providers/SpookySwapV2.ts +++ b/packages/sushi/src/router/liquidity-providers/SpookySwapV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class SpookySwapV2Provider extends UniswapV2BaseProvider { +export class SpookySwapV2Provider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.FANTOM]: '0x152eE697f2E276fA89E96742e9bB9aB1F2E61bE3', diff --git a/packages/sushi/src/router/liquidity-providers/SpookySwapV3.ts b/packages/sushi/src/router/liquidity-providers/SpookySwapV3.ts index 69d1f34c0e..6f498334cd 100644 --- a/packages/sushi/src/router/liquidity-providers/SpookySwapV3.ts +++ b/packages/sushi/src/router/liquidity-providers/SpookySwapV3.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class SpookySwapV3Provider extends UniswapV3BaseProvider { +export class SpookySwapV3Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.FANTOM]: '0x7928a2c48754501f3a8064765ECaE541daE5c3E6', diff --git a/packages/sushi/src/router/liquidity-providers/SushiSwapV2.ts b/packages/sushi/src/router/liquidity-providers/SushiSwapV2.ts index deb99dc463..73affe3eec 100644 --- a/packages/sushi/src/router/liquidity-providers/SushiSwapV2.ts +++ b/packages/sushi/src/router/liquidity-providers/SushiSwapV2.ts @@ -4,10 +4,10 @@ import { SUSHISWAP_V2_FACTORY_ADDRESS, SUSHISWAP_V2_INIT_CODE_HASH, } from '../../config/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class SushiSwapV2Provider extends UniswapV2BaseProvider { +export class SushiSwapV2Provider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = SUSHISWAP_V2_FACTORY_ADDRESS super(chainId, web3Client, factory, SUSHISWAP_V2_INIT_CODE_HASH) diff --git a/packages/sushi/src/router/liquidity-providers/SushiSwapV3.ts b/packages/sushi/src/router/liquidity-providers/SushiSwapV3.ts index 7139b54a70..816d45eb0a 100644 --- a/packages/sushi/src/router/liquidity-providers/SushiSwapV3.ts +++ b/packages/sushi/src/router/liquidity-providers/SushiSwapV3.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class SushiSwapV3Provider extends UniswapV3BaseProvider { +export class SushiSwapV3Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.ARBITRUM_NOVA]: '0xaa26771d497814E81D305c511Efbb3ceD90BF5bd', diff --git a/packages/sushi/src/router/liquidity-providers/SwapBlast.ts b/packages/sushi/src/router/liquidity-providers/SwapBlast.ts index 4f04bd000b..8500485199 100644 --- a/packages/sushi/src/router/liquidity-providers/SwapBlast.ts +++ b/packages/sushi/src/router/liquidity-providers/SwapBlast.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class SwapBlastProvider extends UniswapV2BaseProvider { +export class SwapBlastProvider extends RainUniswapV2BaseProvider { override fee = 0.001 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/ThrusterV2.ts b/packages/sushi/src/router/liquidity-providers/ThrusterV2.ts index b1cfad438b..994aeddb3d 100644 --- a/packages/sushi/src/router/liquidity-providers/ThrusterV2.ts +++ b/packages/sushi/src/router/liquidity-providers/ThrusterV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class ThrusterV2_3Provider extends UniswapV2BaseProvider { +export class ThrusterV2_3Provider extends RainUniswapV2BaseProvider { override fee = 0.003 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { @@ -23,7 +23,7 @@ export class ThrusterV2_3Provider extends UniswapV2BaseProvider { } } -export class ThrusterV2_1Provider extends UniswapV2BaseProvider { +export class ThrusterV2_1Provider extends RainUniswapV2BaseProvider { override fee = 0.01 constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { diff --git a/packages/sushi/src/router/liquidity-providers/ThrusterV3.ts b/packages/sushi/src/router/liquidity-providers/ThrusterV3.ts index d8c7447385..6b321ebc0e 100644 --- a/packages/sushi/src/router/liquidity-providers/ThrusterV3.ts +++ b/packages/sushi/src/router/liquidity-providers/ThrusterV3.ts @@ -2,10 +2,10 @@ import { Address, PublicClient } from 'viem' import { uniswapV3FactoryAbi } from '../../abi/uniswapV3FactoryAbi.js' import { ChainId } from '../../chain/index.js' import { SushiSwapV3FeeAmount } from '../../config/sushiswap-v3.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class ThrusterV3Provider extends UniswapV3BaseProvider { +export class ThrusterV3Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.BLAST]: '0xa08ae3d3f4dA51C22d3c041E468bdF4C61405AaB', diff --git a/packages/sushi/src/router/liquidity-providers/TraderJoe.ts b/packages/sushi/src/router/liquidity-providers/TraderJoe.ts index 968e5c9b0a..a8c67c5e5f 100644 --- a/packages/sushi/src/router/liquidity-providers/TraderJoe.ts +++ b/packages/sushi/src/router/liquidity-providers/TraderJoe.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class TraderJoeProvider extends UniswapV2BaseProvider { +export class TraderJoeProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.AVALANCHE]: '0x9Ad6C38BE94206cA50bb0d90783181662f0Cfa10', diff --git a/packages/sushi/src/router/liquidity-providers/UbeSwap.ts b/packages/sushi/src/router/liquidity-providers/UbeSwap.ts index 288b1882a1..0d471a7a4f 100644 --- a/packages/sushi/src/router/liquidity-providers/UbeSwap.ts +++ b/packages/sushi/src/router/liquidity-providers/UbeSwap.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class UbeSwapProvider extends UniswapV2BaseProvider { +export class UbeSwapProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.CELO]: '0x62d5b84bE28a183aBB507E125B384122D2C25fAE', diff --git a/packages/sushi/src/router/liquidity-providers/UniswapV2.ts b/packages/sushi/src/router/liquidity-providers/UniswapV2.ts index 782cef3a46..e0380979da 100644 --- a/packages/sushi/src/router/liquidity-providers/UniswapV2.ts +++ b/packages/sushi/src/router/liquidity-providers/UniswapV2.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class UniswapV2Provider extends UniswapV2BaseProvider { +export class UniswapV2Provider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.ETHEREUM]: '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f', diff --git a/packages/sushi/src/router/liquidity-providers/UniswapV2Base.ts b/packages/sushi/src/router/liquidity-providers/UniswapV2Base.ts index a41c4db9f8..5345febcd2 100644 --- a/packages/sushi/src/router/liquidity-providers/UniswapV2Base.ts +++ b/packages/sushi/src/router/liquidity-providers/UniswapV2Base.ts @@ -160,7 +160,7 @@ export abstract class UniswapV2BaseProvider extends LiquidityProvider { } async getReserves( - poolCodesToCreate: PoolCode[], + poolCodesToCreate: Address[], options?: DataFetcherOptions, ): Promise { const multicallMemoize = await memoizer.fn(this.client.multicall) @@ -171,9 +171,9 @@ export abstract class UniswapV2BaseProvider extends LiquidityProvider { allowFailure: true, blockNumber: options?.blockNumber, contracts: poolCodesToCreate.map( - (poolCode) => + (address) => ({ - address: poolCode.pool.address as Address, + address, chainId: this.chainId, abi: this.getReservesAbi, functionName: 'getReserves', @@ -263,7 +263,10 @@ export abstract class UniswapV2BaseProvider extends LiquidityProvider { } }) - const reserves = await this.getReserves(poolCodesToCreate, options) + const reserves = await this.getReserves( + poolCodesToCreate.map((v) => v.pool.address), + options, + ) this.handleCreatePoolCode(poolCodesToCreate, reserves, validUntilTimestamp) // console.debug( // `${this.getLogPrefix()} - ON DEMAND: Created and fetched reserves for ${created} pools, extended 'lifetime' for ${updated} pools` diff --git a/packages/sushi/src/router/liquidity-providers/UniswapV3.ts b/packages/sushi/src/router/liquidity-providers/UniswapV3.ts index 1fb4f36665..56b21f3de6 100644 --- a/packages/sushi/src/router/liquidity-providers/UniswapV3.ts +++ b/packages/sushi/src/router/liquidity-providers/UniswapV3.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' -export class UniswapV3Provider extends UniswapV3BaseProvider { +export class UniswapV3Provider extends RainUniswapV3BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.ETHEREUM]: '0x1F98431c8aD98523631AE4a59f267346ea31F984', diff --git a/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts b/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts index 49fb2c80ba..d072c3fa10 100644 --- a/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts +++ b/packages/sushi/src/router/liquidity-providers/UniswapV3Base.ts @@ -50,7 +50,7 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { TICK_SPACINGS: UniV3TickSpacingType = TICK_SPACINGS FEE: UniV3FeeType = SushiSwapV3FeeAmount poolsByTrade: Map = new Map() - pools: Map = new Map() + poolsCodes: Map = new Map() blockListener?: (() => void) | undefined unwatchBlockNumber?: () => void @@ -475,7 +475,7 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { this.getPoolProviderName(), ) transformedV3Pools.push(pc) - this.pools.set(pool.address.toLowerCase(), pc) + this.poolsCodes.set(pool.address.toLowerCase(), pc) }) this.poolsByTrade.set( @@ -556,7 +556,7 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { // }) } - getCurrentPoolList(): PoolCode[] { + getCurrentPoolList(_t0: Token, _t1: Token): PoolCode[] { // const tradeId = this.getTradeId(t0, t1) // const poolsByTrade = this.poolsByTrade.get(tradeId) ?? [] // return poolsByTrade @@ -564,7 +564,7 @@ export abstract class UniswapV3BaseProvider extends LiquidityProvider { // .filter(([poolAddress]) => poolsByTrade.includes(poolAddress)) // .map(([, p]) => p) // : [] - return Array.from(this.pools.values()) + return Array.from(this.poolsCodes.values()) } stopFetchPoolsData() { diff --git a/packages/sushi/src/router/liquidity-providers/VVSStandard.ts b/packages/sushi/src/router/liquidity-providers/VVSStandard.ts index a25d5eb3f3..02baf0b65e 100644 --- a/packages/sushi/src/router/liquidity-providers/VVSStandard.ts +++ b/packages/sushi/src/router/liquidity-providers/VVSStandard.ts @@ -1,9 +1,9 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV2BaseProvider } from '../rain/RainUniswapV2Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV2BaseProvider } from './UniswapV2Base.js' -export class VVSStandardProvider extends UniswapV2BaseProvider { +export class VVSStandardProvider extends RainUniswapV2BaseProvider { constructor(chainId: ChainId, web3Client: PublicClient) { const factory = { [ChainId.CRONOS]: '0x3B44B2a187a7b3824131F8db5a74194D0a42Fc15', diff --git a/packages/sushi/src/router/liquidity-providers/Wagmi.ts b/packages/sushi/src/router/liquidity-providers/Wagmi.ts index 4c6b5f846c..80bbbb30b4 100644 --- a/packages/sushi/src/router/liquidity-providers/Wagmi.ts +++ b/packages/sushi/src/router/liquidity-providers/Wagmi.ts @@ -1,7 +1,7 @@ import { PublicClient } from 'viem' import { ChainId } from '../../chain/index.js' +import { RainUniswapV3BaseProvider } from '../rain/RainUniswapV3Base.js' import { LiquidityProviders } from './LiquidityProvider.js' -import { UniswapV3BaseProvider } from './UniswapV3Base.js' enum WagmiFeeAmount { /** 0.01% */ @@ -21,7 +21,7 @@ const WagmiTickSpacing: Record = { [WagmiFeeAmount.HIGH]: 200, } -export class WagmiProvider extends UniswapV3BaseProvider { +export class WagmiProvider extends RainUniswapV3BaseProvider { override FEE = WagmiFeeAmount override TICK_SPACINGS = WagmiTickSpacing constructor(chainId: ChainId, web3Client: PublicClient) { diff --git a/packages/sushi/src/router/liquidity-providers/AlgebraV1Base.ts b/packages/sushi/src/router/rain/AlgebraV1Base.ts similarity index 58% rename from packages/sushi/src/router/liquidity-providers/AlgebraV1Base.ts rename to packages/sushi/src/router/rain/AlgebraV1Base.ts index bd6958be91..2b0f1e82c6 100644 --- a/packages/sushi/src/router/liquidity-providers/AlgebraV1Base.ts +++ b/packages/sushi/src/router/rain/AlgebraV1Base.ts @@ -1,27 +1,51 @@ import { Address, Hex, + Log, PublicClient, encodeAbiParameters, getAddress, keccak256, + parseAbiItem, } from 'viem' import { ChainId } from '../../chain/index.js' import { Token } from '../../currency/index.js' import { DataFetcherOptions } from '../data-fetcher.js' import { getCurrencyCombinations } from '../get-currency-combinations.js' -import { memoizer } from '../memoizer.js' import { - NUMBER_OF_SURROUNDING_TICKS, PoolFilter, StaticPoolUniV3, - UniswapV3BaseProvider, - V3Pool, - bitmapIndex, -} from './UniswapV3Base.js' +} from '../liquidity-providers/UniswapV3Base.js' +import { memoizer } from '../memoizer.js' +import { RainUniswapV3BaseProvider, RainV3Pool } from './RainUniswapV3Base.js' -export abstract class AlgebraV1BaseProvider extends UniswapV3BaseProvider { +export const AlgebraEventsAbi = [ + parseAbiItem( + 'event Mint(address sender, address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1)', + ), + parseAbiItem( + 'event Collect(address indexed owner, address recipient, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount0, uint128 amount1)', + ), + parseAbiItem( + 'event Burn(address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1)', + ), + parseAbiItem( + 'event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)', + ), + parseAbiItem( + 'event Flash(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1)', + ), + parseAbiItem('event Fee(uint16 fee)'), + parseAbiItem('event TickSpacing(int24 newTickSpacing)'), + // for algebra factory, new pool created + parseAbiItem( + 'event Pool(address indexed token0, address indexed token1, address pool)', + ), +] + +export abstract class AlgebraV1BaseProvider extends RainUniswapV3BaseProvider { override TICK_SPACINGS: Record = {} + override eventsAbi = AlgebraEventsAbi as any readonly BASE_FEE = 100 DEFAULT_TICK_SPACING = 1 @@ -51,11 +75,18 @@ export abstract class AlgebraV1BaseProvider extends UniswapV3BaseProvider { t1: Token, excludePools?: Set | PoolFilter, options?: DataFetcherOptions, - ): Promise { + ): Promise { let staticPools = this.getStaticPools(t0, t1) if (excludePools) staticPools = staticPools.filter((p) => !excludePools.has(p.address)) + const tradeId = this.getTradeId(t0, t1) + if (!this.poolsByTrade.has(tradeId)) + this.poolsByTrade.set( + tradeId, + staticPools.map((pool) => pool.address.toLowerCase()), + ) + const multicallMemoize = await memoizer.fn(this.client.multicall) const globalStateData = { @@ -118,155 +149,62 @@ export abstract class AlgebraV1BaseProvider extends UniswapV3BaseProvider { return undefined }) - let poolsTickSpacing: - | ( - | number - | { - error?: undefined - result: number - status: 'success' - } - | { - error: Error - result?: undefined - status: 'failure' - } - )[] - | undefined - - try { - const tickSpacingsData = { - multicallAddress: this.client.chain?.contracts?.multicall3?.address!, - allowFailure: true, - blockNumber: options?.blockNumber, - contracts: staticPools.map( - (pool) => - ({ - address: pool.address, - chainId: this.chainId, - abi: [ - { - inputs: [], - name: 'tickSpacing', - outputs: [{ internalType: 'int24', name: '', type: 'int24' }], - stateMutability: 'view', - type: 'function', - }, - ] as const, - functionName: 'tickSpacing', - }) as const, - ), - } - poolsTickSpacing = options?.memoize - ? await (multicallMemoize(tickSpacingsData) as Promise).catch( - (e) => { - console.warn( - `${this.getLogPrefix()} - INIT: multicall failed, message: ${ - e.message - }`, - ) - return undefined - }, - ) - : await this.client.multicall(tickSpacingsData).catch((e) => { - console.warn( - `${this.getLogPrefix()} - INIT: multicall failed, message: ${ - e.message - }`, - ) - return undefined - }) - } catch (_error) {} + const tickSpacings = await this.getTickSpacing(staticPools, options) - const existingPools: V3Pool[] = [] + const existingPools: RainV3Pool[] = [] staticPools.forEach((pool, i) => { - if (globalState === undefined || !globalState[i]) return - let thisPoolTickSpacing = this.DEFAULT_TICK_SPACING - if (poolsTickSpacing !== undefined && Array.isArray(poolsTickSpacing)) { - if (poolsTickSpacing[i] !== undefined) { - const ts = poolsTickSpacing[i] - if (typeof ts === 'number') { - thisPoolTickSpacing = ts - } else { - if (ts?.status === 'success') { - thisPoolTickSpacing = ts.result - } + const poolAddress = pool.address.toLowerCase() + if (this.pools.has(poolAddress)) return + if (this.nonExistentPools.get(poolAddress) ?? 0 > 1) return + if (globalState === undefined || !globalState[i]) { + this.handleNonExistentPool(poolAddress) + return + } + let tickSpacing = this.DEFAULT_TICK_SPACING + if (tickSpacings?.[i] !== undefined) { + const ts = tickSpacings[i] + if (typeof ts === 'number') { + tickSpacing = ts + } else { + if (ts?.status === 'success') { + tickSpacing = ts.result } } } const sqrtPriceX96 = globalState[i]!.result?.[0] // price const tick = globalState[i]!.result?.[1] // tick - if (!sqrtPriceX96 || sqrtPriceX96 === 0n || typeof tick !== 'number') + if (!sqrtPriceX96 || sqrtPriceX96 === 0n || typeof tick !== 'number') { + this.handleNonExistentPool(poolAddress) return + } const fee = globalState[i]!.result?.[2] // fee - if (!fee) return - const activeTick = - Math.floor(tick / thisPoolTickSpacing) * thisPoolTickSpacing - if (typeof activeTick !== 'number') return - this.TICK_SPACINGS[pool.address.toLowerCase()] = thisPoolTickSpacing + if (!fee) { + this.handleNonExistentPool(poolAddress) + return + } + const activeTick = this.getActiveTick(tick, tickSpacing) + if (typeof activeTick !== 'number') { + this.handleNonExistentPool(poolAddress) + return + } existingPools.push({ ...pool, fee, sqrtPriceX96, activeTick, + tickSpacing, + ticks: new Map(), + reserve0: 0n, + reserve1: 0n, + liquidity: 0n, + blockNumber: options?.blockNumber ?? 0n, }) }) return existingPools } - override getIndexes(existingPools: V3Pool[]): [number[], number[]] { - const minIndexes = existingPools.map((pool) => - bitmapIndex( - pool.activeTick - NUMBER_OF_SURROUNDING_TICKS, - this.TICK_SPACINGS[pool.address.toLowerCase()]!, - ), - ) - const maxIndexes = existingPools.map((pool) => - bitmapIndex( - pool.activeTick + NUMBER_OF_SURROUNDING_TICKS, - this.TICK_SPACINGS[pool.address.toLowerCase()]!, - ), - ) - return [minIndexes, maxIndexes] - } - - override handleTickBoundries( - i: number, - pool: V3Pool, - poolTicks: { - index: number - DLiquidity: bigint - }[], - minIndexes: number[], - maxIndexes: number[], - ) { - const lowerUnknownTick = - minIndexes[i]! * this.TICK_SPACINGS[pool.address.toLowerCase()]! * 256 - - this.TICK_SPACINGS[pool.address.toLowerCase()]! - console.assert( - poolTicks.length === 0 || lowerUnknownTick < poolTicks[0]!.index, - 'Error 236: unexpected min tick index', - ) - poolTicks.unshift({ - index: lowerUnknownTick, - DLiquidity: 0n, - }) - const upperUnknownTick = - (maxIndexes[i]! + 1) * - this.TICK_SPACINGS[pool.address.toLowerCase()]! * - 256 - console.assert( - poolTicks[poolTicks.length - 1]!.index < upperUnknownTick, - 'Error 244: unexpected max tick index', - ) - poolTicks.push({ - index: upperUnknownTick, - DLiquidity: 0n, - }) - } - override getStaticPools(t1: Token, t2: Token): StaticPoolUniV3[] { const allCombinations = getCurrencyCombinations(this.chainId, t1, t2) const currencyCombinations: [Token, Token][] = [] @@ -299,6 +237,39 @@ export abstract class AlgebraV1BaseProvider extends UniswapV3BaseProvider { override async ensureFeeAndTicks(): Promise { return true } + + // handle extra events that Algebra has + override otherEventCases( + log: Log, + event: Log< + bigint, + number, + boolean, + (typeof AlgebraEventsAbi)[number], + true, + typeof AlgebraEventsAbi, + (typeof AlgebraEventsAbi)[number]['name'] + >, + pool: RainV3Pool, + ): void { + switch (event.eventName) { + case 'Fee': { + if (log.blockNumber! >= pool.blockNumber) { + pool.blockNumber = log.blockNumber! + pool.fee = event.args.fee + } + break + } + case 'TickSpacing': { + if (log.blockNumber! >= pool.blockNumber) { + pool.blockNumber = log.blockNumber! + pool.tickSpacing = event.args.newTickSpacing + } + break + } + default: + } + } } // from packages/extractor/src/AlgebraExtractor.ts diff --git a/packages/sushi/src/router/rain/RainUniswapV2Base.ts b/packages/sushi/src/router/rain/RainUniswapV2Base.ts new file mode 100644 index 0000000000..029195737f --- /dev/null +++ b/packages/sushi/src/router/rain/RainUniswapV2Base.ts @@ -0,0 +1,171 @@ +import { Log, parseAbiItem, parseEventLogs } from 'viem' +import { Token } from '../../currency/index.js' +import { ConstantProductRPool, RToken } from '../../tines/index.js' +import { DataFetcherOptions } from '../data-fetcher.js' +import { filterOnDemandPools } from '../lib/api.js' +import { + StaticPool, + UniswapV2BaseProvider, +} from '../liquidity-providers/UniswapV2Base.js' +import { ConstantProductPoolCode, type PoolCode } from '../pool-codes/index.js' + +// extends v2 static pool +export interface RainV2Pool extends StaticPool { + reserve0: bigint + reserve1: bigint + blockNumber: bigint +} + +export const UniV2EventsAbi = [ + parseAbiItem('event Sync(uint112 reserve0, uint112 reserve1)'), + parseAbiItem( + 'event PairCreated(address indexed token0, address indexed token1, address pair, uint)', + ), +] + +export abstract class RainUniswapV2BaseProvider extends UniswapV2BaseProvider { + nonExistentPools: Map = new Map() + pools: Map = new Map() + eventsAbi = UniV2EventsAbi + + override async getOnDemandPools( + t0: Token, + t1: Token, + excludePools?: Set, + options?: DataFetcherOptions, + ): Promise { + const topPoolAddresses = Array.from(this.topPools.keys()) + let pools = + topPoolAddresses.length > 0 + ? filterOnDemandPools( + Array.from(this.availablePools.values()), + t0.address, + t1.address, + topPoolAddresses, + this.ON_DEMAND_POOL_SIZE, + ) + : this.getStaticPools(t0, t1) + if (excludePools) + pools = (pools as RainV2Pool[]).filter( + (p) => !excludePools.has(p.address), + ) + + if (pools.length === 0) { + return + } + + this.poolsByTrade.set( + this.getTradeId(t0, t1), + pools.map((pool) => pool.address.toLowerCase() as `0x${string}`), + ) + const poolCodesToCreate: RainV2Pool[] = [] + pools.forEach((pool) => { + const existingPool = this.pools.get(pool.address.toLowerCase()) + const nonExistentPool = this.nonExistentPools.get( + pool.address.toLowerCase(), + ) + if ( + existingPool === undefined && + (!nonExistentPool || nonExistentPool < 2) + ) { + poolCodesToCreate.push({ + ...pool, + blockNumber: options?.blockNumber ?? 0n, + } as RainV2Pool) + } + }) + + const reserves = await this.getReserves( + poolCodesToCreate.map((v) => v.address), + options, + ) + this.setPool(poolCodesToCreate, reserves) + } + + override getCurrentPoolList(t0: Token, t1: Token): PoolCode[] { + const tradeId = this.getTradeId(t0, t1) + const poolsByTrade = this.poolsByTrade.get(tradeId) ?? [] + const onDemandPoolCodes = poolsByTrade + ? Array.from(this.pools) + .filter(([poolAddress]) => + poolsByTrade.includes(poolAddress as `0x${string}`), + ) + .map(([, pool]) => { + const rPool = new ConstantProductRPool( + pool.address, + pool.token0 as RToken, + pool.token1 as RToken, + 'fee' in pool ? pool.fee : this.fee, + pool.reserve0, + pool.reserve1, + ) + return new ConstantProductPoolCode( + rPool, + this.getType(), + this.getPoolProviderName(), + ) + }) + : [] + + return [...this.topPools.values(), onDemandPoolCodes].flat() + } + + override processLog(log: Log) { + const factory = + this.factory[this.chainId as keyof typeof this.factory]!.toLowerCase() + const logAddress = log.address.toLowerCase() + if (logAddress === factory) { + try { + const event = parseEventLogs({ + logs: [log], + abi: this.eventsAbi, + eventName: 'PairCreated', + })[0]! + this.nonExistentPools.delete(event.args[2].toLowerCase()) + } catch {} + } else { + const pool = this.pools.get(logAddress as `0x${string}`) + if (pool) { + if (log.blockNumber! >= pool.blockNumber) { + try { + const event = parseEventLogs({ + logs: [log], + abi: this.eventsAbi, + eventName: 'Sync', + })[0]! + pool.blockNumber = log.blockNumber! + pool.reserve0 = event.args.reserve0 + pool.reserve1 = event.args.reserve1 + } catch {} + } + } + } + } + + setPool(poolCodesToCreate: RainV2Pool[], reserves: any[]) { + poolCodesToCreate.forEach((pool, i) => { + const res0 = reserves?.[i]?.result?.[0] + const res1 = reserves?.[i]?.result?.[1] + + if (res0 !== undefined && res1 !== undefined) { + this.pools.set(pool.address.toLowerCase(), { + ...pool, + reserve0: res0, + reserve1: res1, + }) + } else { + const nonExistentPool = this.nonExistentPools.get( + pool.address.toLowerCase(), + ) + if (nonExistentPool) { + this.nonExistentPools.set( + pool.address.toLowerCase(), + nonExistentPool + 1, + ) + } else { + this.nonExistentPools.set(pool.address.toLowerCase(), 1) + } + } + }) + } +} diff --git a/packages/sushi/src/router/rain/RainUniswapV3Base.ts b/packages/sushi/src/router/rain/RainUniswapV3Base.ts new file mode 100644 index 0000000000..862409329b --- /dev/null +++ b/packages/sushi/src/router/rain/RainUniswapV3Base.ts @@ -0,0 +1,760 @@ +import { Address, Log, parseAbiItem, parseEventLogs } from 'viem' +import { erc20Abi, tickLensAbi } from '../../abi/index.js' +import { Token } from '../../currency/index.js' +import { CLTick, RToken, UniV3Pool } from '../../tines/index.js' +import { DataFetcherOptions } from '../data-fetcher.js' +import { + NUMBER_OF_SURROUNDING_TICKS, + PoolFilter, + StaticPoolUniV3, + UniswapV3BaseProvider, + V3Pool, + bitmapIndex, +} from '../liquidity-providers/UniswapV3Base.js' +import { type PoolCode, UniV3PoolCode } from '../pool-codes/index.js' + +// extends V3Pool from UniswapV3Base +export interface RainV3Pool extends V3Pool { + tickSpacing: number + ticks: Map + reserve0: bigint + reserve1: bigint + liquidity: bigint + blockNumber: bigint +} + +export const tickSpacingAbi = [ + { + inputs: [], + name: 'tickSpacing', + outputs: [{ internalType: 'int24', name: '', type: 'int24' }], + stateMutability: 'view', + type: 'function', + }, +] as const + +export const UniV3EventsAbi = [ + parseAbiItem( + 'event Mint(address sender, address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1)', + ), + parseAbiItem( + 'event Collect(address indexed owner, address recipient, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount0, uint128 amount1)', + ), + parseAbiItem( + 'event Burn(address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1)', + ), + parseAbiItem( + 'event Swap(address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick)', + ), + parseAbiItem( + 'event Flash(address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1)', + ), + parseAbiItem( + 'event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1)', + ), + parseAbiItem( + 'event PoolCreated(address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool)', + ), +] + +export abstract class RainUniswapV3BaseProvider extends UniswapV3BaseProvider { + pools: Map = new Map() + nonExistentPools: Map = new Map() + eventsAbi = UniV3EventsAbi + newTicksQueue: [RainV3Pool, number[]][] = [] + + override getActiveTick = (tickCurrent: number, tickSpacing?: number) => + typeof tickCurrent === 'number' && typeof tickSpacing === 'number' + ? Math.floor(tickCurrent / tickSpacing) * tickSpacing + : undefined + + override async fetchPoolData( + t0: Token, + t1: Token, + excludePools?: Set | PoolFilter, + options?: DataFetcherOptions, + ): Promise { + let staticPools = this.getStaticPools(t0, t1) + if (excludePools) + staticPools = staticPools.filter((p) => !excludePools.has(p.address)) + + const tradeId = this.getTradeId(t0, t1) + if (!this.poolsByTrade.has(tradeId)) + this.poolsByTrade.set( + tradeId, + staticPools.map((pool) => pool.address.toLowerCase()), + ) + + const slot0 = await this.client + .multicall({ + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: staticPools.map( + (pool) => + ({ + address: pool.address as Address, + chainId: this.chainId, + abi: [ + { + inputs: [], + name: 'slot0', + outputs: [ + { + 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' }, + ], + stateMutability: 'view', + type: 'function', + }, + ], + functionName: 'slot0', + }) as const, + ), + }) + .catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + + const tickSpacings = await this.getTickSpacing(staticPools, options) + + const existingPools: RainV3Pool[] = [] + + staticPools.forEach((pool, i) => { + const poolAddress = pool.address.toLowerCase() + if (this.pools.has(poolAddress)) return + if (this.nonExistentPools.get(poolAddress) ?? 0 > 1) return + if (slot0 === undefined || !slot0[i]) { + this.handleNonExistentPool(poolAddress) + return + } + const sqrtPriceX96 = slot0[i]!.result?.[0] + const tick = slot0[i]!.result?.[1] + if (!sqrtPriceX96 || sqrtPriceX96 === 0n || typeof tick !== 'number') { + this.handleNonExistentPool(poolAddress) + return + } + let tickSpacing = this.TICK_SPACINGS[pool.fee]! + if (typeof tickSpacings?.[i] !== 'undefined') { + const ts = tickSpacings[i] + if (typeof ts === 'number') { + tickSpacing = ts + } else { + if (ts?.status === 'success') { + tickSpacing = ts.result + } + } + } + const activeTick = this.getActiveTick(tick, tickSpacing) + if (typeof activeTick !== 'number') { + this.handleNonExistentPool(poolAddress) + return + } + existingPools.push({ + ...pool, + sqrtPriceX96, + activeTick, + tickSpacing, + ticks: new Map(), + reserve0: 0n, + reserve1: 0n, + liquidity: 0n, + blockNumber: options?.blockNumber ?? 0n, + }) + }) + + return existingPools + } + + override getIndexes(existingPools: RainV3Pool[]): [number[], number[]] { + const minIndexes = existingPools.map((pool) => + bitmapIndex( + pool.activeTick - NUMBER_OF_SURROUNDING_TICKS, + pool.tickSpacing, + ), + ) + const maxIndexes = existingPools.map((pool) => + bitmapIndex( + pool.activeTick + NUMBER_OF_SURROUNDING_TICKS, + pool.tickSpacing, + ), + ) + return [minIndexes, maxIndexes] + } + + override async fetchPoolsForToken( + t0: Token, + t1: Token, + excludePools?: Set | PoolFilter, + options?: DataFetcherOptions, + ): Promise { + const existingPools = await this.fetchPoolData( + t0, + t1, + excludePools, + options, + ) + if (existingPools.length === 0) return + + const [liquidity, reserves, ticks] = await Promise.all([ + this.getLiquidity(existingPools, options), + this.getReserves(existingPools, options), + this.getTicks(existingPools, options), + ]) + existingPools.forEach((pool, i) => { + if ( + liquidity === undefined || + reserves === undefined || + ticks === undefined + ) + return + if ( + liquidity[i] === undefined || + reserves[i] === undefined || + ticks[i] === undefined + ) + return + this.pools.set(pool.address.toLowerCase(), { + ...pool, + reserve0: reserves[i]![0], + reserve1: reserves[i]![1], + liquidity: liquidity[i]!, + ticks: ticks[i]!, + }) + }) + } + + override getCurrentPoolList(t0: Token, t1: Token): PoolCode[] { + const tradeId = this.getTradeId(t0, t1) + const poolsByTrade = this.poolsByTrade.get(tradeId) ?? [] + return Array.from(this.pools.values()) + .filter((pool) => poolsByTrade.includes(pool.address.toLowerCase())) + .map((pool) => { + const v3Pool = new UniV3Pool( + pool.address, + pool.token0 as RToken, + pool.token1 as RToken, + pool.fee / 1_000_000, + pool.reserve0, + pool.reserve1, + pool.activeTick, + pool.liquidity, + pool.sqrtPriceX96, + this.getMaxTickDiapason(pool.activeTick, pool), + ) + + return new UniV3PoolCode( + v3Pool, + this.getType(), + this.getPoolProviderName(), + ) + }) + } + + async getReserves( + existingPools: RainV3Pool[], + options?: DataFetcherOptions, + ): Promise<([bigint, bigint] | undefined)[]> { + const results = await this.client + .multicall({ + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: existingPools.flatMap( + (pool) => + [ + { + chainId: this.chainId, + address: pool.token0.wrapped.address as Address, + args: [pool.address as Address], + abi: erc20Abi, + functionName: 'balanceOf', + }, + { + chainId: this.chainId, + address: pool.token1.wrapped.address as Address, + args: [pool.address as Address], + abi: erc20Abi, + functionName: 'balanceOf', + }, + ] as const, + ), + }) + .catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return Array.from({ length: existingPools.length }, () => undefined) + }) + + const reserves = [] + for (let i = 0; i < results.length; i += 2) { + const res0 = results?.[i]?.result + const res1 = results?.[i + 1]?.result + if (typeof res0 === 'bigint' && typeof res1 === 'bigint') { + reserves.push([res0, res1] as [bigint, bigint]) + } else { + reserves.push(undefined) + } + } + return reserves + } + + async getLiquidity( + existingPools: RainV3Pool[], + options?: DataFetcherOptions, + ): Promise<(bigint | undefined)[]> { + const results = await this.client + .multicall({ + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: existingPools.map( + (pool) => + ({ + chainId: this.chainId, + address: pool.address as Address, + abi: [ + { + inputs: [], + name: 'liquidity', + outputs: [ + { internalType: 'uint128', name: '', type: 'uint128' }, + ], + stateMutability: 'view', + type: 'function', + }, + ], + functionName: 'liquidity', + }) as const, + ), + }) + .catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return Array.from({ length: existingPools.length }, () => undefined) + }) + + const liquidities = [] + for (let i = 0; i < results.length; i++) { + const liquidity = results?.[i]?.result + if (typeof liquidity === 'bigint') { + liquidities.push(liquidity) + } else { + liquidities.push(undefined) + } + } + return liquidities + } + + async getTicks( + existingPools: RainV3Pool[], + options?: DataFetcherOptions, + ): Promise[] | undefined> { + const [minIndexes, maxIndexes] = this.getIndexes(existingPools) + const wordList = existingPools.map((pool, i) => { + const minIndex = minIndexes[i]! + const maxIndex = maxIndexes[i]! + + return [ + pool, + Array.from({ length: maxIndex - minIndex + 1 }, (_, i) => minIndex + i), + ] as [RainV3Pool, number[]] + }) + return await this.getTicksInner(wordList, options) + } + + async getTicksInner( + existingPools: [RainV3Pool, number[]][], + options?: DataFetcherOptions, + ): Promise[] | undefined> { + const wordList = existingPools.flatMap(([pool, words], i) => { + return words.flatMap((j) => ({ + chainId: this.chainId, + address: this.tickLens[ + this.chainId as keyof typeof this.tickLens + ] as Address, + args: [pool.address, j] as const, + abi: tickLensAbi, + functionName: 'getPopulatedTicksInWord' as const, + index: [i, j], + })) + }) + + const tickResults = await this.client + .multicall({ + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + contracts: wordList, + blockNumber: options?.blockNumber, + }) + .catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + + if (!tickResults) return undefined + + const poolTicks: Map[] = [] + tickResults.forEach((t, i) => { + const index = wordList[i]!.index[0]! + const wordIndex = wordList[i]!.index[1]! + if (poolTicks[index] === undefined) poolTicks[index] = new Map() + poolTicks[index]!.set( + wordIndex, + (t?.result || []) + .map((tick) => ({ + index: tick.tick, + DLiquidity: tick.liquidityNet, + })) + .sort((a, b) => a.index - b.index), + ) + }) + return poolTicks + } + + // fetches pool tickSpacing, this will be used + // instead of hardcoded TICK_SPACINGS values + async getTickSpacing(pools: StaticPoolUniV3[], options?: DataFetcherOptions) { + const calldata = { + multicallAddress: this.client.chain?.contracts?.multicall3 + ?.address as Address, + allowFailure: true, + blockNumber: options?.blockNumber, + contracts: pools.map( + (pool) => + ({ + address: pool.address as Address, + chainId: this.chainId, + abi: tickSpacingAbi, + functionName: 'tickSpacing', + }) as const, + ), + } + return await this.client.multicall(calldata).catch((e) => { + console.warn( + `${this.getLogPrefix()} - INIT: multicall failed, message: ${ + e.message + }`, + ) + return undefined + }) + } + + getMaxTickDiapason(tick: number, pool: RainV3Pool): CLTick[] { + const currentTickIndex = bitmapIndex(tick, pool.tickSpacing) + if (!pool.ticks.has(currentTickIndex)) return [] + let minIndex + let maxIndex + for (minIndex = currentTickIndex; pool.ticks.has(minIndex); --minIndex); + for (maxIndex = currentTickIndex + 1; pool.ticks.has(maxIndex); ++maxIndex); + if (maxIndex - minIndex <= 1) return [] + + let poolTicks: CLTick[] = [] + for (let i = minIndex + 1; i < maxIndex; ++i) + poolTicks = poolTicks.concat(pool.ticks.get(i)!) + + const lowerUnknownTick = + (minIndex + 1) * pool.tickSpacing * 256 - pool.tickSpacing + console.assert( + poolTicks.length === 0 || lowerUnknownTick < poolTicks[0]!.index, + 'Error 236: unexpected min tick index', + ) + poolTicks.unshift({ + index: lowerUnknownTick, + DLiquidity: 0n, + }) + const upperUnknownTick = maxIndex * pool.tickSpacing * 256 + console.assert( + poolTicks[poolTicks.length - 1]!.index < upperUnknownTick, + 'Error 244: unexpected max tick index', + ) + poolTicks.push({ + index: upperUnknownTick, + DLiquidity: 0n, + }) + + return poolTicks + } + + override processLog(log: Log) { + const factory = + this.factory[this.chainId as keyof typeof this.factory]!.toLowerCase() + const logAddress = log.address.toLowerCase() + if (logAddress === factory) { + try { + const event = parseEventLogs({ + logs: [log], + abi: this.eventsAbi, + eventName: 'PoolCreated', + })[0]! + this.nonExistentPools.delete(event.args.pool.toLowerCase()) + } catch {} + } else { + const pool = this.pools.get(logAddress) as RainV3Pool | undefined + if (pool) { + try { + const event = parseEventLogs({ logs: [log], abi: this.eventsAbi })[0]! + switch (event.eventName) { + case 'Mint': { + const { amount, amount0, amount1 } = event.args + const { tickLower, tickUpper } = event.args + if (log.blockNumber! >= pool.blockNumber) { + pool.blockNumber = log.blockNumber! + if ( + tickLower !== undefined && + tickUpper !== undefined && + amount !== undefined + ) { + const tick = pool.activeTick + if (tickLower <= tick && tick < tickUpper) + pool.liquidity += amount + } + if (amount1 !== undefined && amount0 !== undefined) { + pool.reserve0 += amount0 + pool.reserve1 += amount1 + } + if ( + tickLower !== undefined && + tickUpper !== undefined && + amount !== undefined + ) { + this.addTick(tickLower, amount, pool) + this.addTick(tickUpper, -amount, pool) + } + } + break + } + case 'Burn': { + const { amount } = event.args + const { tickLower, tickUpper } = event.args + if (log.blockNumber! >= pool.blockNumber) { + pool.blockNumber = log.blockNumber! + if ( + tickLower !== undefined && + tickUpper !== undefined && + amount !== undefined + ) { + const tick = pool.activeTick + if (tickLower <= tick && tick < tickUpper) + pool.liquidity -= amount + } + if ( + tickLower !== undefined && + tickUpper !== undefined && + amount !== undefined + ) { + this.addTick(tickLower, -amount, pool) + this.addTick(tickUpper, amount, pool) + } + } + break + } + case 'Collect': + case 'CollectProtocol': { + if (log.blockNumber! >= pool.blockNumber) { + pool.blockNumber = log.blockNumber! + const { amount0, amount1 } = event.args + if (amount0 !== undefined && amount1 !== undefined) { + pool.reserve0 -= amount0 + pool.reserve1 -= amount1 + } + } + break + } + case 'Flash': { + if (log.blockNumber! >= pool.blockNumber) { + pool.blockNumber = log.blockNumber! + const { paid0, paid1 } = event.args + if (paid0 !== undefined && paid1 !== undefined) { + pool.reserve0 += paid0 + pool.reserve1 += paid1 + } + } + break + } + case 'Swap': { + if (log.blockNumber! >= pool.blockNumber) { + pool.blockNumber = log.blockNumber! + const { amount0, amount1, sqrtPriceX96, liquidity, tick } = + event.args + if (amount0 !== undefined && amount1 !== undefined) { + pool.reserve0 += amount0 + pool.reserve1 += amount1 + } + if (sqrtPriceX96 !== undefined) pool.sqrtPriceX96 = sqrtPriceX96 + if (liquidity !== undefined) pool.liquidity = liquidity + if (tick !== undefined) { + pool.activeTick = + Math.floor(tick / pool.tickSpacing) * pool.tickSpacing + const newTicks = this.onPoolTickChange(pool.activeTick, pool) + const queue = this.newTicksQueue.find( + (v) => v[0].address === pool.address, + ) + if (queue) { + for (const tick of newTicks) { + if (!queue[1].includes(tick)) queue[1].push(tick) + } + } else { + this.newTicksQueue.push([pool, newTicks]) + } + } + } + break + } + default: { + this.otherEventCases(log, event, pool) + } + } + } catch {} + } + } + } + + // for child classes if they have other events to handle such as AlgebraV1Base + otherEventCases(_log: Log, _event: Log, _pool: RainV3Pool) {} + + override async afterProcessLog(untilBlock: bigint) { + const newTicksQueue = [...this.newTicksQueue.splice(0)] + try { + if (newTicksQueue.length) { + const newTicks = await this.getTicksInner(newTicksQueue, { + blockNumber: untilBlock, + }) + newTicksQueue.forEach(([pool], i) => { + newTicks?.[i]?.forEach((newTick, index) => { + pool.ticks.set(index, newTick) + }) + }) + } + } catch { + // if unsuccessfull to get new ticks, put them back on queue for next try + newTicksQueue.forEach(([pool, newTicks]) => { + const queue = this.newTicksQueue.find( + (v) => v[0].address === pool.address, + ) + if (queue) { + for (const tick of newTicks) { + if (!queue[1].includes(tick)) queue[1].push(tick) + } + } else { + this.newTicksQueue.push([pool, newTicks]) + } + }) + throw '' + } + } + + addTick(tick: number, amount: bigint, pool: RainV3Pool) { + const tickWord = bitmapIndex(tick, pool.tickSpacing) + const ticks = pool.ticks.get(tickWord) + if (ticks !== undefined) { + if (ticks.length === 0 || tick < ticks[0]!.index) { + ticks.unshift({ index: tick, DLiquidity: amount }) + return + } + if (tick === ticks[0]!.index) { + ticks[0]!.DLiquidity = ticks[0]!.DLiquidity + amount + if (ticks[0]!.DLiquidity === 0n) ticks.splice(0, 1) + return + } + + let start = 0 + let end = ticks.length + while (end - start > 1) { + const middle = Math.floor((start + end) / 2) + const index = ticks[middle]!.index + if (index < tick) start = middle + else if (index > tick) end = middle + else { + ticks[middle]!.DLiquidity = ticks[middle]!.DLiquidity + amount + if (ticks[middle]!.DLiquidity === 0n) ticks.splice(middle, 1) + return + } + } + ticks.splice(start + 1, 0, { index: tick, DLiquidity: amount }) + } + } + + onPoolTickChange(tick: number, pool: RainV3Pool): number[] { + const currentTickWord = bitmapIndex(tick, pool.tickSpacing) + const minWord = bitmapIndex( + tick - NUMBER_OF_SURROUNDING_TICKS, + pool.tickSpacing, + ) + const maxWord = bitmapIndex( + tick + NUMBER_OF_SURROUNDING_TICKS, + pool.tickSpacing, + ) + + const direction = currentTickWord - minWord <= maxWord - currentTickWord + const wordNumber = maxWord - minWord + const newTicks: number[] = [] + for (let i = wordNumber; i >= 0; --i) { + const wordIndex = currentTickWord + this.getJump(i, direction) + const wordState = pool.ticks.get(wordIndex) + if (wordState === undefined) newTicks.push(wordIndex) + } + return newTicks + } + + // if positiveFirst == true returns 0, 1, -1, 2, -2, 3, -3, ... + // if positiveFirst == false returns 0, -1, 1, -2, 2, -3, 3, ... + getJump(index: number, positiveFirst: boolean): number { + let res + if (index % 2 === 0) res = -index / 2 + else res = (index + 1) / 2 + return positiveFirst ? res : -res + } + + handleNonExistentPool(poolAddress: string) { + const v = this.nonExistentPools.get(poolAddress) + if (v) { + this.nonExistentPools.set(poolAddress, v + 1) + } else { + this.nonExistentPools.set(poolAddress, 1) + } + } +} diff --git a/packages/sushi/src/router/rain/rain-data-fetcher.ts b/packages/sushi/src/router/rain/rain-data-fetcher.ts new file mode 100644 index 0000000000..a54e8d669d --- /dev/null +++ b/packages/sushi/src/router/rain/rain-data-fetcher.ts @@ -0,0 +1,225 @@ +import { isDeepStrictEqual } from 'util' +import { ParseAbiItem } from 'viem' +import { Type } from '../../currency/index.js' +import { DataFetcher, DataFetcherOptions } from '../data-fetcher.js' +import { ApeSwapProvider } from '../liquidity-providers/ApeSwap.js' +import { BaseSwapProvider } from '../liquidity-providers/BaseSwap.js' +import { BiswapProvider } from '../liquidity-providers/Biswap.js' +import { BlastDEXProvider } from '../liquidity-providers/BlastDEX.js' +import { BlazeSwapProvider } from '../liquidity-providers/BlazeSwap.js' +import { DfynProvider } from '../liquidity-providers/Dfyn.js' +import { DovishV3Provider } from '../liquidity-providers/DovishV3.js' +import { DyorV2Provider } from '../liquidity-providers/DyorV2.js' +import { ElkProvider } from '../liquidity-providers/Elk.js' +import { EnosysProvider } from '../liquidity-providers/Enosys.js' +import { GravityFinanceProvider } from '../liquidity-providers/GravityFinance.js' +import { HoneySwapProvider } from '../liquidity-providers/HoneySwap.js' +import { HyperBlastProvider } from '../liquidity-providers/HyperBlast.js' +import { JetSwapProvider } from '../liquidity-providers/JetSwap.js' +import { KinetixV2Provider } from '../liquidity-providers/KinetixV2.js' +import { KinetixV3Provider } from '../liquidity-providers/KinetixV3.js' +import { LaserSwapV2Provider } from '../liquidity-providers/LaserSwap.js' +import { LiquidityProviders } from '../liquidity-providers/LiquidityProvider.js' +import { LynexV1Provider } from '../liquidity-providers/LynexV1.js' +import { LynexV2Provider } from '../liquidity-providers/LynexV2.js' +import { MSwapProvider } from '../liquidity-providers/MSwap.js' +import { MonoswapV2Provider } from '../liquidity-providers/MonoSwapV2.js' +import { MonoswapV3Provider } from '../liquidity-providers/MonoSwapV3.js' +import { NativeWrapProvider } from '../liquidity-providers/NativeWrapProvider.js' +import { NetSwapProvider } from '../liquidity-providers/NetSwap.js' +import { PancakeSwapV2Provider } from '../liquidity-providers/PancakeSwapV2.js' +import { PancakeSwapV3Provider } from '../liquidity-providers/PancakeSwapV3.js' +import { QuickSwapV2Provider } from '../liquidity-providers/QuickSwapV2.js' +import { QuickSwapV3Provider } from '../liquidity-providers/QuickswapV3.js' +import { SolarbeamProvider } from '../liquidity-providers/Solarbeam.js' +import { SparkDexV2Provider } from '../liquidity-providers/SparkDexV2.js' +import { SparkDexV3Provider } from '../liquidity-providers/SparkDexV3.js' +import { SparkDexV3_1Provider } from '../liquidity-providers/SparkDexV3_1.js' +import { SpookySwapV2Provider } from '../liquidity-providers/SpookySwapV2.js' +import { SpookySwapV3Provider } from '../liquidity-providers/SpookySwapV3.js' +import { SushiSwapV2Provider } from '../liquidity-providers/SushiSwapV2.js' +import { SushiSwapV3Provider } from '../liquidity-providers/SushiSwapV3.js' +import { SwapBlastProvider } from '../liquidity-providers/SwapBlast.js' +import { + ThrusterV2_1Provider, + ThrusterV2_3Provider, +} from '../liquidity-providers/ThrusterV2.js' +import { ThrusterV3Provider } from '../liquidity-providers/ThrusterV3.js' +import { TraderJoeProvider } from '../liquidity-providers/TraderJoe.js' +import { UbeSwapProvider } from '../liquidity-providers/UbeSwap.js' +import { UniswapV2Provider } from '../liquidity-providers/UniswapV2.js' +import { UniswapV3Provider } from '../liquidity-providers/UniswapV3.js' +import { VVSStandardProvider } from '../liquidity-providers/VVSStandard.js' +import { WagmiProvider } from '../liquidity-providers/Wagmi.js' +import { RainUniswapV2BaseProvider } from './RainUniswapV2Base.js' +import { RainUniswapV3BaseProvider } from './RainUniswapV3Base.js' + +export class RainDataFetcher extends DataFetcher { + eventsAbi: ParseAbiItem[] = [] + + override _setProviders(providers?: LiquidityProviders[]) { + this.providers = [new NativeWrapProvider(this.chainId, this.web3Client)] + ;[ + ApeSwapProvider, + BaseSwapProvider, + BiswapProvider, + BlastDEXProvider, + BlazeSwapProvider, + DfynProvider, + DovishV3Provider, + DyorV2Provider, + ElkProvider, + EnosysProvider, + GravityFinanceProvider, + HoneySwapProvider, + HyperBlastProvider, + JetSwapProvider, + KinetixV2Provider, + KinetixV3Provider, + LaserSwapV2Provider, + LynexV1Provider, + LynexV2Provider, + MonoswapV2Provider, + MonoswapV3Provider, + MSwapProvider, + NetSwapProvider, + PancakeSwapV2Provider, + PancakeSwapV3Provider, + QuickSwapV2Provider, + QuickSwapV3Provider, + SolarbeamProvider, + SparkDexV2Provider, + SparkDexV3Provider, + SparkDexV3_1Provider, + SpookySwapV2Provider, + SpookySwapV3Provider, + SushiSwapV2Provider, + SushiSwapV3Provider, + SwapBlastProvider, + ThrusterV2_1Provider, + ThrusterV2_3Provider, + ThrusterV3Provider, + TraderJoeProvider, + UbeSwapProvider, + UniswapV2Provider, + UniswapV3Provider, + VVSStandardProvider, + WagmiProvider, + ].forEach((p) => { + try { + const provider = new p(this.chainId, this.web3Client) + if ( + // If none passed, include all + !providers || + this._providerIsIncluded(provider.getType(), providers) + ) { + this.providers.push(provider) + // gather eventsAbi unique instances + if (provider?.eventsAbi?.length) { + ;(provider.eventsAbi as any[]).forEach((v) => { + if (this.eventsAbi.every((e) => !isDeepStrictEqual(e, v))) { + this.eventsAbi.push(v) + } + }) + } + } + } catch (_e: unknown) {} + }) + } + + override async fetchPoolsForToken( + currency0: Type, + currency1: Type, + excludePools?: Set, + options?: DataFetcherOptions, + ): Promise { + let opts = options + if (!opts) { + opts = { + blockNumber: await this.web3Client.getBlockNumber(), + } + } + if (typeof opts.blockNumber !== 'bigint') { + opts.blockNumber = await this.web3Client.getBlockNumber() + } + await super.fetchPoolsForToken(currency0, currency1, excludePools, opts) + } + + // updates the pool data of all dexes by enevts until the given block + async updatePools(untilBlock?: bigint) { + let fromBlock = -1n + const addresses: string[] = [] + if (typeof untilBlock !== 'bigint') { + untilBlock = await this.web3Client.getBlockNumber() + } + + // gather all provider factory and pools addresses + this.providers.forEach((provider: any) => { + if ( + provider instanceof RainUniswapV2BaseProvider || + provider instanceof RainUniswapV3BaseProvider + ) { + const factory = + provider.factory[ + this.chainId as keyof typeof provider.factory + ]!.toLowerCase() + if (!addresses.includes(factory)) { + addresses.push(factory) + } + const pools = provider.pools + pools.forEach((pool, address) => { + if (!addresses.includes(address)) { + addresses.push(address) + } + if (fromBlock === -1n) { + fromBlock = pool.blockNumber + } + if (pool.blockNumber < fromBlock) { + fromBlock = pool.blockNumber + } + }) + } + }) + if (!addresses.length) return + if (fromBlock === untilBlock) return + + // get logs and sort them from earliest block to latest + const logs = await this.web3Client.getLogs({ + events: this.eventsAbi, + address: addresses as `0x${string}`[], + fromBlock, + toBlock: untilBlock, + }) + logs.sort((a, b) => { + const diff = a.blockNumber - b.blockNumber + if (diff === 0n) { + return a.logIndex - b.logIndex + } else { + return Number(diff) + } + }) + + // process each log for each provider + logs.forEach((log) => { + this.providers.forEach((p) => { + p.processLog(log) + }) + }) + const results = await Promise.allSettled( + this.providers.map((p) => p.afterProcessLog(untilBlock!)), + ) + results.forEach((res, i) => { + if (res.status === 'fulfilled') { + const provider = this.providers[i] + if ( + provider instanceof RainUniswapV2BaseProvider || + provider instanceof RainUniswapV3BaseProvider + ) { + provider.pools.forEach((pool) => { + pool.blockNumber = untilBlock! + }) + } + } + }) + } +} diff --git a/packages/sushi/src/router/rain/rain.test.ts b/packages/sushi/src/router/rain/rain.test.ts new file mode 100644 index 0000000000..4024d0fef1 --- /dev/null +++ b/packages/sushi/src/router/rain/rain.test.ts @@ -0,0 +1,124 @@ +import { describe, expect, it } from 'vitest' +import { ChainId } from '../../chain/constants.js' +import { ROUTE_PROCESSOR_4_ADDRESS } from '../../config/route-processor.js' +import { USDC, USDT } from '../../currency/tokens.js' +import { LiquidityProviders } from '../liquidity-providers/index.js' +import { Router } from '../router.js' +import { RainDataFetcher } from './rain-data-fetcher.js' + +describe('Rain fork tests', async () => { + it('should correctly update pools data by logs', async () => { + // 2000 blocks apart + const currentBlockNumber = 64818756n + const oldBlockNumber = 64816756n + const fromToken = USDT[ChainId.POLYGON] + const toToken = USDC[ChainId.POLYGON] + const amountIn = 10_000_000n // 10e6, ie 10 USDT + const gasPrice = 30_000_000 + // one of each type + const lps = [ + LiquidityProviders.UniswapV3, // univ3 + LiquidityProviders.QuickSwapV3, // algebra + LiquidityProviders.QuickSwapV2, // univ2 + ] + + // data fetcher without indexer (normal way ie raw pool data fecthed directly by contract calls) + const dataFectherFresh = new RainDataFetcher(ChainId.POLYGON) + dataFectherFresh.startDataFetching(lps) + dataFectherFresh.stopDataFetching() + + // data fetcher with indexer (pool data will be updated from older block to current block by contract logs) + const dataFectherIndexer = new RainDataFetcher(ChainId.POLYGON) + dataFectherIndexer.startDataFetching(lps) + dataFectherIndexer.stopDataFetching() + + // get token pools data for current block on fresh dataFetcher + await dataFectherFresh.fetchPoolsForToken(fromToken, toToken, undefined, { + blockNumber: currentBlockNumber, + }) + const freshPcMap = dataFectherFresh.getCurrentPoolCodeMap( + fromToken, + toToken, + ) + const freshRoute = Router.findBestRoute( + freshPcMap, + ChainId.POLYGON, + fromToken, + amountIn, + toToken, + gasPrice, + lps, + undefined, + undefined, + 'single', + ) + + // get token pools data for older block on indexer dataFetcher, then ensure the + // amount out it gives is different to fresh datafetcher amount out + // and then update the pools data with contract logs from between the blocks + await dataFectherIndexer.fetchPoolsForToken(fromToken, toToken, undefined, { + blockNumber: oldBlockNumber, + }) + const indexerPcMapOld = dataFectherIndexer.getCurrentPoolCodeMap( + fromToken, + toToken, + ) + const indexerRouteOld = Router.findBestRoute( + indexerPcMapOld, + ChainId.POLYGON, + fromToken, + amountIn, + toToken, + gasPrice, + lps, + undefined, + undefined, + 'single', + ) + // should not be equal (just to make sure) + expect(freshRoute.amountOutBI).not.equal(indexerRouteOld.amountOutBI) + + // now update the indexer pools data with logs + await dataFectherIndexer.updatePools(currentBlockNumber) + const indexerPcMap = dataFectherIndexer.getCurrentPoolCodeMap( + fromToken, + toToken, + ) + const indexerRoute = Router.findBestRoute( + indexerPcMap, + ChainId.POLYGON, + fromToken, + amountIn, + toToken, + gasPrice, + lps, + undefined, + undefined, + 'single', + ) + + // assert amount outs and status + expect(freshRoute.amountOutBI).toEqual(indexerRoute.amountOutBI) + expect(freshRoute.totalAmountOutBI).toEqual(indexerRoute.totalAmountOutBI) + expect(freshRoute.status).toEqual(indexerRoute.status) + + // assert produced route code + const freshRpParams = Router.routeProcessor4Params( + freshPcMap, + freshRoute, + fromToken, + toToken, + ROUTE_PROCESSOR_4_ADDRESS[ChainId.POLYGON], + ROUTE_PROCESSOR_4_ADDRESS[ChainId.POLYGON], + ) + const indexerRpParams = Router.routeProcessor4Params( + indexerPcMap, + indexerRoute, + fromToken, + toToken, + ROUTE_PROCESSOR_4_ADDRESS[ChainId.POLYGON], + ROUTE_PROCESSOR_4_ADDRESS[ChainId.POLYGON], + ) + expect(freshRpParams.routeCode).toEqual(indexerRpParams.routeCode) + }) +})