diff --git a/src/api/address-resolver/UniswapV1Resolver.ts b/src/api/address-resolver/UniswapV1Resolver.ts new file mode 100644 index 0000000..a8cb2e0 --- /dev/null +++ b/src/api/address-resolver/UniswapV1Resolver.ts @@ -0,0 +1,60 @@ +import { BaseProvider } from "@ethersproject/providers"; +import { Contract } from "@ethersproject/contracts"; +import { IAddressResolver } from "./address-resolver"; +import { ChecksummedAddress, TokenMeta } from "../../types"; +import { ERCTokenResolver } from "./ERCTokenResolver"; + +const UNISWAP_V1_FACTORY = "0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95"; + +const UNISWAP_V1_FACTORY_ABI = [ + "function getToken(address exchange) external view returns (address token)", +]; + +const NULL_ADDRESS = "0x0000000000000000000000000000000000000000"; + +export type UniswapV1TokenMeta = { + address: ChecksummedAddress; +} & TokenMeta; + +export type UniswapV1PairMeta = { + exchange: ChecksummedAddress; + token: UniswapV1TokenMeta; +}; + +const ercResolver = new ERCTokenResolver(); + +export class UniswapV1Resolver implements IAddressResolver { + async resolveAddress( + provider: BaseProvider, + address: string + ): Promise { + const factoryContract = new Contract( + UNISWAP_V1_FACTORY, + UNISWAP_V1_FACTORY_ABI, + provider + ); + + try { + // First, probe the getToken() function; if it responds with an UniswapV1 exchange + // address, it is a LP + const token = (await factoryContract.getToken(address)) as string; + if (token === NULL_ADDRESS) { + return undefined; + } + + const metadata = await ercResolver.resolveAddress(provider, token); + if (metadata === undefined) { + return undefined; + } + + return { + exchange: address, + token: { address: token, ...metadata }, + }; + } catch (err) { + // Ignore on purpose; this indicates the probe failed and the address + // is not a token + } + return undefined; + } +} diff --git a/src/api/address-resolver/index.ts b/src/api/address-resolver/index.ts index ba96106..5899231 100644 --- a/src/api/address-resolver/index.ts +++ b/src/api/address-resolver/index.ts @@ -2,6 +2,7 @@ import { BaseProvider } from "@ethersproject/providers"; import { ensRenderer } from "../../components/ENSName"; import { plainStringRenderer } from "../../components/PlainString"; import { tokenRenderer } from "../../components/TokenName"; +import { uniswapV1PairRenderer } from "../../components/UniswapV1ExchangeName"; import { uniswapV2PairRenderer } from "../../components/UniswapV2PairName"; import { IAddressResolver, ResolvedAddressRenderer } from "./address-resolver"; import { @@ -9,6 +10,7 @@ import { SelectedResolvedName, } from "./CompositeAddressResolver"; import { ENSAddressResolver } from "./ENSAddressResolver"; +import { UniswapV1Resolver } from "./UniswapV1Resolver"; import { UniswapV2Resolver } from "./UniswapV2Resolver"; import { ERCTokenResolver } from "./ERCTokenResolver"; import { HardcodedAddressResolver } from "./HardcodedAddressResolver"; @@ -18,12 +20,14 @@ export type ResolvedAddresses = Record>; // Create and configure the main resolver export const ensResolver = new ENSAddressResolver(); export const uniswapV2Resolver = new UniswapV2Resolver(); +export const uniswapV1Resolver = new UniswapV1Resolver(); export const ercTokenResolver = new ERCTokenResolver(); export const hardcodedResolver = new HardcodedAddressResolver(); const _mainResolver = new CompositeAddressResolver(); _mainResolver.addResolver(ensResolver); _mainResolver.addResolver(uniswapV2Resolver); +_mainResolver.addResolver(uniswapV1Resolver); _mainResolver.addResolver(ercTokenResolver); _mainResolver.addResolver(hardcodedResolver); @@ -36,6 +40,7 @@ export const resolverRendererRegistry = new Map< >(); resolverRendererRegistry.set(ensResolver, ensRenderer); resolverRendererRegistry.set(uniswapV2Resolver, uniswapV2PairRenderer); +resolverRendererRegistry.set(uniswapV1Resolver, uniswapV1PairRenderer); resolverRendererRegistry.set(ercTokenResolver, tokenRenderer); resolverRendererRegistry.set(hardcodedResolver, plainStringRenderer); diff --git a/src/components/UniswapV1ExchangeName.tsx b/src/components/UniswapV1ExchangeName.tsx new file mode 100644 index 0000000..710c543 --- /dev/null +++ b/src/components/UniswapV1ExchangeName.tsx @@ -0,0 +1,93 @@ +import React from "react"; +import { NavLink } from "react-router-dom"; +import TokenLogo from "./TokenLogo"; +import { ResolvedAddressRenderer } from "../api/address-resolver/address-resolver"; +import { ChecksummedAddress } from "../types"; +import { + UniswapV1PairMeta, + UniswapV1TokenMeta, +} from "../api/address-resolver/UniswapV1Resolver"; + +type UniswapV1ExchangeNameProps = { + address: string; + token: UniswapV1TokenMeta; + linkable: boolean; + dontOverrideColors?: boolean; +}; + +const UniswapV1ExchangeName: React.FC = ({ + address, + token, + linkable, + dontOverrideColors, +}) => { + if (linkable) { + return ( + + Uniswap V1 LP: + + + ); + } + + return ( +
+ Uniswap V1 LP: + +
+ ); +}; + +type ContentProps = { + linkable: boolean; + address: ChecksummedAddress; + name: string; + symbol: string; +}; + +const Content: React.FC = ({ + address, + name, + symbol, + linkable, +}) => ( + <> +
+ +
+ {symbol} + +); + +export const uniswapV1PairRenderer: ResolvedAddressRenderer = + (address, tokenMeta, linkable, dontOverrideColors) => ( + + ); + +export default UniswapV1ExchangeName;