First working iteration of uniswap V2 LP address resolver
This commit is contained in:
parent
9443a91a46
commit
c58d9afb38
@ -31,8 +31,8 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
||||
<span className="text-gray-500">
|
||||
<FontAwesomeIcon icon={faCaretRight} size="1x" />
|
||||
</span>
|
||||
<div className="grid grid-cols-5 gap-x-1">
|
||||
<div className="flex space-x-1">
|
||||
<div className="grid grid-cols-7 gap-x-1 w-full">
|
||||
<div className="col-span-2 flex space-x-1">
|
||||
<span className="font-bold">From</span>
|
||||
<TransactionAddress
|
||||
address={t.from}
|
||||
@ -41,7 +41,7 @@ const TokenTransferItem: React.FC<TokenTransferItemProps> = ({
|
||||
metadata={metadatas[t.from]}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex space-x-1">
|
||||
<div className="col-span-2 flex space-x-1">
|
||||
<span className="font-bold">To</span>
|
||||
<TransactionAddress
|
||||
address={t.to}
|
||||
|
83
src/api/address-resolver/UniswapV2Resolver.ts
Normal file
83
src/api/address-resolver/UniswapV2Resolver.ts
Normal file
@ -0,0 +1,83 @@
|
||||
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_V2_FACTORY = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f";
|
||||
|
||||
const UNISWAP_V2_FACTORY_ABI = [
|
||||
"function getPair(address tokenA, address tokenB) external view returns (address pair)",
|
||||
];
|
||||
|
||||
const UNISWAP_V2_PAIR_ABI = [
|
||||
"function factory() external view returns (address)",
|
||||
"function token0() external view returns (address)",
|
||||
"function token1() external view returns (address)",
|
||||
];
|
||||
|
||||
export type UniswapV2TokenMeta = {
|
||||
address: ChecksummedAddress;
|
||||
} & TokenMeta;
|
||||
|
||||
export type UniswapV2PairMeta = {
|
||||
pair: ChecksummedAddress;
|
||||
token0: UniswapV2TokenMeta;
|
||||
token1: UniswapV2TokenMeta;
|
||||
};
|
||||
|
||||
const ercResolver = new ERCTokenResolver();
|
||||
|
||||
export class UniswapV2Resolver implements IAddressResolver<UniswapV2PairMeta> {
|
||||
async resolveAddress(
|
||||
provider: BaseProvider,
|
||||
address: string
|
||||
): Promise<UniswapV2PairMeta | undefined> {
|
||||
const pairContract = new Contract(address, UNISWAP_V2_PAIR_ABI, provider);
|
||||
const factoryContract = new Contract(
|
||||
UNISWAP_V2_FACTORY,
|
||||
UNISWAP_V2_FACTORY_ABI,
|
||||
provider
|
||||
);
|
||||
|
||||
try {
|
||||
// First, probe the factory() function; if it responds with UniswapV2 factory
|
||||
// address, it may be a pair
|
||||
const factoryAddress = (await pairContract.factory()) as string;
|
||||
if (factoryAddress !== UNISWAP_V2_FACTORY) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Probe the token0/token1
|
||||
const [token0, token1] = await Promise.all([
|
||||
pairContract.token0() as string,
|
||||
pairContract.token1() as string,
|
||||
]);
|
||||
|
||||
// Probe the factory to ensure it is a legit pair
|
||||
const expectedPairAddress = await factoryContract.getPair(token0, token1);
|
||||
if (expectedPairAddress !== address) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
console.log(`Found pair: ${token0}/${token1}`);
|
||||
const [meta0, meta1] = await Promise.all([
|
||||
ercResolver.resolveAddress(provider, token0),
|
||||
ercResolver.resolveAddress(provider, token1),
|
||||
]);
|
||||
if (meta0 === undefined || meta1 === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
pair: address,
|
||||
token0: { address: token0, ...meta0 },
|
||||
token1: { address: token1, ...meta1 },
|
||||
};
|
||||
} catch (err) {
|
||||
// Ignore on purpose; this indicates the probe failed and the address
|
||||
// is not a token
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
@ -2,12 +2,14 @@ import { BaseProvider } from "@ethersproject/providers";
|
||||
import { ensRenderer } from "../../components/ENSName";
|
||||
import { plainStringRenderer } from "../../components/PlainString";
|
||||
import { tokenRenderer } from "../../components/TokenName";
|
||||
import { uniswapV2PairRenderer } from "../../components/UniswapV2PairName";
|
||||
import { IAddressResolver, ResolvedAddressRenderer } from "./address-resolver";
|
||||
import {
|
||||
CompositeAddressResolver,
|
||||
SelectedResolvedName,
|
||||
} from "./CompositeAddressResolver";
|
||||
import { ENSAddressResolver } from "./ENSAddressResolver";
|
||||
import { UniswapV2Resolver } from "./UniswapV2Resolver";
|
||||
import { ERCTokenResolver } from "./ERCTokenResolver";
|
||||
import { HardcodedAddressResolver } from "./HardcodedAddressResolver";
|
||||
|
||||
@ -15,11 +17,13 @@ export type ResolvedAddresses = Record<string, SelectedResolvedName<any>>;
|
||||
|
||||
// Create and configure the main resolver
|
||||
export const ensResolver = new ENSAddressResolver();
|
||||
export const uniswapV2Resolver = new UniswapV2Resolver();
|
||||
export const ercTokenResolver = new ERCTokenResolver();
|
||||
export const hardcodedResolver = new HardcodedAddressResolver();
|
||||
|
||||
const _mainResolver = new CompositeAddressResolver();
|
||||
_mainResolver.addResolver(ensResolver);
|
||||
_mainResolver.addResolver(uniswapV2Resolver);
|
||||
_mainResolver.addResolver(ercTokenResolver);
|
||||
_mainResolver.addResolver(hardcodedResolver);
|
||||
|
||||
@ -31,6 +35,7 @@ export const resolverRendererRegistry = new Map<
|
||||
ResolvedAddressRenderer<any>
|
||||
>();
|
||||
resolverRendererRegistry.set(ensResolver, ensRenderer);
|
||||
resolverRendererRegistry.set(uniswapV2Resolver, uniswapV2PairRenderer);
|
||||
resolverRendererRegistry.set(ercTokenResolver, tokenRenderer);
|
||||
resolverRendererRegistry.set(hardcodedResolver, plainStringRenderer);
|
||||
|
||||
|
110
src/components/UniswapV2PairName.tsx
Normal file
110
src/components/UniswapV2PairName.tsx
Normal file
@ -0,0 +1,110 @@
|
||||
import React from "react";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import TokenLogo from "./TokenLogo";
|
||||
import { ResolvedAddressRenderer } from "../api/address-resolver/address-resolver";
|
||||
import {
|
||||
UniswapV2PairMeta,
|
||||
UniswapV2TokenMeta,
|
||||
} from "../api/address-resolver/UniswapV2Resolver";
|
||||
import { ChecksummedAddress } from "../types";
|
||||
|
||||
type UniswapV2PairNameProps = {
|
||||
address: string;
|
||||
token0: UniswapV2TokenMeta;
|
||||
token1: UniswapV2TokenMeta;
|
||||
linkable: boolean;
|
||||
dontOverrideColors?: boolean;
|
||||
};
|
||||
|
||||
const UniswapV2PairName: React.FC<UniswapV2PairNameProps> = ({
|
||||
address,
|
||||
token0,
|
||||
token1,
|
||||
linkable,
|
||||
dontOverrideColors,
|
||||
}) => {
|
||||
if (linkable) {
|
||||
return (
|
||||
<NavLink
|
||||
className={`flex items-baseline space-x-1 font-sans ${
|
||||
dontOverrideColors ? "" : "text-link-blue hover:text-link-blue-hover"
|
||||
} truncate`}
|
||||
to={`/address/${address}`}
|
||||
title={`Uniswap V2 LP (${token0.symbol}/${token1.symbol}): ${address}`}
|
||||
>
|
||||
<span>Uniswap V2 LP:</span>
|
||||
<Content
|
||||
linkable={true}
|
||||
address={token0.address}
|
||||
name={token0.name}
|
||||
symbol={token0.symbol}
|
||||
/>
|
||||
<span>/</span>
|
||||
<Content
|
||||
linkable={true}
|
||||
address={token1.address}
|
||||
name={token1.name}
|
||||
symbol={token1.symbol}
|
||||
/>
|
||||
</NavLink>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="flex items-baseline space-x-1 font-sans text-gray-700 truncate"
|
||||
title={`Uniswap V2 LP (${token0.symbol}/${token1.symbol}): ${address}`}
|
||||
>
|
||||
<span>Uniswap V2 LP:</span>
|
||||
<Content
|
||||
linkable={false}
|
||||
address={token0.address}
|
||||
name={token0.name}
|
||||
symbol={token0.symbol}
|
||||
/>
|
||||
<span>/</span>
|
||||
<Content
|
||||
linkable={false}
|
||||
address={token1.address}
|
||||
name={token1.name}
|
||||
symbol={token1.symbol}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type ContentProps = {
|
||||
linkable: boolean;
|
||||
address: ChecksummedAddress;
|
||||
name: string;
|
||||
symbol: string;
|
||||
};
|
||||
|
||||
const Content: React.FC<ContentProps> = ({
|
||||
address,
|
||||
name,
|
||||
symbol,
|
||||
linkable,
|
||||
}) => (
|
||||
<>
|
||||
<div
|
||||
className={`self-center w-5 h-5 ${linkable ? "" : "filter grayscale"}`}
|
||||
>
|
||||
<TokenLogo address={address} name={name} />
|
||||
</div>
|
||||
<span>{symbol}</span>
|
||||
</>
|
||||
);
|
||||
|
||||
export const uniswapV2PairRenderer: ResolvedAddressRenderer<UniswapV2PairMeta> =
|
||||
(address, tokenMeta, linkable, dontOverrideColors) => (
|
||||
<UniswapV2PairName
|
||||
address={address}
|
||||
token0={tokenMeta.token0}
|
||||
token1={tokenMeta.token1}
|
||||
linkable={linkable}
|
||||
dontOverrideColors={dontOverrideColors}
|
||||
/>
|
||||
);
|
||||
|
||||
export default UniswapV2PairName;
|
Loading…
Reference in New Issue
Block a user