diff --git a/packages/site/package.json b/packages/site/package.json index 16c5cd4..2bf6f1c 100644 --- a/packages/site/package.json +++ b/packages/site/package.json @@ -33,6 +33,8 @@ "dependencies": { "@material-ui/core": "^4.12.4", "@metamask/providers": "^9.0.0", + "@nivo/bar": "^0.80.0", + "@nivo/core": "^0.80.0", "js-big-decimal": "^1.4.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/packages/site/src/components/EarningsChart.stories.tsx b/packages/site/src/components/EarningsChart.stories.tsx new file mode 100644 index 0000000..e8e4eb9 --- /dev/null +++ b/packages/site/src/components/EarningsChart.stories.tsx @@ -0,0 +1,94 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { address } from '../utils/tokens'; +import { + EarningsChart, + EarningsChartProps, + StampedEarning, +} from './EarningsChart'; + +const meta: Meta = { + title: 'EarningsChart', + component: EarningsChart, + argTypes: { + timeScale: { + options: ['day', 'week', 'month'], + control: { + type: 'radio', + }, + }, + }, +}; + +export default meta; + +type EC = StoryObj; + +export const NoData: EC = { + args: { + timeScale: 'day', + earnings: [], + }, +}; + +export const Day: EC = { + args: { + timeScale: 'day', + earnings: randomEarnings(oneDayAgo()), + }, +}; + +export const Week: EC = { + args: { + timeScale: 'week', + earnings: randomEarnings(oneWeekAgo()), + }, +}; + +export const Month: EC = { + args: { + timeScale: 'month', + earnings: randomEarnings(oneMonthAgo()), + }, +}; + +function oneMonthAgo(): Date { + return new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); +} + +function oneDayAgo(): Date { + return new Date(Date.now() - 24 * 60 * 60 * 1000); +} + +function oneWeekAgo(): Date { + return new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); +} + +function randomEarnings( + since: Date, + tokens: address[] = ['0x0000000000000000000000000000000000000000'], +): StampedEarning[] { + const earnings: StampedEarning[] = []; + + for (let i = 0; i < 100; i++) { + const time = randSince(since); + const token = tokens[Math.floor(Math.random() * tokens.length)]; + earnings.push({ + time, + token, + amount: BigInt(Math.floor(Math.random() * 100)), + }); + } + return earnings; +} + +/** + * Generates a random date between the given date and now. + * + * @param then - Lower bound for the random date. + * @returns A date between the given date and now. + */ +function randSince(then: Date): Date { + const weight = Math.random(); + const epoch = then.getTime() * weight + Date.now() * (1 - weight); + return new Date(epoch); +} diff --git a/packages/site/src/components/EarningsChart.tsx b/packages/site/src/components/EarningsChart.tsx new file mode 100644 index 0000000..5c13eaa --- /dev/null +++ b/packages/site/src/components/EarningsChart.tsx @@ -0,0 +1,188 @@ +import React from 'react'; +import * as bar from '@nivo/bar'; +import AxisProps from '@nivo/axes/dist/types'; +import { address, getToken } from '../utils/tokens'; + +export type StampedEarning = { + time: Date; + token: address; + amount: bigint; +}; + +export type EarningsChartProps = { + earnings: StampedEarning[]; + timeScale: 'day' | 'week' | 'month' | number; // number is in days +}; + +type Bucket = { + startTime: Date; + endTime: Date; + label: string; + earnings: { [x: address]: bigint }; +}; + +function getBuckets(timeScale: EarningsChartProps['timeScale']): Bucket[] { + const now = new Date(); + const buckets: Bucket[] = []; + + if (timeScale === 'day') { + for (let i = 24; i > 0; i--) { + buckets.push({ + startTime: new Date(now.getTime() - i * 60 * 60 * 1000), + endTime: new Date(now.getTime() - (i - 1) * 60 * 60 * 1000), + label: `${i - 1}`, + earnings: {}, + }); + } + } + + if (timeScale === 'week') { + for (let i = 7; i > 0; i--) { + buckets.push({ + startTime: new Date(now.getTime() - i * 24 * 60 * 60 * 1000), + endTime: new Date(now.getTime() - (i - 1) * 24 * 60 * 60 * 1000), + label: `${i - 1}`, + earnings: {}, + }); + } + } + + if (timeScale === 'month') { + for (let i = 30; i > 0; i--) { + buckets.push({ + startTime: new Date(now.getTime() - i * 24 * 60 * 60 * 1000), + endTime: new Date(now.getTime() - (i - 1) * 24 * 60 * 60 * 1000), + label: `${i - 1}`, + earnings: {}, + }); + } + } + + return buckets; +} + +function fillBuckets(buckets: Bucket[], earnings: StampedEarning[]): Bucket[] { + const tokens = getTokenList(earnings); + + for (const bucket of buckets) { + for (const token of tokens) { + bucket.earnings[token] = 0n; + } + } + + for (const earning of earnings) { + for (const bucket of buckets) { + if (earning.time > bucket.startTime && earning.time < bucket.endTime) { + bucket.earnings[earning.token] += earning.amount; + } + } + } + + return buckets; +} + +function getTokenList(earnings: StampedEarning[]): address[] { + const tokens = new Set
(); + earnings.forEach((x) => tokens.add(x.token)); + return Array.from(tokens); +} + +/** + * Converts 'filled' time buckets to data for nivo bar chart. + * + * @param buckets - Filled time buckets. + * @returns Formatted data for nivo bar chart. + */ +function getChartData(buckets: Bucket[]): bar.BarDatum[] { + const data: bar.BarDatum[] = []; + + for (const bucket of buckets) { + const datum: bar.BarDatum = { + id: bucket.label, + }; + for (const token of Object.keys(bucket.earnings)) { + datum[token] = Number(bucket.earnings[token]); + } + data.push(datum); + } + return data; +} + +export const EarningsChart: React.FC = (props) => { + const { earnings, timeScale } = props; + const timeBuckets = getBuckets(timeScale); + fillBuckets(timeBuckets, earnings); + + const data: bar.BarDatum[] = getChartData(timeBuckets); + + return ( +
+ +
+ ); +}; diff --git a/packages/site/src/utils/tokens.ts b/packages/site/src/utils/tokens.ts new file mode 100644 index 0000000..fb3e45c --- /dev/null +++ b/packages/site/src/utils/tokens.ts @@ -0,0 +1,739 @@ +/** + * An Ethereum contract / EOA address. + */ +export type address = string; + +type token = { + chainID: number; + addr: address; + name: string; + ticker: string; + icon?: string; +}; + +type EthMainNetToken = token & { + chainID: 1; +}; + +export const EthereumTokens: EthMainNetToken[] = [ + { + chainID: 1, + addr: '0xdac17f958d2ee523a2206206994597c13d831ec7', + name: 'Tether USD', + ticker: 'USDT', + icon: 'https://etherscan.io/token/images/tethernew_32.png', + }, + { + chainID: 1, + addr: '0xB8c77482e45F1F44dE1745F52C74426C631bDD52', + name: 'BNB', + ticker: 'BNB', + icon: 'https://etherscan.io/token/images/bnb_28_2.png', + }, + { + chainID: 1, + addr: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', + name: 'USD Coin', + ticker: 'USDC', + icon: 'https://etherscan.io/token/images/centre-usdc_28.png', + }, + { + chainID: 1, + addr: '0x2b591e99afe9f32eaa6214f7b7629768c40eeb39', + name: 'HEX', + ticker: 'HEX', + icon: 'https://etherscan.io/token/images/hex_32.png', + }, + { + chainID: 1, + addr: '0xae7ab96520de3a18e5e111b5eaab095312d7fe84', + name: 'stETH', + ticker: 'stETH', + icon: 'https://etherscan.io/token/images/lido-steth_32.png', + }, + { + chainID: 1, + addr: '0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0', + name: 'Matic Token', + ticker: 'MATIC', + icon: 'https://etherscan.io/token/images/polygonnew_32.png', + }, + { + chainID: 1, + addr: '0x0abcfbfa8e3fda8b7fba18721caf7d5cf55cf5f5', + name: 'ANY Litecoin', + ticker: 'anyLTC', + icon: 'https://etherscan.io/token/images/anylitecoin_32.png', + }, + { + chainID: 1, + addr: '0x4fabb145d64652a948d72533023f6e7a623c7c53', + name: 'Binance USD', + ticker: 'BUSD', + icon: 'https://etherscan.io/token/images/binanceusd_32.png', + }, + { + chainID: 1, + addr: '0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE', + name: 'SHIBA INU', + ticker: 'SHIB', + icon: 'https://etherscan.io/token/images/shibatoken_32.png', + }, + { + chainID: 1, + addr: '0x3883f5e181fccaf8410fa61e12b59bad963fb645', + name: 'Theta Token', + ticker: 'THETA', + icon: 'https://etherscan.io/token/images/theta_28.png', + }, + { + chainID: 1, + addr: '0x6b175474e89094c44da98b954eedeac495271d0f', + name: 'Dai Stablecoin', + ticker: 'DAI', + icon: 'https://etherscan.io/token/images/MCDDai_32.png', + }, + { + chainID: 1, + addr: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984', + name: 'Uniswap', + ticker: 'UNI', + icon: 'https://etherscan.io/token/images/uniswap_32.png', + }, + { + chainID: 1, + addr: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + name: 'Wrapped BTC', + ticker: 'WBTC', + icon: 'https://etherscan.io/token/images/wbtc_28.png?v=1', + }, + { + chainID: 1, + addr: '0x514910771af9ca656af840dff83e8264ecf986ca', + name: 'ChainLink Token', + ticker: 'LINK', + icon: 'https://etherscan.io/token/images/chainlinktoken_32.png?v=6', + }, + { + chainID: 1, + addr: '0x582d872a1b094fc48f5de31d3b73f2d9be47def1', + name: 'Wrapped TON Coin', + ticker: 'TONCOIN', + icon: 'https://etherscan.io/token/images/theopennetwork_32.png', + }, + { + chainID: 1, + addr: '0x2af5d2ad76741191d15dfe7bf6ac92d4bd912ca3', + name: 'Bitfinex LEO Token', + ticker: 'LEO', + icon: 'https://etherscan.io/token/images/leo_28_2.png', + }, + { + chainID: 1, + addr: '0x75231f58b43240c9718dd58b4967c5114342a86c', + name: 'OKB', + ticker: 'OKB', + icon: 'https://etherscan.io/token/images/okex_28.png', + }, + { + chainID: 1, + addr: '0x5a98fcbea516cf06857215779fd812ca3bef1b32', + name: 'Lido DAO Token', + ticker: 'LDO', + icon: 'https://etherscan.io/token/images/lido-dao_32.png', + }, + { + chainID: 1, + addr: '0x0000000000085d4780B73119b644AE5ecd22b376', + name: 'TrueUSD', + ticker: 'TUSD', + icon: 'https://etherscan.io/token/images/trueusd_32.png?v=2', + }, + { + chainID: 1, + addr: '0x85f17cf997934a597031b2e18a9ab6ebd4b9f6a4', + name: 'NEAR', + ticker: 'NEAR', + icon: 'https://etherscan.io/token/images/near_32.png?v=3', + }, + { + chainID: 1, + addr: '0xd850942ef8811f2a866692a623011bde52a462c1', + name: 'VeChain', + ticker: 'VEN', + icon: 'https://etherscan.io/token/images/vechain_28.png', + }, + { + chainID: 1, + addr: '0x4a220e6096b25eadb88358cb44068a3248254675', + name: 'Quant', + ticker: 'QNT', + icon: 'https://etherscan.io/token/images/quantnetwork_28_2.png?v=6', + }, + { + chainID: 1, + addr: '0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b', + name: 'Cronos Coin', + ticker: 'CRO', + icon: 'https://etherscan.io/token/images/cro_32.png', + }, + { + chainID: 1, + addr: '0xB50721BCf8d664c30412Cfbc6cf7a15145234ad1', + name: 'Arbitrum', + ticker: 'ARB', + icon: 'https://etherscan.io/token/images/arbitrumone2_32.png', + }, + { + chainID: 1, + addr: '0x4d224452801aced8b2f0aebe155379bb5d594381', + name: 'ApeCoin', + ticker: 'APE', + icon: 'https://etherscan.io/token/images/apecoin_32.png', + }, + { + chainID: 1, + addr: '0x4e15361fd6b4bb609fa63c81a2be19d873717870', + name: 'Fantom Token', + ticker: 'FTM', + icon: 'https://etherscan.io/token/images/fantomtoken_32.png', + }, + { + chainID: 1, + addr: '0xc944e90c64b2c07662a292be6244bdf05cda44a7', + name: 'Graph Token', + ticker: 'GRT', + icon: 'https://etherscan.io/token/images/TheGraph_32.png', + }, + { + chainID: 1, + addr: '0x3845badAde8e6dFF049820680d1F14bD3903a5d0', + name: 'SAND', + ticker: 'SAND', + icon: 'https://etherscan.io/token/images/sand_32.png', + }, + { + chainID: 1, + addr: '0xba3D9687Cf50fE253cd2e1cFeEdE1d6787344Ed5', + name: 'Aave Interest bearing Aave Token', + ticker: 'aAAVE', + icon: 'https://etherscan.io/token/images/aAAVE_32.png', + }, + { + chainID: 1, + addr: '0x4da27a545c0c5b758a6ba100e3a049001de870f5', + name: 'Staked Aave', + ticker: 'stkAAVE', + icon: 'https://etherscan.io/token/images/stakedaave_32.png', + }, + { + chainID: 1, + addr: '0xfd09cf7cfffa9932e33668311c4777cb9db3c9be', + name: 'Wrapped Decentraland MANA', + ticker: 'wMANA', + icon: 'https://etherscan.io/token/images/decentraland_32.png?v=1', + }, + { + chainID: 1, + addr: '0x853d955acef822db058eb8505911ed77f175b99e', + name: 'Frax', + ticker: 'FRAX', + icon: 'https://etherscan.io/token/images/fraxfinanceeth2_32.png', + }, + { + chainID: 1, + addr: '0xf57e7e7c23978c3caec3c3548e3d615c346e79ff', + name: 'Immutable X', + ticker: 'IMX', + icon: 'https://etherscan.io/token/images/immutable_32.png', + }, + { + chainID: 1, + addr: '0x8e870d67f660d95d5be530380d0ec0bd388289e1', + name: 'Pax Dollar', + ticker: 'USDP', + icon: 'https://etherscan.io/token/images/paxos-usdp_32.png', + }, + { + chainID: 1, + addr: '0xf34960d9d60be18cc1d5afc1a6f012a723a28811', + name: 'KuCoin Token', + ticker: 'KCS', + icon: 'https://etherscan.io/token/images/kucointoken_32.png', + }, + { + chainID: 1, + addr: '0xd33526068d116ce69f19a9ee46f0bd304f21a51f', + name: 'Rocket Pool', + ticker: 'RPL', + icon: 'https://etherscan.io/token/images/Rocketpool_32.png', + }, + { + chainID: 1, + addr: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f', + name: 'Synthetix Network Token', + ticker: 'SNX', + icon: 'https://etherscan.io/token/images/SynthetixSNX_32.png', + }, + { + chainID: 1, + addr: '0x1a4b46696b2bb4794eb3d4c26f1c55f9170fa4c5', + name: 'BitDAO', + ticker: 'BIT', + icon: 'https://etherscan.io/token/images/bitdao_32.png?=v97', + }, + { + chainID: 1, + addr: '0x0C10bF8FcB7Bf5412187A595ab97a3609160b5c6', + name: 'Decentralized USD', + ticker: 'USDD', + icon: 'https://etherscan.io/token/images/usdd-tron_32.png', + }, + { + chainID: 1, + addr: '0x3506424f91fd33084466f402d5d97f05f8e3b4af', + name: 'chiliZ', + ticker: 'CHZ', + icon: 'https://etherscan.io/token/images/chiliz_28.png', + }, + { + chainID: 1, + addr: '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2', + name: 'Maker', + ticker: 'MKR', + icon: 'https://etherscan.io/token/images/mkr-etherscan-35.png', + }, + { + chainID: 1, + addr: '0x3432b6a60d23ca0dfca7761b7ab56459d9c964d0', + name: 'Frax Share', + ticker: 'FXS', + icon: 'https://etherscan.io/token/images/fraxfxs_new_32.png', + }, + { + chainID: 1, + addr: '0x6f259637dcd74c767781e37bc6133cd6a68aa161', + name: 'HuobiToken', + ticker: 'HT', + icon: 'https://etherscan.io/token/images/huobi_28.png', + }, + { + chainID: 1, + addr: '0x6de037ef9ad2725eb40118bb1702ebb27e4aeb24', + name: 'Render Token', + ticker: 'RNDR', + icon: 'https://etherscan.io/token/images/Render2_32.png', + }, + { + chainID: 1, + addr: '0xc669928185dbce49d2230cc9b0979be6dc797957', + name: 'BitTorrent', + ticker: 'BTT', + icon: 'https://etherscan.io/token/images/bittorrent_32.png', + }, + { + chainID: 1, + addr: '0x41ab1b6fcbb2fa9dced81acbdec13ea6315f2bf2', + name: 'XinFin XDCE', + ticker: 'XDCE', + icon: 'https://etherscan.io/token/images/xinfin_28.png', + }, + { + chainID: 1, + addr: '0xe28b3B32B6c345A34Ff64674606124Dd5Aceca30', + name: 'Injective Token', + ticker: 'INJ', + icon: 'https://etherscan.io/token/images/injective_32.png?v=3', + }, + { + chainID: 1, + addr: '0x45804880De22913dAFE09f4980848ECE6EcbAf78', + name: 'Paxos Gold', + ticker: 'PAXG', + icon: 'https://etherscan.io/token/images/paxosgold_32.png', + }, + { + chainID: 1, + addr: '0x68749665ff8d2d112fa859aa293f07a622782f38', + name: 'Tether Gold', + ticker: 'XAUt', + icon: 'https://etherscan.io/token/images/tethergoldnew_32.png', + }, + { + chainID: 1, + addr: '0xae78736cd615f374d3085123a210448e74fc6393', + name: 'Rocket Pool ETH', + ticker: 'rETH', + icon: 'https://etherscan.io/token/images/rocketpooleth_32.png?v=2', + }, + { + chainID: 1, + addr: '0x5b7533812759b45c2b44c19e320ba2cd2681b542', + name: 'SingularityNET Token', + ticker: 'AGIX', + icon: 'https://etherscan.io/token/images/singularitynet_28.png', + }, + { + chainID: 1, + addr: '0x05f4a42e251f2d52b8ed15e9fedaacfcef1fad27', + name: 'Zilliqa', + ticker: 'ZIL', + icon: 'https://etherscan.io/token/images/zilliqa_28_2.png', + }, + { + chainID: 1, + addr: '0xbbbbca6a901c926f240b89eacb641d8aec7aeafd', + name: 'LoopringCoin V2', + ticker: 'LRC', + icon: 'https://etherscan.io/token/images/lrc_32.png', + }, + { + chainID: 1, + addr: '0x4e3fbd56cd56c3e72c1403e103b45db9da5b9d2b', + name: 'Convex Token', + ticker: 'CVX', + icon: 'https://etherscan.io/token/images/ConvexFinance_32.png', + }, + { + chainID: 1, + addr: '0x111111111117dc0aa78b770fa6a738034120c302', + name: '1INCH Token', + ticker: '1INCH', + icon: 'https://etherscan.io/token/images/1inch_32.png', + }, + { + chainID: 1, + addr: '0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c', + name: 'EnjinCoin', + ticker: 'ENJ', + icon: 'https://etherscan.io/token/images/enjin_28_2.png', + }, + { + chainID: 1, + addr: '0x4691937a7508860f876c9c0a2a617e7d9e945d4b', + name: 'Wootrade Network', + ticker: 'WOO', + icon: 'https://etherscan.io/token/images/wootrade_32.png?=v2', + }, + { + chainID: 1, + addr: '0x69af81e73a73b40adf4f3d4223cd9b1ece623074', + name: 'Mask Network', + ticker: 'MASK', + icon: 'https://etherscan.io/token/images/mask_32.png', + }, + { + chainID: 1, + addr: '0x0d8775f648430679a709e98d2b0cb6250d2887ef', + name: 'BAT', + ticker: 'BAT', + icon: 'https://etherscan.io/token/images/bat.png', + }, + { + chainID: 1, + addr: '0x92d6c1e31e14520e676a687f0a93788b716beff5', + name: 'dYdX', + ticker: 'DYDX', + icon: 'https://etherscan.io/token/images/dydx2_32.png', + }, + { + chainID: 1, + addr: '0x056fd409e1d7a124bd7017459dfea2f387b6d5cd', + name: 'Gemini dollar', + ticker: 'GUSD', + icon: 'https://etherscan.io/token/images/gemini_28.png', + }, + { + chainID: 1, + addr: '0xb62132e35a6c13ee1ee0f84dc5d40bad8d815206', + name: 'Nexo', + ticker: 'NEXO', + icon: 'https://etherscan.io/token/images/nexo_32.png', + }, + { + chainID: 1, + addr: '0xd7c49cee7e9188cca6ad8ff264c1da2e69d4cf3b', + name: 'NXM', + ticker: 'NXM', + icon: 'https://etherscan.io/token/images/nxm_32.png?v=2', + }, + { + chainID: 1, + addr: '0xaea46A60368A7bD060eec7DF8CBa43b7EF41Ad85', + name: 'Fetch', + ticker: 'FET', + icon: 'https://etherscan.io/token/images/fetch_32.png?v=2', + }, + { + chainID: 1, + addr: '0x6c6ee5e31d828de241282b9606c8e98ea48526e2', + name: 'HoloToken', + ticker: 'HOT', + icon: 'https://etherscan.io/token/images/holo_28.png', + }, + { + chainID: 1, + addr: '0x8fc8f8269ebca376d046ce292dc7eac40c8d358a', + name: 'DeFiChain Token', + ticker: 'DFI', + icon: 'https://etherscan.io/token/images/defichain_32.png', + }, + { + chainID: 1, + addr: '0xc18360217d8f7ab5e7c516566761ea12ce7f9d72', + name: 'Ethereum Name Service', + ticker: 'ENS', + icon: 'https://etherscan.io/token/images/ens2_32.png?v=2', + }, + { + chainID: 1, + addr: '0xcf0c122c6b73ff809c693db761e7baebe62b6a2e', + name: 'FLOKI', + ticker: 'FLOKI', + icon: 'https://etherscan.io/token/images/floki_32.png', + }, + { + chainID: 1, + addr: '0xe452e6ea2ddeb012e20db73bf5d3863a3ac8d77a', + name: 'Wrapped Celo', + ticker: 'wCELO', + icon: 'https://etherscan.io/token/images/wrappedcelo_32.png', + }, + { + chainID: 1, + addr: '0x3103df8f05c4d8af16fd22ae63e406b97fec6938', + name: 'WQtum', + ticker: 'WQTUM', + icon: 'https://etherscan.io/token/images/qtum_28.png', + }, + { + chainID: 1, + addr: '0x8ce9137d39326ad0cd6491fb5cc0cba0e089b6a9', + name: 'Swipe', + ticker: 'SXP', + icon: 'https://etherscan.io/token/images/swipe_32.png', + }, + { + chainID: 1, + addr: '0x15D4c048F83bd7e37d49eA4C83a07267Ec4203dA', + name: 'Gala', + ticker: 'GALA', + icon: 'https://etherscan.io/token/images/gala_32.png?v=3', + }, + { + chainID: 1, + addr: '0x6810e776880c02933d47db1b9fc05908e5386b96', + name: 'Gnosis', + ticker: 'GNO', + icon: 'https://etherscan.io/token/images/gnosans_32.png?v=2', + }, + { + chainID: 1, + addr: '0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e', + name: 'yearn.finance', + ticker: 'YFI', + icon: 'https://etherscan.io/token/images/yfi_32_2.png', + }, + { + chainID: 1, + addr: '0xc00e94cb662c3520282e6f5717214004a7f26888', + name: 'Compound', + ticker: 'COMP', + icon: 'https://etherscan.io/token/images/comp_32.png', + }, + { + chainID: 1, + addr: '0x799a4202c12ca952cb311598a024c80ed371a41e', + name: 'HarmonyOne', + ticker: 'ONE', + icon: 'https://etherscan.io/token/images/harmonyone_32.png', + }, + { + chainID: 1, + addr: '0xba100000625a3754423978a60c9317c58a424e3d', + name: 'Balancer', + ticker: 'BAL', + icon: 'https://etherscan.io/token/images/Balancer_32.png', + }, + { + chainID: 1, + addr: '0x64aa3364f17a4d01c6f1751fd97c2bd3d7e7f1d5', + name: 'Olympus', + ticker: 'OHM', + icon: 'https://etherscan.io/token/images/olympusdao2_32.png', + }, + { + chainID: 1, + addr: '0xe3c408BD53c31C085a1746AF401A4042954ff740', + name: 'GreenMetaverseToken', + ticker: 'GMT', + icon: 'https://etherscan.io/token/images/stepngmt_32.png', + }, + { + chainID: 1, + addr: '0x0316EB71485b0Ab14103307bf65a021042c6d380', + name: 'Huobi BTC', + ticker: 'HBTC', + icon: 'https://etherscan.io/token/images/huobibtc_32.png', + }, + { + chainID: 1, + addr: '0x6fb3e0a217407efff7ca062d46c26e5d60a14d69', + name: 'IoTeX Network', + ticker: 'IOTX', + icon: 'https://etherscan.io/token/images/iotex_28.png', + }, + { + chainID: 1, + addr: '0x5e8422345238f34275888049021821e8e08caa1f', + name: 'Frax Ether', + ticker: 'frxETH', + icon: 'https://etherscan.io/token/images/fraxethcanonical2_32.png', + }, + { + chainID: 1, + addr: '0x5283d291dbcf85356a21ba090e6db59121208b44', + name: 'Blur', + ticker: 'BLUR', + icon: 'https://etherscan.io/token/images/blurio_32.png', + }, + { + chainID: 1, + addr: '0x7DD9c5Cba05E151C895FDe1CF355C9A1D5DA6429', + name: 'Golem Network Token', + ticker: 'GLM', + icon: 'https://etherscan.io/token/images/golem-glm_32.png?v=2', + }, + { + chainID: 1, + addr: '0xb63b606ac810a52cca15e44bb630fd42d8d1d83d', + name: 'MCO', + ticker: 'MCO', + icon: 'https://etherscan.io/token/images/cryptocom_28.png', + }, + { + chainID: 1, + addr: '0x9d65ff81a3c488d585bbfb0bfe3c7707c7917f54', + name: 'SSV Token', + ticker: 'SSV', + icon: 'https://etherscan.io/token/images/ssvnetwork_32.png', + }, + { + chainID: 1, + addr: '0xe41d2489571d322189246dafa5ebde1f4699f498', + name: 'ZRX', + ticker: 'ZRX', + icon: 'https://etherscan.io/token/images/zrx_28.png?v=3', + }, + { + chainID: 1, + addr: '0xba11d00c5f74255f56a5e366f4f77f5a186d7f55', + name: 'BandToken', + ticker: 'BAND', + icon: 'https://etherscan.io/token/images/bandtoken_32.png', + }, + { + chainID: 1, + addr: '0x177d39ac676ed1c67a2b268ad7f1e58826e5b0af', + name: 'CoinDash Token', + ticker: 'CDT', + icon: 'https://etherscan.io/token/images/coindashtoken_32.png', + }, + { + chainID: 1, + addr: '0xC581b735A1688071A1746c968e0798D642EDE491', + name: 'Euro Tether', + ticker: 'EURT', + icon: 'https://etherscan.io/token/images/tethernew_32.png', + }, + { + chainID: 1, + addr: '0x11eef04c884e24d9b7b4760e7476d06ddf797f36', + name: 'MX Token', + ticker: 'MX', + icon: 'https://etherscan.io/token/images/mexc_32.png', + }, + { + chainID: 1, + addr: '0x6b3595068778dd592e39a122f4f5a5cf09c90fe2', + name: 'SushiToken', + ticker: 'SUSHI', + icon: 'https://etherscan.io/token/images/sushitoken_32.png', + }, + { + chainID: 1, + addr: '0xff20817765cb7f73d4bde2e66e067e58d11095c2', + name: 'Amp', + ticker: 'AMP', + icon: 'https://etherscan.io/token/images/amp_32.png?v=2', + }, + { + chainID: 1, + addr: '0x0f51bb10119727a7e5ea3538074fb341f56b09ad', + name: 'DAO Maker', + ticker: 'DAO', + icon: 'https://etherscan.io/token/images/daomaker_32.png', + }, + { + chainID: 1, + addr: '0xfa1a856cfa3409cfa145fa4e20eb270df3eb21ab', + name: 'IOSToken', + ticker: 'IOST', + icon: 'https://etherscan.io/token/images/iost_28.png', + }, + { + chainID: 1, + addr: '0xd26114cd6EE289AccF82350c8d8487fedB8A0C07', + name: 'OMG Network', + ticker: 'OMG', + icon: 'https://etherscan.io/token/images/OMGNetwork_32.png', + }, + { + chainID: 1, + addr: '0x320623b8e4ff03373931769a31fc52a4e78b5d70', + name: 'Reserve Rights', + ticker: 'RSR', + icon: 'https://etherscan.io/token/images/reserverights_32.png', + }, + { + chainID: 1, + addr: '0xed04915c23f00a313a544955524eb7dbd823143d', + name: 'Alchemy', + ticker: 'ACH', + icon: 'https://etherscan.io/token/images/ach_32.png?=v1', + }, + { + chainID: 1, + addr: '0xcb86c6a22cb56b6cf40cafedb06ba0df188a416e', + name: 'inSure', + ticker: 'SURE', + icon: 'https://etherscan.io/token/images/insure_32.png?v=4', + }, + { + chainID: 1, + addr: '0x6dea81c8171d0ba574754ef6f8b412f2ed88c54d', + name: 'LQTY', + ticker: 'LQTY', + icon: 'https://etherscan.io/token/images/liquity_32.png', + }, +]; + +/** + * Catalog of known tokens, indexed by address and chainID. + * + * Includes token name, ticker symbol, and icon. + */ +const tokens: token[] = [...EthereumTokens]; + +/** + * Get token metadata by address and chainID. + * + * @param addr - Contract address of the token. + * @param chainID - Chain ID of the token. + * @returns Token metadata, or undefined if not found. + */ +export function getToken(addr: string, chainID = 1): token | undefined { + return tokens.find( + (token) => token.addr === addr && token.chainID === chainID, + ); +} diff --git a/yarn.lock b/yarn.lock index 370d979..9767e3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3749,6 +3749,140 @@ __metadata: languageName: node linkType: hard +"@nivo/annotations@npm:0.80.0": + version: 0.80.0 + resolution: "@nivo/annotations@npm:0.80.0" + dependencies: + "@nivo/colors": 0.80.0 + "@react-spring/web": 9.4.5 + lodash: ^4.17.21 + peerDependencies: + "@nivo/core": 0.80.0 + react: ">= 16.14.0 < 19.0.0" + checksum: 6b2dcc3cc443a660b10feab378b816e84f8aff4390345f8316852d88dcd893af0c8de63aad6bb6296ae13c55e4cce4a57f3f3898f5fdac03bb756df24a81d3fd + languageName: node + linkType: hard + +"@nivo/axes@npm:0.80.0": + version: 0.80.0 + resolution: "@nivo/axes@npm:0.80.0" + dependencies: + "@nivo/scales": 0.80.0 + "@react-spring/web": 9.4.5 + d3-format: ^1.4.4 + d3-time-format: ^3.0.0 + peerDependencies: + "@nivo/core": 0.80.0 + prop-types: ">= 15.5.10 < 16.0.0" + react: ">= 16.14.0 < 19.0.0" + checksum: 59cea2b3f2dc547f3ebbe920b4c789ac8c5dd4b3e5ba3c323d84f63d052faf813655cca8bb861a035ee064a8d71931bb35e956fc581051c0e86c4a80a1e581df + languageName: node + linkType: hard + +"@nivo/bar@npm:^0.80.0": + version: 0.80.0 + resolution: "@nivo/bar@npm:0.80.0" + dependencies: + "@nivo/annotations": 0.80.0 + "@nivo/axes": 0.80.0 + "@nivo/colors": 0.80.0 + "@nivo/legends": 0.80.0 + "@nivo/scales": 0.80.0 + "@nivo/tooltip": 0.80.0 + "@react-spring/web": 9.4.5 + d3-scale: ^3.2.3 + d3-shape: ^1.3.5 + lodash: ^4.17.21 + peerDependencies: + "@nivo/core": 0.80.0 + react: ">= 16.14.0 < 19.0.0" + checksum: ab1a243b256970fd4b8a7874e67bbc95aac8d175865ba87b2c1fbc04394d823a752f13b485250aa53528790b5fa8c015a5a66e858ad43883bc6ec8cb49587a52 + languageName: node + linkType: hard + +"@nivo/colors@npm:0.80.0": + version: 0.80.0 + resolution: "@nivo/colors@npm:0.80.0" + dependencies: + d3-color: ^2.0.0 + d3-scale: ^3.2.3 + d3-scale-chromatic: ^2.0.0 + lodash: ^4.17.21 + peerDependencies: + "@nivo/core": 0.80.0 + prop-types: ">= 15.5.10 < 16.0.0" + react: ">= 16.14.0 < 19.0.0" + checksum: 0dc2044c984d8180d2f0a13fba6b6cdda0a18eec6ef71e9e523794f7ba70ed896231840f22c93f7e30b8730fde26ec07fff8e3d08fef120e91c8a9d552aa1fca + languageName: node + linkType: hard + +"@nivo/core@npm:^0.80.0": + version: 0.80.0 + resolution: "@nivo/core@npm:0.80.0" + dependencies: + "@nivo/recompose": 0.80.0 + "@react-spring/web": 9.4.5 + d3-color: ^2.0.0 + d3-format: ^1.4.4 + d3-interpolate: ^2.0.1 + d3-scale: ^3.2.3 + d3-scale-chromatic: ^2.0.0 + d3-shape: ^1.3.5 + d3-time-format: ^3.0.0 + lodash: ^4.17.21 + peerDependencies: + "@nivo/tooltip": 0.80.0 + prop-types: ">= 15.5.10 < 16.0.0" + react: ">= 16.14.0 < 19.0.0" + checksum: 05fd9ccc9d9876affea17498a2007f5974641c7a2bb1c12c4d8f97bdae48519dd5882c65653074b6756d14eef47dbaeaf8b8243d3e5389efd3d1e180ecbfde75 + languageName: node + linkType: hard + +"@nivo/legends@npm:0.80.0": + version: 0.80.0 + resolution: "@nivo/legends@npm:0.80.0" + peerDependencies: + "@nivo/core": 0.80.0 + prop-types: ">= 15.5.10 < 16.0.0" + react: ">= 16.14.0 < 19.0.0" + checksum: 659aba35105143ac7da663a51bc6a69f815e23efd278b8b1314bd48b9c853f4c3b7e9cf61de1439390eff3983b754eaea5dd3923fa2caad97b0fca6133a05ae9 + languageName: node + linkType: hard + +"@nivo/recompose@npm:0.80.0": + version: 0.80.0 + resolution: "@nivo/recompose@npm:0.80.0" + dependencies: + react-lifecycles-compat: ^3.0.4 + peerDependencies: + react: ">= 16.14.0 < 19.0.0" + checksum: 5495eeba137f4d3a46d1ada99948c5b097ccf00c532f10a627bb0525c6fbab14d93c9297a25d90c0041f72432efbd4e9a2ef5d408063e2c4cf25d05f33b1fcc5 + languageName: node + linkType: hard + +"@nivo/scales@npm:0.80.0": + version: 0.80.0 + resolution: "@nivo/scales@npm:0.80.0" + dependencies: + d3-scale: ^3.2.3 + d3-time: ^1.0.11 + d3-time-format: ^3.0.0 + lodash: ^4.17.21 + checksum: 75bdc838ab593306268cda2c098430cb5ff1f20ab73dd5d61845fa013e30fd85020c910c9dd0a7641d90ee04f86031af71a250f5f5992fe74a23bcf4fb954f95 + languageName: node + linkType: hard + +"@nivo/tooltip@npm:0.80.0": + version: 0.80.0 + resolution: "@nivo/tooltip@npm:0.80.0" + dependencies: + "@react-spring/web": 9.4.5 + peerDependencies: + "@nivo/core": 0.80.0 + checksum: bc0eec5d0d05f653186b040105d7451c823d8ee0844b7767f3d827424346d6379966452055be168994132ca8610f1956798274701a1bb948d3e8d961f23a07b7 + languageName: node + linkType: hard + "@noble/hashes@npm:^1.1.3": version: 1.1.3 resolution: "@noble/hashes@npm:1.1.3" @@ -4308,6 +4442,73 @@ __metadata: languageName: node linkType: hard +"@react-spring/animated@npm:~9.4.5": + version: 9.4.5 + resolution: "@react-spring/animated@npm:9.4.5" + dependencies: + "@react-spring/shared": ~9.4.5 + "@react-spring/types": ~9.4.5 + peerDependencies: + react: ^16.8.0 || >=17.0.0 || >=18.0.0 + checksum: e85c0bd65bd76e1c8ca830b22e31956401e29593cbc1df7560f5b77bd7b31acded61e1732717803cdfd993f30c2559ffbd6fb5f0d48b1c749323bee3597d7834 + languageName: node + linkType: hard + +"@react-spring/core@npm:~9.4.5": + version: 9.4.5 + resolution: "@react-spring/core@npm:9.4.5" + dependencies: + "@react-spring/animated": ~9.4.5 + "@react-spring/rafz": ~9.4.5 + "@react-spring/shared": ~9.4.5 + "@react-spring/types": ~9.4.5 + peerDependencies: + react: ^16.8.0 || >=17.0.0 || >=18.0.0 + checksum: e5aee7f68f15c9d5d6f230703d22cb34edb8aae3ba0d70c01847f7c78e47f9f8177f87c095aff5ed1b98c2a218238d5ec28f9bf451f3e13bfdad6e3170a60226 + languageName: node + linkType: hard + +"@react-spring/rafz@npm:~9.4.5": + version: 9.4.5 + resolution: "@react-spring/rafz@npm:9.4.5" + checksum: 0ac722881b107baf55338a0123bc889d88faca53f034eb6d26ebab3ae6e4dc1717654b09d0e6e5e9bf587c2ba182d6aae90ca22c833dc55024ee52d88f8579a2 + languageName: node + linkType: hard + +"@react-spring/shared@npm:~9.4.5": + version: 9.4.5 + resolution: "@react-spring/shared@npm:9.4.5" + dependencies: + "@react-spring/rafz": ~9.4.5 + "@react-spring/types": ~9.4.5 + peerDependencies: + react: ^16.8.0 || >=17.0.0 || >=18.0.0 + checksum: 2f20e410c03166de19b2d668d6841d24778c37da3083d37fe70acfcf2cf0cb3bd4a5cf92d42f1590b9de5d0a6603dc75cf8c319c0089df4e713226364a204b51 + languageName: node + linkType: hard + +"@react-spring/types@npm:~9.4.5": + version: 9.4.5 + resolution: "@react-spring/types@npm:9.4.5" + checksum: f8fecb54015de23899cc595d949e3676835e612d4dda05af470cab9ee20dd98c86ebca1c4ba75d2a9f63a4acba4b75febf6bab71da0b2e9556e6ff684b22f139 + languageName: node + linkType: hard + +"@react-spring/web@npm:9.4.5": + version: 9.4.5 + resolution: "@react-spring/web@npm:9.4.5" + dependencies: + "@react-spring/animated": ~9.4.5 + "@react-spring/core": ~9.4.5 + "@react-spring/shared": ~9.4.5 + "@react-spring/types": ~9.4.5 + peerDependencies: + react: ^16.8.0 || >=17.0.0 || >=18.0.0 + react-dom: ^16.8.0 || >=17.0.0 || >=18.0.0 + checksum: 9d7eea4b8b0399c205743acade141679f3f729a64631f8480d44d14bb59781ea807977a4671cbe1d56e31389b69ef325ec975275446f08997f555f2981d220c8 + languageName: node + linkType: hard + "@scure/base@npm:^1.1.1": version: 1.1.1 resolution: "@scure/base@npm:1.1.1" @@ -10782,6 +10983,109 @@ __metadata: languageName: node linkType: hard +"d3-array@npm:2, d3-array@npm:^2.3.0": + version: 2.12.1 + resolution: "d3-array@npm:2.12.1" + dependencies: + internmap: ^1.0.0 + checksum: 97853b7b523aded17078f37c67742f45d81e88dda2107ae9994c31b9e36c5fa5556c4c4cf39650436f247813602dfe31bf7ad067ff80f127a16903827f10c6eb + languageName: node + linkType: hard + +"d3-color@npm:1 - 2, d3-color@npm:^2.0.0": + version: 2.0.0 + resolution: "d3-color@npm:2.0.0" + checksum: b887354aa383937abd04fbffed3e26e5d6a788472cd3737fb10735930e427763e69fe93398663bccf88c0b53ee3e638ac6fcf0c02226b00ed9e4327c2dfbf3dc + languageName: node + linkType: hard + +"d3-format@npm:1 - 2": + version: 2.0.0 + resolution: "d3-format@npm:2.0.0" + checksum: c4d3c8f9941d097d514d3986f54f21434e08e5876dc08d1d65226447e8e167600d5b9210235bb03fd45327225f04f32d6e365f08f76d2f4b8bff81594851aaf7 + languageName: node + linkType: hard + +"d3-format@npm:^1.4.4": + version: 1.4.5 + resolution: "d3-format@npm:1.4.5" + checksum: 1b8b2c0bca182173bccd290a43e8b635a83fc8cfe52ec878c7bdabb997d47daac11f2b175cebbe73f807f782ad655f542bdfe18180ca5eb3498a3a82da1e06ab + languageName: node + linkType: hard + +"d3-interpolate@npm:1 - 2, d3-interpolate@npm:1.2.0 - 2, d3-interpolate@npm:^2.0.1": + version: 2.0.1 + resolution: "d3-interpolate@npm:2.0.1" + dependencies: + d3-color: 1 - 2 + checksum: 4a2018ac34fbcc3e0e7241e117087ca1b2274b8b33673913658623efacc5db013b8d876586d167b23e3145bdb34ec8e441d301299b082e1a90985b2f18d4299c + languageName: node + linkType: hard + +"d3-path@npm:1": + version: 1.0.9 + resolution: "d3-path@npm:1.0.9" + checksum: d4382573baf9509a143f40944baeff9fead136926aed6872f7ead5b3555d68925f8a37935841dd51f1d70b65a294fe35c065b0906fb6e42109295f6598fc16d0 + languageName: node + linkType: hard + +"d3-scale-chromatic@npm:^2.0.0": + version: 2.0.0 + resolution: "d3-scale-chromatic@npm:2.0.0" + dependencies: + d3-color: 1 - 2 + d3-interpolate: 1 - 2 + checksum: 9fe5b4c1d9907abbda76e414856d9089182a0641f3bbf43d8d3008dbcccb52781e21793682e2b53663d3c6cd63e76965f961894e53ed3b01a345797412fe5b1f + languageName: node + linkType: hard + +"d3-scale@npm:^3.2.3": + version: 3.3.0 + resolution: "d3-scale@npm:3.3.0" + dependencies: + d3-array: ^2.3.0 + d3-format: 1 - 2 + d3-interpolate: 1.2.0 - 2 + d3-time: ^2.1.1 + d3-time-format: 2 - 3 + checksum: f77e73f0fb422292211d0687914c30d26e29011a936ad2a535a868ae92f306c3545af1fe7ea5db1b3e67dbce7a6c6cd952e53d02d1d557543e7e5d30e30e52f2 + languageName: node + linkType: hard + +"d3-shape@npm:^1.3.5": + version: 1.3.7 + resolution: "d3-shape@npm:1.3.7" + dependencies: + d3-path: 1 + checksum: 46566a3ab64a25023653bf59d64e81e9e6c987e95be985d81c5cedabae5838bd55f4a201a6b69069ca862eb63594cd263cac9034afc2b0e5664dfe286c866129 + languageName: node + linkType: hard + +"d3-time-format@npm:2 - 3, d3-time-format@npm:^3.0.0": + version: 3.0.0 + resolution: "d3-time-format@npm:3.0.0" + dependencies: + d3-time: 1 - 2 + checksum: c20c1667dbea653f81d923e741f84c23e4b966002ba0d6ed94cbc70692105566e55e89d18d175404534a879383fd1123300bd12885a3c924fe924032bb0060db + languageName: node + linkType: hard + +"d3-time@npm:1 - 2, d3-time@npm:^2.1.1": + version: 2.1.1 + resolution: "d3-time@npm:2.1.1" + dependencies: + d3-array: 2 + checksum: d1c7b9658c20646e46c3dd19e11c38e02dec098e8baa7d2cd868af8eb01953668f5da499fa33dc63541cf74a26e788786f8828c4381dbbf475a76b95972979a6 + languageName: node + linkType: hard + +"d3-time@npm:^1.0.11": + version: 1.1.0 + resolution: "d3-time@npm:1.1.0" + checksum: 33fcfff94ff093dde2048c190ecca8b39fe0ec8b3c61e9fc39c5f6072ce5b86dd2b91823f086366995422bbbac7f74fd9abdb7efe4f292a73b1c6197c699cc78 + languageName: node + linkType: hard + "d@npm:1, d@npm:^1.0.1": version: 1.0.1 resolution: "d@npm:1.0.1" @@ -15262,6 +15566,13 @@ __metadata: languageName: node linkType: hard +"internmap@npm:^1.0.0": + version: 1.0.1 + resolution: "internmap@npm:1.0.1" + checksum: 9d00f8c0cf873a24a53a5a937120dab634c41f383105e066bb318a61864e6292d24eb9516e8e7dccfb4420ec42ca474a0f28ac9a6cc82536898fa09bbbe53813 + languageName: node + linkType: hard + "interpret@npm:^2.2.0": version: 2.2.0 resolution: "interpret@npm:2.2.0" @@ -22407,6 +22718,8 @@ __metadata: "@metamask/eslint-config-nodejs": ^10.0.0 "@metamask/eslint-config-typescript": ^10.0.0 "@metamask/providers": ^9.0.0 + "@nivo/bar": ^0.80.0 + "@nivo/core": ^0.80.0 "@storybook/addon-actions": ^6.5.16 "@storybook/addon-docs": ^6.5.16 "@storybook/addon-essentials": ^6.5.16