diff --git a/bin/app.ts b/bin/app.ts index d7099fb22..81135d5e4 100644 --- a/bin/app.ts +++ b/bin/app.ts @@ -186,7 +186,8 @@ export class RoutingAPIPipeline extends Stack { chainId !== ChainId.WORLDCHAIN && chainId !== ChainId.UNICHAIN_SEPOLIA && chainId !== ChainId.MONAD_TESTNET && - chainId !== ChainId.BASE_SEPOLIA + chainId !== ChainId.BASE_SEPOLIA && + chainId !== ChainId.UNICHAIN ) { const key = `WEB3_RPC_${chainId}` jsonRpcProviders[key] = jsonRpcProvidersSecret.secretValueFromJson(key).toString() diff --git a/lib/config/rpcProviderProdConfig.json b/lib/config/rpcProviderProdConfig.json index 3539dc776..99f304402 100644 --- a/lib/config/rpcProviderProdConfig.json +++ b/lib/config/rpcProviderProdConfig.json @@ -143,5 +143,14 @@ "providerInitialWeights": [1], "providerUrls": ["UNIRPC_0"], "providerNames": ["UNIRPC"] + }, + { + "chainId": 130, + "useMultiProviderProb": 1, + "latencyEvaluationSampleProb": 0, + "healthCheckSampleProb": 0, + "providerInitialWeights": [1], + "providerUrls": ["UNIRPC_0"], + "providerNames": ["UNIRPC"] } ] diff --git a/lib/cron/cache-config.ts b/lib/cron/cache-config.ts index 393019eb2..c09e18459 100644 --- a/lib/cron/cache-config.ts +++ b/lib/cron/cache-config.ts @@ -45,6 +45,8 @@ export const v3SubgraphUrlOverride = (chainId: ChainId) => { return `https://subgraph.satsuma-prod.com/${process.env.ALCHEMY_QUERY_KEY}/uniswap/uniswap-v3-worldchain/api` case ChainId.UNICHAIN_SEPOLIA: return `https://subgraph.satsuma-prod.com/${process.env.ALCHEMY_QUERY_KEY}/uniswap/uniswap-v3-astrochain-sepolia/api` + case ChainId.UNICHAIN: + return `https://subgraph.satsuma-prod.com/${process.env.ALCHEMY_QUERY_KEY}/uniswap/uniswap-v3-unichain/api` default: return undefined } @@ -74,6 +76,8 @@ export const v2SubgraphUrlOverride = (chainId: ChainId) => { return `https://subgraph.satsuma-prod.com/${process.env.ALCHEMY_QUERY_KEY}/uniswap/uniswap-v2-astrochain-sepolia/api` case ChainId.MONAD_TESTNET: return `https://subgraph.satsuma-prod.com/${process.env.ALCHEMY_QUERY_KEY}/uniswap/uniswap-v2-monad-testnet/api` + case ChainId.UNICHAIN: + return `https://subgraph.satsuma-prod.com/${process.env.ALCHEMY_QUERY_KEY}/uniswap/uniswap-v2-unichain/api` default: return undefined } diff --git a/lib/dashboards/rpc-providers-widgets-factory.ts b/lib/dashboards/rpc-providers-widgets-factory.ts index bc2b92645..027471af1 100644 --- a/lib/dashboards/rpc-providers-widgets-factory.ts +++ b/lib/dashboards/rpc-providers-widgets-factory.ts @@ -23,6 +23,7 @@ const ID_TO_PROVIDER = (id: ChainId): string => { case ChainId.BASE_SEPOLIA: case ChainId.UNICHAIN_SEPOLIA: case ChainId.MONAD_TESTNET: + case ChainId.UNICHAIN: return ProviderName.QUIKNODE case ChainId.CELO_ALFAJORES: return ProviderName.FORNO diff --git a/lib/graphql/graphql-provider.ts b/lib/graphql/graphql-provider.ts index ba628f912..a8fc8b125 100644 --- a/lib/graphql/graphql-provider.ts +++ b/lib/graphql/graphql-provider.ts @@ -65,6 +65,8 @@ export class UniGraphQLProvider implements IUniGraphQLProvider { return 'UNICHAIN_SEPOLIA' case ChainId.MONAD_TESTNET: return 'MONAD_TESTNET' + case ChainId.UNICHAIN: + return 'UNICHAIN' default: throw new Error(`UniGraphQLProvider._chainIdToGraphQLChainName unsupported ChainId: ${chainId}`) } diff --git a/lib/handlers/injector-sor.ts b/lib/handlers/injector-sor.ts index 14f9cbc47..3f92eb4c9 100644 --- a/lib/handlers/injector-sor.ts +++ b/lib/handlers/injector-sor.ts @@ -111,6 +111,7 @@ export const SUPPORTED_CHAINS: ChainId[] = [ ChainId.UNICHAIN_SEPOLIA, ChainId.MONAD_TESTNET, ChainId.BASE_SEPOLIA, + ChainId.UNICHAIN, ] const DEFAULT_TOKEN_LIST = 'https://gateway.ipfs.io/ipns/tokens.uniswap.org' @@ -371,6 +372,7 @@ export abstract class InjectorSOR extends Injector< case ChainId.UNICHAIN_SEPOLIA: case ChainId.MONAD_TESTNET: case ChainId.BASE_SEPOLIA: + case ChainId.UNICHAIN: const currentQuoteProvider = new OnChainQuoteProvider( chainId, provider, @@ -512,6 +514,7 @@ export abstract class InjectorSOR extends Injector< ChainId.BLAST, ChainId.WORLDCHAIN, ChainId.MONAD_TESTNET, + ChainId.UNICHAIN, ] const v4Supported = [ diff --git a/lib/handlers/quote/util/quote-provider-traffic-switch-configuration.ts b/lib/handlers/quote/util/quote-provider-traffic-switch-configuration.ts index 48e1aec65..881730021 100644 --- a/lib/handlers/quote/util/quote-provider-traffic-switch-configuration.ts +++ b/lib/handlers/quote/util/quote-provider-traffic-switch-configuration.ts @@ -232,6 +232,7 @@ export const QUOTE_PROVIDER_TRAFFIC_SWITCH_CONFIGURATION = ( case ChainId.UNICHAIN_SEPOLIA: case ChainId.BASE_SEPOLIA: case ChainId.MONAD_TESTNET: + case ChainId.UNICHAIN: switch (protocol) { case Protocol.MIXED: case Protocol.V4: diff --git a/lib/handlers/shared.ts b/lib/handlers/shared.ts index f6e069ec4..431e13464 100644 --- a/lib/handlers/shared.ts +++ b/lib/handlers/shared.ts @@ -85,6 +85,7 @@ export const DEFAULT_ROUTING_CONFIG_BY_CHAIN = (chainId: ChainId): AlphaRouterCo case ChainId.UNICHAIN_SEPOLIA: case ChainId.MONAD_TESTNET: case ChainId.BASE_SEPOLIA: + case ChainId.UNICHAIN: return { v2PoolSelection: { topN: 3, diff --git a/lib/rpc/utils.ts b/lib/rpc/utils.ts index 52665bcad..5a881a8f8 100644 --- a/lib/rpc/utils.ts +++ b/lib/rpc/utils.ts @@ -26,6 +26,8 @@ export function chainIdToNetworkName(networkId: ChainId): string { return 'monad-testnet' case ChainId.BASE_SEPOLIA: return 'base-sepolia' + case ChainId.UNICHAIN: + return 'unichain' default: return 'ethereum' } @@ -109,6 +111,9 @@ export function generateProviderUrl(key: string, value: string, chainId: number) // URL contains unichain-sepolia.quiknode.pro, we had to not disclose prior to the unichain annouce return `${tokens[0]}` } + case 'QUICKNODE_130': { + return `https://${tokens[0]}.unichain-mainnet.quiknode.pro/${tokens[1]}` + } case 'QUICKNODE_480': { return `https://${tokens[0]}.worldchain-mainnet.quiknode.pro/${tokens[1]}` } diff --git a/lib/util/onChainQuoteProviderConfigs.ts b/lib/util/onChainQuoteProviderConfigs.ts index 4fb48c29b..05c200161 100644 --- a/lib/util/onChainQuoteProviderConfigs.ts +++ b/lib/util/onChainQuoteProviderConfigs.ts @@ -62,6 +62,11 @@ export const RETRY_OPTIONS: { [chainId: number]: AsyncRetry.Options | undefined minTimeout: 100, maxTimeout: 1000, }, + [ChainId.UNICHAIN]: { + retries: 2, + minTimeout: 100, + maxTimeout: 1000, + }, } export const OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol]: { [chainId: number]: BatchParams } } = { @@ -125,7 +130,7 @@ export const OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol]: { gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, - // TODO: once astrochain-sepolia has view-quoter, optimize muilcallChunk and gasLimitPerCall + // TODO: once unichain-sepolia has view-quoter, optimize muilcallChunk and gasLimitPerCall [ChainId.UNICHAIN_SEPOLIA]: { multicallChunk: 80, gasLimitPerCall: 1_200_000, @@ -142,6 +147,12 @@ export const OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol]: { gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, + // TODO: once unichain has view-quoter, optimize muilcallChunk and gasLimitPerCall + [ChainId.UNICHAIN]: { + multicallChunk: 80, + gasLimitPerCall: 1_200_000, + quoteMinSuccessRate: 0.1, + }, }, [Protocol.V3]: { ...constructSameBatchParamsMap(DEFAULT_BATCH_PARAMS), @@ -201,7 +212,7 @@ export const OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol]: { gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, - // TODO: once astrochain-sepolia has view-quoter, optimize muilcallChunk and gasLimitPerCall + // TODO: once unichain-sepolia has view-quoter, optimize muilcallChunk and gasLimitPerCall [ChainId.UNICHAIN_SEPOLIA]: { multicallChunk: 80, gasLimitPerCall: 1_200_000, @@ -218,6 +229,12 @@ export const OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol]: { gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, + // TODO: once unichain has view-quoter, optimize muilcallChunk and gasLimitPerCall + [ChainId.UNICHAIN]: { + multicallChunk: 80, + gasLimitPerCall: 1_200_000, + quoteMinSuccessRate: 0.1, + }, }, // V4 can be the same as V4 to begin. likely v4 is more gas efficient because of pool singleton for swaps by accounting mechanism [Protocol.V4]: { @@ -278,7 +295,7 @@ export const OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol]: { gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, - // TODO: once astrochain-sepolia has view-quoter, optimize muilcallChunk and gasLimitPerCall + // TODO: once unichain-sepolia has view-quoter, optimize muilcallChunk and gasLimitPerCall [ChainId.UNICHAIN_SEPOLIA]: { multicallChunk: 80, gasLimitPerCall: 1_200_000, @@ -295,6 +312,12 @@ export const OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol]: { gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, + // TODO: once unichain has view-quoter, optimize muilcallChunk and gasLimitPerCall + [ChainId.UNICHAIN]: { + multicallChunk: 80, + gasLimitPerCall: 1_200_000, + quoteMinSuccessRate: 0.1, + }, }, [Protocol.MIXED]: { ...constructSameBatchParamsMap(DEFAULT_BATCH_PARAMS), @@ -366,6 +389,12 @@ export const OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol]: { gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, + // TODO: once unichain has view-quoter, optimize muilcallChunk and gasLimitPerCall + [ChainId.UNICHAIN]: { + multicallChunk: 80, + gasLimitPerCall: 1_200_000, + quoteMinSuccessRate: 0.1, + }, }, } @@ -448,6 +477,12 @@ export const NON_OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol] gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, + // TODO: once unichain has view-quoter, optimize muilcallChunk and gasLimitPerCall + [ChainId.UNICHAIN]: { + multicallChunk: 80, + gasLimitPerCall: 1_200_000, + quoteMinSuccessRate: 0.1, + }, }, [Protocol.V3]: { ...constructSameBatchParamsMap(DEFAULT_BATCH_PARAMS), @@ -524,6 +559,12 @@ export const NON_OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol] gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, + // TODO: once unichain has view-quoter, optimize muilcallChunk and gasLimitPerCall + [ChainId.UNICHAIN]: { + multicallChunk: 80, + gasLimitPerCall: 1_200_000, + quoteMinSuccessRate: 0.1, + }, }, // V4 can be the same as V4 to begin. likely v4 is more gas efficient because of pool singleton for swaps by accounting mechanism [Protocol.V4]: { @@ -601,6 +642,12 @@ export const NON_OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol] gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, + // TODO: once unichain has view-quoter, optimize muilcallChunk and gasLimitPerCall + [ChainId.UNICHAIN]: { + multicallChunk: 80, + gasLimitPerCall: 1_200_000, + quoteMinSuccessRate: 0.1, + }, }, [Protocol.MIXED]: { ...constructSameBatchParamsMap(DEFAULT_BATCH_PARAMS), @@ -661,6 +708,12 @@ export const NON_OPTIMISTIC_CACHED_ROUTES_BATCH_PARAMS: { [protocol in Protocol] gasLimitPerCall: 1_200_000, quoteMinSuccessRate: 0.1, }, + // TODO: once unichain has view-quoter, optimize muilcallChunk and gasLimitPerCall + [ChainId.UNICHAIN]: { + multicallChunk: 80, + gasLimitPerCall: 1_200_000, + quoteMinSuccessRate: 0.1, + }, // TODO: once monad-testnet has view-quoter, optimize muilcallChunk and gasLimitPerCall [ChainId.MONAD_TESTNET]: { multicallChunk: 80, @@ -709,6 +762,10 @@ export const GAS_ERROR_FAILURE_OVERRIDES: { [chainId: number]: FailureOverrides gasLimitOverride: 3_000_000, multicallChunk: 45, }, + [ChainId.UNICHAIN]: { + gasLimitOverride: 3_000_000, + multicallChunk: 45, + }, [ChainId.MONAD_TESTNET]: { gasLimitOverride: 3_000_000, multicallChunk: 45, @@ -761,6 +818,10 @@ export const SUCCESS_RATE_FAILURE_OVERRIDES: { [chainId: number]: FailureOverrid gasLimitOverride: 3_000_000, multicallChunk: 45, }, + [ChainId.UNICHAIN]: { + gasLimitOverride: 3_000_000, + multicallChunk: 45, + }, } export const BLOCK_NUMBER_CONFIGS: { [chainId: number]: BlockNumberConfig } = { @@ -829,6 +890,14 @@ export const BLOCK_NUMBER_CONFIGS: { [chainId: number]: BlockNumberConfig } = { rollbackBlockOffset: -20, }, }, + [ChainId.UNICHAIN]: { + baseBlockOffset: -25, + rollback: { + enabled: true, + attemptsBeforeRollback: 1, + rollbackBlockOffset: -20, + }, + }, } // block -1 means it's never deployed diff --git a/package-lock.json b/package-lock.json index 88534d26f..d9f1c6844 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "@uniswap/permit2-sdk": "^1.3.0", "@uniswap/router-sdk": "^1.20.0", "@uniswap/sdk-core": "^7.3.0", - "@uniswap/smart-order-router": "4.16.1", + "@uniswap/smart-order-router": "4.17.0", "@uniswap/token-lists": "^1.0.0-beta.33", "@uniswap/universal-router-sdk": "^4.13.0", "@uniswap/v2-sdk": "^4.11.0", @@ -4508,9 +4508,9 @@ } }, "node_modules/@uniswap/smart-order-router": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@uniswap/smart-order-router/-/smart-order-router-4.16.1.tgz", - "integrity": "sha512-NyuVEjsNULde1MA0qAHf5+qcG7mpkr05iV5DYL24rp8gauwU/4bayUYDCI0TVi9nFLmnKQ5NPUS8xAEsN4Vu0w==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@uniswap/smart-order-router/-/smart-order-router-4.17.0.tgz", + "integrity": "sha512-ryDn87jOnhYsiRJAuOmxpIaRvd/l9a3KK91SCw7x/22T5TLipQoSlrzS6vWdJ2TTf8yzGLrXRMZnWSKCcMxjJA==", "dependencies": { "@eth-optimism/sdk": "^3.2.2", "@types/brotli": "^1.3.4", @@ -27936,9 +27936,9 @@ } }, "@uniswap/smart-order-router": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@uniswap/smart-order-router/-/smart-order-router-4.16.1.tgz", - "integrity": "sha512-NyuVEjsNULde1MA0qAHf5+qcG7mpkr05iV5DYL24rp8gauwU/4bayUYDCI0TVi9nFLmnKQ5NPUS8xAEsN4Vu0w==", + "version": "4.17.0", + "resolved": "https://registry.npmjs.org/@uniswap/smart-order-router/-/smart-order-router-4.17.0.tgz", + "integrity": "sha512-ryDn87jOnhYsiRJAuOmxpIaRvd/l9a3KK91SCw7x/22T5TLipQoSlrzS6vWdJ2TTf8yzGLrXRMZnWSKCcMxjJA==", "requires": { "@eth-optimism/sdk": "^3.2.2", "@types/brotli": "^1.3.4", diff --git a/package.json b/package.json index 2e702d8bb..3a7de12f6 100644 --- a/package.json +++ b/package.json @@ -88,7 +88,7 @@ "@uniswap/permit2-sdk": "^1.3.0", "@uniswap/router-sdk": "^1.20.0", "@uniswap/sdk-core": "^7.3.0", - "@uniswap/smart-order-router": "4.16.1", + "@uniswap/smart-order-router": "4.17.0", "@uniswap/token-lists": "^1.0.0-beta.33", "@uniswap/universal-router-sdk": "^4.13.0", "@uniswap/v2-sdk": "^4.11.0", diff --git a/test/mocha/e2e/quote.test.ts b/test/mocha/e2e/quote.test.ts index 90ede830d..3200673c9 100644 --- a/test/mocha/e2e/quote.test.ts +++ b/test/mocha/e2e/quote.test.ts @@ -3045,7 +3045,8 @@ describe('quote', function () { chain === ChainId.BLAST || chain === ChainId.ZORA || chain === ChainId.ZKSYNC || - chain === ChainId.UNICHAIN_SEPOLIA + chain === ChainId.UNICHAIN_SEPOLIA || + chain === ChainId.UNICHAIN ) { // Blast doesn't have DAI or USDC yet // Zora doesn't have DAI diff --git a/test/utils/tokens.ts b/test/utils/tokens.ts index 4fe791ef4..f4f84ff9d 100644 --- a/test/utils/tokens.ts +++ b/test/utils/tokens.ts @@ -38,6 +38,7 @@ import { USDT_OPTIMISM, WRAPPED_NATIVE_CURRENCY, USDC_BASE_SEPOLIA, + USDC_UNICHAIN, } from '@uniswap/smart-order-router' import { ethers } from 'ethers' import NodeCache from 'node-cache' @@ -150,6 +151,8 @@ export const USDC_ON = (chainId: ChainId): Token => { return USDC_UNICHAIN_SEPOLIA case ChainId.BASE_SEPOLIA: return USDC_BASE_SEPOLIA + case ChainId.UNICHAIN: + return USDC_UNICHAIN default: throw new Error(`Chain id: ${chainId} not supported`) }