diff --git a/frontend/package-lock.json b/frontend/package-lock.json index aed6fa4..7f3eb18 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,8 +11,10 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "bootstrap": "^5.3.2", "ethers": "^6.9.2", "react": "^18.2.0", + "react-bootstrap": "^2.9.2", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" @@ -3380,6 +3382,68 @@ } } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.1.tgz", + "integrity": "sha512-NqzkLFP8ZVI4GSorS0AYljC13QW2sc8bDqJOkBvkAt3M8gbcAXJWVRGtZBCRscki9RZF+rNlnPdg0G0jYkhJcg==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@restart/hooks": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.15.tgz", + "integrity": "sha512-cZFXYTxbpzYcieq/mBwSyXgqnGMHoBVh3J7MU0CCoIB4NRZxV9/TuwTBAaLMqpNhC3zTPMCgkQ5Ey07L02Xmcw==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz", + "integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -3698,6 +3762,14 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@swc/helpers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", + "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@testing-library/dom": { "version": "9.3.4", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", @@ -4499,6 +4571,14 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/resolve": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", @@ -4575,6 +4655,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" + }, "node_modules/@types/ws": { "version": "8.5.10", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", @@ -5849,6 +5934,24 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, + "node_modules/bootstrap": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz", + "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -6114,6 +6217,11 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==" }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/clean-css": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", @@ -7074,6 +7182,15 @@ "utila": "~0.4" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dom-serializer": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", @@ -9531,6 +9648,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/ipaddr.js": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", @@ -14677,6 +14802,23 @@ "react-is": "^16.13.1" } }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, + "node_modules/prop-types-extra/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -14851,6 +14993,35 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, + "node_modules/react-bootstrap": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.9.2.tgz", + "integrity": "sha512-a36B+EHsAI/aH+ZhXNILBFnqscE3zr10dWmjBmfhIb2QR7KSXJiGzYd6Faf/25G8G7/CP9TCL2B0WhUBOD2UBQ==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.6", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -14990,6 +15161,11 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -15070,6 +15246,21 @@ } } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -17069,6 +17260,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/underscore": { "version": "1.12.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", @@ -17296,6 +17501,14 @@ "makeerror": "1.0.12" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 0a2b2b3..03d2fc0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,8 +6,10 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", + "bootstrap": "^5.3.2", "ethers": "^6.9.2", "react": "^18.2.0", + "react-bootstrap": "^2.9.2", "react-dom": "^18.2.0", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" diff --git a/frontend/src/App.css b/frontend/src/App.css index 5132692..8e63ca1 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -17,7 +17,7 @@ font-size: 24px; margin-bottom: 24px; color: white; - background-color: orange; + background-color: #ff6056; padding: 12px; font-weight: bold; margin-right: 16px; @@ -25,15 +25,29 @@ } input { - font-size: 16px; - padding: 12px; + font-size: 15px; + padding: 6px; border-radius: 8px; + text-align: center; + width: 180px; + height: 30px; + margin-bottom: 16px; } .description { - background-color: orange; + background-color: #ff6056; border-radius: 24px; - padding: 24px; + padding: 36px; + + margin-bottom: 36px; +} - margin-bottom: 64px; +#dropdown-basic{ + color: white; + font-weight: bold; + background-color: #ff6056; + font-size: 15px; + border-radius: 16px; + margin-top: 12px; + margin-bottom: 12px; } diff --git a/frontend/src/App.js b/frontend/src/App.js index 4a66758..5edfbe2 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -2,20 +2,28 @@ import { useEffect, useState } from 'react'; import Ojo from './artifacts/contracts/Ojo.sol/Ojo.json'; import MockOjo from './artifacts/contracts/MockOjoContract.sol/MockOjoContract.json'; import IERC20 from './artifacts/@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol/IERC20.json' +import AssetForm from './components/AssetForm'; +import SymbolDropdown from './components/SymbolDropdown'; +import PriceTable from './components/PriceTable'; +import 'bootstrap/dist/css/bootstrap.min.css'; import './App.css'; const ethers = require("ethers") function App() { - const [assetNames, setAssetNames] = useState(''); + const [assetNames, setAssetNames] = useState([]); const [symbol, setSymbol] = useState(''); const [amount, setAmount] = useState(''); - const [tokenAddress, setTokenAddress] = useState(''); const [priceData, setPriceData] = useState([]); + const [selectAll, setSelectAll] = useState(false); + + const symbolMap = { + "aUSDC": "0x254d06f33bDc5b8ee05b2ea472107E300226659A" + }; let signer = null; let provider; if (window.ethereum == null) { - console.log("MetaMask not installed; using read-only defaults") + alert("MetaMask not installed; using read-only defaults") provider = ethers.getDefaultProvider() } else { provider = new ethers.BrowserProvider(window.ethereum) @@ -38,12 +46,15 @@ function App() { }) const relayOjoPriceDataWithToken = async () => { - if (!assetNames || !symbol || !amount || !tokenAddress) return; - const tokenContract = new ethers.Contract(tokenAddress, IERC20.abi, signer); + if (assetNames.length === 0 || !symbol || !amount) { + setPriceData([]); + return + } + const tokenContract = new ethers.Contract(symbolMap[symbol], IERC20.abi, signer); const mockOjoContract = new ethers.Contract(mockOjoAddress, MockOjo.abi, signer); if (typeof window.ethereum !== "undefined") { - const assetNamesArray = assetNames.split(',').map(name => ethers.encodeBytes32String(name.trim())); + const assetNamesArray = assetNames.map(name => ethers.encodeBytes32String(name.trim())); try { // increase the allowance of MockOjo contract const tx1 = await tokenContract.approve(mockOjoAddress, amount); @@ -53,26 +64,26 @@ function App() { assetNamesArray, symbol, amount, - tokenAddress, + symbolMap[symbol], { value: ethers.parseEther("0.01") } ); await tx2.wait(); alert('Ojo Price Data relay request sent successfully!'); } catch(error) { - alert(error) console.error('Error sending Ojo Price Data relay request:', error); + alert('Failed to send Ojo Price Data relay request') } } } const displayRelayedPrices = async () => { - if (!assetNames || assetNames.length === 0) { + if (assetNames.length === 0) { setPriceData([]); return } const ojoContract = new ethers.Contract(ojoAddress, Ojo.abi, signer); - const assetNamesArray = assetNames.split(',').map(name => ethers.encodeBytes32String(name)); + const assetNamesArray = assetNames.map(name => ethers.encodeBytes32String(name)); try { const data = await ojoContract.getPriceDataBulk(assetNamesArray); if (!data || data.length === 0) { @@ -83,7 +94,7 @@ function App() { } } catch (error) { console.error('Error fetching price data:', error); - alert('Failed to fetch price data.'); + alert('Failed to fetch price data'); } }; @@ -96,25 +107,21 @@ function App() {
- +
- setAssetNames(e.target.value)} placeholder="Enter asset names separated by commas" /> - setSymbol(e.target.value)} placeholder="Enter token symbol" /> - setAmount(e.target.value)} placeholder="Enter amount" /> - setTokenAddress(e.target.value)} placeholder="Enter token address" /> -
- {priceData.map((data, index) => { - if (assetNames.length === 0 || !data || data.length === 0 || data.assetName === undefined || data.price === undefined) { - return
No data available for this asset.
; - } - return ( -
-

Asset: {ethers.decodeBytes32String(data.assetName)}

-

Price: {data.price.toString()}

-
- ); - })} -
+ + + setAmount(e.target.value)} placeholder="Enter fee token amount" /> + ); diff --git a/frontend/src/components/AssetForm.js b/frontend/src/components/AssetForm.js new file mode 100644 index 0000000..a03acdf --- /dev/null +++ b/frontend/src/components/AssetForm.js @@ -0,0 +1,54 @@ +import React from 'react'; +import { Form } from 'react-bootstrap'; + +const AssetForm = ({ assetNames, setAssetNames, selectAll, setSelectAll }) => { + const availableAssets = [ + "ATOM","AXL","BNB","BTC","CMDX","CMST","CRV","DAI","DOT","ETH", + "INJ","IST","JUNO","KUJI","LINK","LUNA","MATIC","MKR","MNTA", + "OSMO","RETH","SCRT","SEI","STARS","STATOM","STJUNO","STOSMO", + "SUSHI","USDC","USDT","USK","WBTC","WETH","XRP" + ]; + + const handleSelectAllChange = () => { + if (selectAll) { + setAssetNames([]); + } else { + setAssetNames(availableAssets); + } + setSelectAll(!selectAll); + }; + + const handleSwitchChange = (asset) => { + if (assetNames.includes(asset)) { + setAssetNames(assetNames.filter(a => a !== asset)); + } else { + setAssetNames([...assetNames, asset]); + } + }; + + return ( +
+ + {availableAssets.map((asset, index) => ( + handleSwitchChange(asset)} + checked={assetNames.includes(asset)} + /> + ))} + + ); +}; + +export default AssetForm; diff --git a/frontend/src/components/PriceTable.js b/frontend/src/components/PriceTable.js new file mode 100644 index 0000000..89a93a0 --- /dev/null +++ b/frontend/src/components/PriceTable.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { Table } from 'react-bootstrap'; +import { ethers } from 'ethers'; + +const PriceTable = ({ priceData }) => { + const formatter = new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + }); + + return ( + + + + + + + + + + {priceData.map((data, index) => ( + + + + + + ))} + +
AssetPriceTimestamp
{ ethers.decodeBytes32String(data.assetName) }{ formatter.format(Number(data.price) / 1000000000) }{ new Date(Number(data.resolveTime) * 1000).toLocaleString() }
+ ); +}; + +export default PriceTable; diff --git a/frontend/src/components/SymbolDropdown.js b/frontend/src/components/SymbolDropdown.js new file mode 100644 index 0000000..23950a2 --- /dev/null +++ b/frontend/src/components/SymbolDropdown.js @@ -0,0 +1,25 @@ +import React from 'react'; +import { Dropdown } from 'react-bootstrap'; + +const SymbolDropdown = ({ symbolMap, symbol, setSymbol }) => { + const handleSelectSymbol = (key) => { + setSymbol(key); + }; + + return ( + + + {symbol || 'Select Fee Token'} + + + + None + {Object.keys(symbolMap).map((symbol, index) => ( + {symbol} + ))} + + + ); +}; + +export default SymbolDropdown;