From 099c1465b01372a16c20ed95a9854d8c957f3f8e Mon Sep 17 00:00:00 2001 From: Willian Mitsuda Date: Sun, 31 Oct 2021 19:51:11 -0300 Subject: [PATCH] Add ERC token address resolver --- .../CompositeAddressResolver.ts | 8 +- src/api/address-resolver/ERCTokenResolver.ts | 14 +++- src/api/address-resolver/address-resolver.ts | 4 +- src/api/address-resolver/index.ts | 17 +++-- src/components/ENSName.tsx | 2 +- src/components/TokenLogo.tsx | 2 +- src/components/TokenName.tsx | 73 +++++++++++++++++++ 7 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 src/components/TokenName.tsx diff --git a/src/api/address-resolver/CompositeAddressResolver.ts b/src/api/address-resolver/CompositeAddressResolver.ts index 7c11f3d..3590f3d 100644 --- a/src/api/address-resolver/CompositeAddressResolver.ts +++ b/src/api/address-resolver/CompositeAddressResolver.ts @@ -3,7 +3,7 @@ import { IAddressResolver } from "./address-resolver"; export type SelectedResolvedName = [IAddressResolver, T]; -export class CompositeAddressResolver +export class CompositeAddressResolver implements IAddressResolver> { private resolvers: IAddressResolver[] = []; @@ -17,9 +17,9 @@ export class CompositeAddressResolver address: string ): Promise | undefined> { for (const r of this.resolvers) { - const name = await r.resolveAddress(provider, address); - if (name !== undefined) { - return [r, name]; + const resolvedAddress = await r.resolveAddress(provider, address); + if (resolvedAddress !== undefined) { + return [r, resolvedAddress]; } } diff --git a/src/api/address-resolver/ERCTokenResolver.ts b/src/api/address-resolver/ERCTokenResolver.ts index dc8bb74..02625dc 100644 --- a/src/api/address-resolver/ERCTokenResolver.ts +++ b/src/api/address-resolver/ERCTokenResolver.ts @@ -2,12 +2,13 @@ import { BaseProvider } from "@ethersproject/providers"; import { Contract } from "@ethersproject/contracts"; import { IAddressResolver } from "./address-resolver"; import erc20 from "../../erc20.json"; +import { TokenMeta } from "../../types"; -export class ERCTokenResolver implements IAddressResolver { +export class ERCTokenResolver implements IAddressResolver { async resolveAddress( provider: BaseProvider, address: string - ): Promise { + ): Promise { const erc20Contract = new Contract(address, erc20, provider); try { const [name, symbol, decimals] = await Promise.all([ @@ -15,9 +16,14 @@ export class ERCTokenResolver implements IAddressResolver { erc20Contract.symbol(), erc20Contract.decimals(), ]); - return name; + return { + name, + symbol, + decimals, + }; } catch (err) { - console.warn(`Couldn't get token ${address} metadata; ignoring`, 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/address-resolver.ts b/src/api/address-resolver/address-resolver.ts index 0527b04..12d289e 100644 --- a/src/api/address-resolver/address-resolver.ts +++ b/src/api/address-resolver/address-resolver.ts @@ -8,9 +8,9 @@ export interface IAddressResolver { ): Promise; } -export type ResolvedAddressRenderer = ( +export type ResolvedAddressRenderer = ( address: string, - resolvedAddress: string, + resolvedAddress: T, linkable: boolean, dontOverrideColors: boolean ) => React.ReactElement; diff --git a/src/api/address-resolver/index.ts b/src/api/address-resolver/index.ts index 6db1b1e..d0a0500 100644 --- a/src/api/address-resolver/index.ts +++ b/src/api/address-resolver/index.ts @@ -1,34 +1,39 @@ import { BaseProvider } from "@ethersproject/providers"; import { ensRenderer } from "../../components/ENSName"; +import { tokenRenderer } from "../../components/TokenName"; import { IAddressResolver, ResolvedAddressRenderer } from "./address-resolver"; import { CompositeAddressResolver, SelectedResolvedName, } from "./CompositeAddressResolver"; import { ENSAddressResolver } from "./ENSAddressResolver"; +import { ERCTokenResolver } from "./ERCTokenResolver"; -export type ResolvedAddresses = Record>; +export type ResolvedAddresses = Record>; // Create and configure the main resolver export const ensResolver = new ENSAddressResolver(); +export const ercTokenResolver = new ERCTokenResolver(); -const _mainResolver = new CompositeAddressResolver(); +const _mainResolver = new CompositeAddressResolver(); _mainResolver.addResolver(ensResolver); +_mainResolver.addResolver(ercTokenResolver); -export const mainResolver: IAddressResolver> = +export const mainResolver: IAddressResolver> = _mainResolver; export const resolverRendererRegistry = new Map< - IAddressResolver, - ResolvedAddressRenderer + IAddressResolver, + ResolvedAddressRenderer >(); resolverRendererRegistry.set(ensResolver, ensRenderer); +resolverRendererRegistry.set(ercTokenResolver, tokenRenderer); export const batchPopulate = async ( provider: BaseProvider, addresses: string[] ): Promise => { - const solvers: Promise | undefined>[] = []; + const solvers: Promise | undefined>[] = []; for (const a of addresses) { solvers.push(mainResolver.resolveAddress(provider, a)); } diff --git a/src/components/ENSName.tsx b/src/components/ENSName.tsx index 05fe27c..f12f416 100644 --- a/src/components/ENSName.tsx +++ b/src/components/ENSName.tsx @@ -58,7 +58,7 @@ const Content: React.FC = ({ linkable, name }) => ( ); -export const ensRenderer: ResolvedAddressRenderer = ( +export const ensRenderer: ResolvedAddressRenderer = ( address, resolvedAddress, linkable, diff --git a/src/components/TokenLogo.tsx b/src/components/TokenLogo.tsx index c6f5b8b..8568a0b 100644 --- a/src/components/TokenLogo.tsx +++ b/src/components/TokenLogo.tsx @@ -9,7 +9,7 @@ type TokenLogoProps = { }; const TokenLogo: React.FC = (props) => ( - }> + ); diff --git a/src/components/TokenName.tsx b/src/components/TokenName.tsx new file mode 100644 index 0000000..7301676 --- /dev/null +++ b/src/components/TokenName.tsx @@ -0,0 +1,73 @@ +import React from "react"; +import { NavLink } from "react-router-dom"; +import TokenLogo from "./TokenLogo"; +import { ResolvedAddressRenderer } from "../api/address-resolver/address-resolver"; +import { TokenMeta } from "../types"; + +type TokenNameProps = { + name: string; + address: string; + linkable: boolean; + dontOverrideColors?: boolean; +}; + +const TokenName: React.FC = ({ + name, + address, + linkable, + dontOverrideColors, +}) => { + if (linkable) { + return ( + + + + ); + } + + return ( +
+ +
+ ); +}; + +type ContentProps = { + address: string; + linkable: boolean; + name: string; +}; + +const Content: React.FC = ({ address, linkable, name }) => ( + <> +
+ +
+ {name} + +); + +export const tokenRenderer: ResolvedAddressRenderer = ( + address, + resolvedAddress, + linkable, + dontOverrideColors +) => ( + +); + +export default TokenName;