From 4e32b68080eec3ee41d1f20318b8922f444f9b00 Mon Sep 17 00:00:00 2001 From: chrisli30 Date: Wed, 12 Aug 2026 18:55:00 -0700 Subject: [PATCH] feat: add Unichain (130) Uniswap V3 + WETH + native USDC Wave B catalog: Chains.UnichainMainnet and official Uniswap v3 deployments plus Circle USDC. Optimism was already in 0.10.0. See avs-infra docs/changes/20260813-chain-expansion-wave-b.md. --- .changeset/unichain-uniswap-weth.md | 5 +++++ README.md | 4 ++-- src/chains.ts | 1 + src/protocols/uniswap-v3.ts | 10 ++++++++++ src/protocols/wrapped.ts | 1 + tests/catalog.test.ts | 13 +++++++++++++ 6 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 .changeset/unichain-uniswap-weth.md diff --git a/.changeset/unichain-uniswap-weth.md b/.changeset/unichain-uniswap-weth.md new file mode 100644 index 0000000..2004b5d --- /dev/null +++ b/.changeset/unichain-uniswap-weth.md @@ -0,0 +1,5 @@ +--- +"@avaprotocol/protocols": minor +--- + +Add Unichain (130) Uniswap V3 (SwapRouter02 / QuoterV2 / Factory / NFT Position Manager / Permit2 / Universal Router) plus `wrapped.weth` and native Circle USDC. Addresses from the official Uniswap Unichain deployment page and Circle USDC docs. diff --git a/README.md b/README.md index 4421b65..99a7b05 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,8 @@ const sig = Protocols.aaveV3.eventTopics.Borrow; | **Sky (sDAI)** | sDAI vault + standard ERC-4626 ABI | Mainnet | | **Spark** | SparkLend Pool (AAVE V3 fork — reuse AAVE Pool ABI) | Mainnet | | **Superfluid** | CFAv1Forwarder + setFlowrate/createFlow ABI | Mainnet, Base, **BNB** | -| **Uniswap V3** | SwapRouter02, QuoterV2, Permit2, Factory, NFT Position Manager, Universal Router + ABIs | Mainnet, Sepolia, **Optimism**, Base, Base Sepolia, **BNB**, **Arbitrum** | -| **Wrapped Ether** | Canonical wrapper of native gas + WETH9 ABI (WBNB on BNB) | Mainnet, Sepolia, **Optimism**, Base, Base Sepolia, **BNB**, **Arbitrum** | +| **Uniswap V3** | SwapRouter02, QuoterV2, Permit2, Factory, NFT Position Manager, Universal Router + ABIs | Mainnet, Sepolia, **Optimism**, Base, Base Sepolia, **BNB**, **Arbitrum**, **Unichain** | +| **Wrapped Ether** | Canonical wrapper of native gas + WETH9 ABI (WBNB on BNB) | Mainnet, Sepolia, **Optimism**, Base, Base Sepolia, **BNB**, **Arbitrum**, **Unichain** | | **ERC-20** | Standard `approve` ABI fragment | n/a | Shared ABIs (consumed by multiple protocol modules): diff --git a/src/chains.ts b/src/chains.ts index 3cdbb05..f3238c3 100644 --- a/src/chains.ts +++ b/src/chains.ts @@ -12,6 +12,7 @@ export const Chains = Object.freeze({ Sepolia: 11_155_111 as const, Holesky: 17_000 as const, OptimismMainnet: 10 as const, + UnichainMainnet: 130 as const, BaseMainnet: 8453 as const, BaseSepolia: 84_532 as const, BnbMainnet: 56 as const, diff --git a/src/protocols/uniswap-v3.ts b/src/protocols/uniswap-v3.ts index 008f1b6..0cbfd4d 100644 --- a/src/protocols/uniswap-v3.ts +++ b/src/protocols/uniswap-v3.ts @@ -26,6 +26,8 @@ const swapRouter02: AddressByChain = { [Chains.BnbMainnet]: "0xB971eF87ede563556b2ED4b1C0b0019111Dd85d2", // https://developers.uniswap.org/contracts/v3/reference/deployments/arbitrum-deployments [Chains.ArbitrumOne]: "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", + // https://developers.uniswap.org/docs/protocols/v3/deployments/v3-unichain-deployments + [Chains.UnichainMainnet]: "0x73855d06DE49d0fe4A9c42636Ba96c62da12FF9C", }; /** QuoterV2 — off-chain quote helper for swap previews. */ @@ -37,6 +39,7 @@ const quoterV2: AddressByChain = { [Chains.OptimismMainnet]: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", [Chains.BnbMainnet]: "0x78D78E420Da98ad378D7799bE8f4AF69033EB077", [Chains.ArbitrumOne]: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e", + [Chains.UnichainMainnet]: "0x385A5cf5F83e99f7BB2852b6A19C3538b9FA7658", }; /** @@ -51,6 +54,7 @@ const permit2: AddressByChain = { [Chains.OptimismMainnet]: "0x000000000022d473030F116dDEE9F6B43aC78BA3", [Chains.BnbMainnet]: "0x000000000022d473030F116dDEE9F6B43aC78BA3", [Chains.ArbitrumOne]: "0x000000000022d473030F116dDEE9F6B43aC78BA3", + [Chains.UnichainMainnet]: "0x000000000022d473030F116dDEE9F6B43aC78BA3", }; /** Uniswap V3 Factory — derives the deterministic pool address per token-pair+fee. */ @@ -62,6 +66,7 @@ const factory: AddressByChain = { [Chains.OptimismMainnet]: "0x1F98431c8aD98523631AE4a59f267346ea31F984", [Chains.BnbMainnet]: "0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7", [Chains.ArbitrumOne]: "0x1F98431c8aD98523631AE4a59f267346ea31F984", + [Chains.UnichainMainnet]: "0x1F98400000000000000000000000000000000003", }; /** NonfungiblePositionManager — LP NFT mint/burn/collect. */ @@ -73,6 +78,7 @@ const nonfungiblePositionManager: AddressByChain = { [Chains.OptimismMainnet]: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", [Chains.BnbMainnet]: "0x7b8A01B39D58278b5DE7e48c8449c9f4F5170613", [Chains.ArbitrumOne]: "0xC36442b4a4522E871399CD717aBDD847Ab11FE88", + [Chains.UnichainMainnet]: "0x943e6e07a7E8E791dAFC44083e54041D743C46E9", }; /** UniversalRouter — Uniswap's multi-step routing entrypoint (Permit2-aware). */ @@ -84,6 +90,7 @@ const universalRouter: AddressByChain = { [Chains.OptimismMainnet]: "0x851116d9223fabed8e56c0e6b8ad0c31d98b3507", [Chains.BnbMainnet]: "0x4Dae2f939ACf50408e13d58534Ff8c2776d45265", [Chains.ArbitrumOne]: "0xa51afafe0263b40edaef0df8781ea9aa03e381a3", + [Chains.UnichainMainnet]: "0xe6039aE5B44f90d56c6B029354Fb22999861A9A0", }; /** @@ -217,6 +224,7 @@ const tokens = Object.freeze({ [Chains.OptimismMainnet]: "0x4200000000000000000000000000000000000006", [Chains.BnbMainnet]: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", [Chains.ArbitrumOne]: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + [Chains.UnichainMainnet]: "0x4200000000000000000000000000000000000006", } satisfies AddressByChain, USDC: { [Chains.Sepolia]: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", @@ -227,6 +235,8 @@ const tokens = Object.freeze({ [Chains.BnbMainnet]: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", // Native Circle USDC (not bridged USDC.e). [Chains.ArbitrumOne]: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", + // Native Circle USDC (https://developers.circle.com/stablecoins/usdc-on-unichain). + [Chains.UnichainMainnet]: "0x078D782b760474a361dDA0AF3839290b0EF57AD6", } satisfies AddressByChain, }); diff --git a/src/protocols/wrapped.ts b/src/protocols/wrapped.ts index 0514c65..66393cd 100644 --- a/src/protocols/wrapped.ts +++ b/src/protocols/wrapped.ts @@ -24,6 +24,7 @@ const weth: AddressByChain = { [Chains.OptimismMainnet]: "0x4200000000000000000000000000000000000006", [Chains.BnbMainnet]: "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", [Chains.ArbitrumOne]: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + [Chains.UnichainMainnet]: "0x4200000000000000000000000000000000000006", }; /** diff --git a/tests/catalog.test.ts b/tests/catalog.test.ts index 60f8401..68d597e 100644 --- a/tests/catalog.test.ts +++ b/tests/catalog.test.ts @@ -321,6 +321,18 @@ describe("Uniswap V3 catalog", () => { expect(Protocols.uniswapV3.swapRouter02[Chains.OptimismMainnet]).toBe( "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45", ); + expect(Protocols.uniswapV3.swapRouter02[Chains.UnichainMainnet]).toBe( + "0x73855d06DE49d0fe4A9c42636Ba96c62da12FF9C", + ); + expect(Protocols.uniswapV3.factory[Chains.UnichainMainnet]).toBe( + "0x1F98400000000000000000000000000000000003", + ); + expect(Protocols.uniswapV3.tokens.USDC[Chains.UnichainMainnet]).toBe( + "0x078D782b760474a361dDA0AF3839290b0EF57AD6", + ); + expect(Protocols.wrapped.weth[Chains.UnichainMainnet]).toBe( + "0x4200000000000000000000000000000000000006", + ); }); it("ships exactInputSingle in the SwapRouter02 ABI", () => { @@ -339,6 +351,7 @@ describe("Uniswap V3 catalog", () => { Chains.OptimismMainnet, Chains.BnbMainnet, Chains.ArbitrumOne, + Chains.UnichainMainnet, ]) { expect(Protocols.uniswapV3.permit2[chainId]?.toLowerCase()).toBe(expected.toLowerCase()); }