diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/.env.example b/sample-dapps/ethereum-dex-trade-performance-analyzer/.env.example new file mode 100644 index 0000000..0609644 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/.env.example @@ -0,0 +1 @@ +VITE_QUICKNODE_ENDPOINT = "YOUR_QUICKNODE_ETHEREUM_ENDPOINT_URL" \ No newline at end of file diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/.gitignore b/sample-dapps/ethereum-dex-trade-performance-analyzer/.gitignore new file mode 100644 index 0000000..438657a --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local +.env + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/README.md b/sample-dapps/ethereum-dex-trade-performance-analyzer/README.md new file mode 100644 index 0000000..801c5c3 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/README.md @@ -0,0 +1,94 @@ +# DEX Trade Performance Analyzer + +## Introduction + +This application is designed to analyze the trading performance of a crypto wallet, leveraging the capabilities of Syve's [DEX Price Data & Wallet Tracking APIs](https://marketplace.quicknode.com/add-on/syve-dex-price-data-and-wallet-tracking-api). The primary focus of this application is to provide users with detailed insights into wallet trading activities, including profit and loss metrics for each traded token. + +For an in-depth guide on how to fetch data and develop further functionalities, refer to [our comprehensive guide on QuickNode](https://www.quicknode.com/guides/quicknode-products/marketplace/how-to-track-dex-wallet-trading-performance). + +### Tech Stack + +- Frontend Framework/Library: React +- Language: TypeScript +- Build Tool/Development Server: Vite +- Styling: Tailwind CSS + +## Features + +- **Overall Wallet Performance**: Displays total profit and loss, win rate, and other key metrics using Syve's Wallet Tracking API. +- **Token-Specific Performance**: Provides detailed trading metrics for each token held in the wallet. + +## Getting Started + +### Prerequisites + +Before you begin, ensure you have the following: + +- [Node.js](https://nodejs.org/en/) installed on your system. +- A QuickNode account with the [DEX Price Data & Wallet Tracking APIs](https://marketplace.quicknode.com/add-on/syve-dex-price-data-and-wallet-tracking-api) enabled. +- A code editor or an IDE (e.g., [VS Code](https://code.visualstudio.com/)) +- [TypeScript](https://www.typescriptlang.org/) and [ts-node](https://typestrong.org/ts-node/) + +> You can run the commands below to install TypeScript and ts-node globally to have TypeScript available across all projects. + +```bash +npm install -g typescript ts-node +``` + +### Installation Dependencies + +1. Clone the repository to your local machine: + +```bash +git clone https://github.com/quiknode-labs/qn-guide-examples.git +``` + +2. Navigate to the project directory: + +```bash +cd sample-dapps/ethereum-dex-trade-performance-analyzer +``` + +3. Install the necessary dependencies: + +```bash +npm install +``` + +### Setting Environment Variables + +Rename `.env.example` to `.env` and replace the `YOUR_QUICKNODE_ENDPOINT_URL` placeholder with your QuickNode Ethereum Node Endpoint. Make sure that the [DEX Price Data & Wallet Tracking APIs](https://marketplace.quicknode.com/add-on/syve-dex-price-data-and-wallet-tracking-api) are enabled. + +```env +VITE_QUICKNODE_ENDPOINT="YOUR_QUICKNODE_ENDPOINT_URL" +``` + +> Please note that while we utilize `dotenv` for environment variable management, sensitive information like endpoints can still be visible on the frontend. This configuration is not recommended for production environments as-is. + +### Running the Application + +Run the development server: + +```bash +npm run dev +``` + +Open [http://localhost:5173/](http://localhost:5173/) with your browser to see the application. + +## Using the App + +1. Input a wallet address. +2. Press **Analyze Wallet**. +3. View the overall and token-specific trading performance. + +The **Wallet Performance Analyzer** will fetch the data using Syve's Wallet Tracking APIs, and display the results. + +Overall Performance | Token Specific Performance +:-------------------------:|:-------------------------: +![App Overall Performance](public/app-overview.png) | ![App Token Specific Performance](public/app-token-details.png) + +## Conclusion + +Syve's [DEX Price Data & Wallet Tracking APIs](https://marketplace.quicknode.com/add-on/syve-dex-price-data-and-wallet-tracking-api) provide developers with powerful tools for analyzing wallet trading activities. By leveraging these APIs, developers can create applications that deliver valuable insights into crypto trading performance. + +[QuickNode](https://quicknode.com/) offers many different marketplace add-ons, providing developers with the resources needed to create robust and feature-rich crypto applications. To learn more about how these APIs and other QuickNode add-ons can benefit your projects, please [contact us](https://www.quicknode.com/contact-us); we're eager to assist you! \ No newline at end of file diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/eslint.config.js b/sample-dapps/ethereum-dex-trade-performance-analyzer/eslint.config.js new file mode 100644 index 0000000..c4bc8f3 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/eslint.config.js @@ -0,0 +1,26 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config({ + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + ignores: ['dist'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +}) diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/index.html b/sample-dapps/ethereum-dex-trade-performance-analyzer/index.html new file mode 100644 index 0000000..ae87a91 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/index.html @@ -0,0 +1,13 @@ + + + + + + + DEX Trade Performance Analyzer + + +
+ + + diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/package.json b/sample-dapps/ethereum-dex-trade-performance-analyzer/package.json new file mode 100644 index 0000000..9bcb51b --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/package.json @@ -0,0 +1,35 @@ +{ + "name": "dex-trade-analyzer", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@quicknode/sdk": "^2.3.0", + "dotenv": "^16.4.5", + "react": "^18.3.1", + "react-dom": "^18.3.1" + }, + "devDependencies": { + "@eslint/js": "^9.8.0", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.20", + "axios": "^1.7.3", + "eslint": "^9.8.0", + "eslint-plugin-react-hooks": "^5.1.0-rc.0", + "eslint-plugin-react-refresh": "^0.4.9", + "npm": "^10.8.2", + "postcss": "^8.4.41", + "tailwindcss": "^3.4.9", + "typescript": "^5.5.3", + "typescript-eslint": "^8.0.0", + "vite": "^5.4.0" + } +} \ No newline at end of file diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/postcss.config.js b/sample-dapps/ethereum-dex-trade-performance-analyzer/postcss.config.js new file mode 100644 index 0000000..2e7af2b --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/public/app-overview.png b/sample-dapps/ethereum-dex-trade-performance-analyzer/public/app-overview.png new file mode 100644 index 0000000..efb6d4a Binary files /dev/null and b/sample-dapps/ethereum-dex-trade-performance-analyzer/public/app-overview.png differ diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/public/app-token-details.png b/sample-dapps/ethereum-dex-trade-performance-analyzer/public/app-token-details.png new file mode 100644 index 0000000..1d70464 Binary files /dev/null and b/sample-dapps/ethereum-dex-trade-performance-analyzer/public/app-token-details.png differ diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/public/vite.svg b/sample-dapps/ethereum-dex-trade-performance-analyzer/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/App.css b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/App.tsx b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/App.tsx new file mode 100644 index 0000000..0f81ed1 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/App.tsx @@ -0,0 +1,95 @@ +import React, { useState } from "react"; +import { OverallStats } from "./interfaces/OverallStats"; +import { TokenPerformance } from "./interfaces/TokenPerformance"; +import { + getWalletLatestTotalPerformance, + getWalletLatestPerformancePerToken, +} from "./services/api"; +import WalletSearch from "./components/WalletSearch"; +import OverallStatsDisplay from "./components/OverallStatsDisplay"; +import TokenPerformanceTable from "./components/TokenPerformanceTable"; + +const App: React.FC = () => { + const [overallStats, setOverallStats] = useState(null); + const [tokenPerformance, setTokenPerformance] = useState( + [] + ); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [dataFetched, setDataFetched] = useState(false); + + const handleSearch = async (address: string) => { + setLoading(true); + setError(null); + setDataFetched(false); + + try { + const [overallStatsData, tokenPerformanceData] = await Promise.all([ + getWalletLatestTotalPerformance(address), + getWalletLatestPerformancePerToken(address), + ]); + + setOverallStats(overallStatsData); + setTokenPerformance(tokenPerformanceData.data || []); + setDataFetched(true); + } catch (err) { + if ( + err instanceof Error && + err.message.includes("does not exist/is not available") + ) { + setError( + "Add-on is not enabled. Please visit your QN dashboard and enable the add-on." + ); + } else { + setError("Error fetching wallet data. Please try again."); + } + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+
+

+ DEX Trade Performance Analyzer +

+ + {loading &&

Loading...

} + {error &&

{error}

} + {dataFetched && overallStats && ( +
+

+ Overall Performance +

+ +
+ )} + {dataFetched && tokenPerformance.length > 0 && ( +
+

+ Token Performance +

+ +
+ )} + {dataFetched && tokenPerformance.length === 0 && ( +

+ No token performance data available for this wallet. +

+ )} + {!loading && !error && !dataFetched && ( +

+ Enter a wallet address to see the analysis. +

+ )} +
+
+
+
+ ); +}; + +export default App; diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/assets/react.svg b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/OverallStatsDisplay.tsx b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/OverallStatsDisplay.tsx new file mode 100644 index 0000000..853d8be --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/OverallStatsDisplay.tsx @@ -0,0 +1,187 @@ +import React from "react"; +import { OverallStats } from "../interfaces/OverallStats"; + +interface OverallStatsDisplayProps { + stats: OverallStats; +} + +const OverallStatsDisplay: React.FC = ({ stats }) => { + const formatDate = (timestamp: number) => { + return new Date(timestamp * 1000).toLocaleString(); + }; + + const colorClass = (value: number) => + value >= 0 ? "text-green-600" : "text-red-600"; + + return ( +
+
+

+ Wallet: {stats.wallet_address} +

+
+
+
+
+
+
PnL
+
+ {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(stats.pnl)} +
+
+
+
+ Total Trades +
+
+ {stats.total_trades} +
+
+
+
+
+
Win Rate
+
+ {(stats.win_rate * 100).toFixed(2)}% +
+
+
+
Total Value
+
+ {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(stats.total_value)} +
+
+
+
+
+
First Trade
+
+ {formatDate(stats.first_trade_timestamp)} +
+
+
+
Last Trade
+
+ {formatDate(stats.last_trade_timestamp)} +
+
+
+
+
+
+ Realized Profit +
+
+ {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(stats.realized_profit)} +
+
+
+
+ Realized Return +
+
+ {stats.realized_return.toFixed(2)}% +
+
+
+
+
+
+ Unrealized Profit +
+
+ {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(stats.unrealized_profit)} +
+
+
+
+ Unrealized Return +
+
+ {stats.unrealized_return.toFixed(2)}% +
+
+
+
+
+
+ Total Buy Volume +
+
+ {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(stats.total_buy_volume)} +
+
+
+
+ Total Sell Volume +
+
+ {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(stats.total_sell_volume)} +
+
+
+
+
+
+ Total Buy Txs +
+
+ {stats.total_buys} +
+
+
+
+ Total Sell Txs +
+
+ {stats.total_sells} +
+
+
+
+
+
+ ); +}; + +export default OverallStatsDisplay; diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/TokenPerformanceTable.tsx b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/TokenPerformanceTable.tsx new file mode 100644 index 0000000..3c2ef30 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/TokenPerformanceTable.tsx @@ -0,0 +1,211 @@ +import React, { useState } from "react"; +import { TokenPerformance } from "../interfaces/TokenPerformance"; + +interface TokenPerformanceTableProps { + performance: TokenPerformance[]; +} + +const TokenPerformanceTable: React.FC = ({ + performance, +}) => { + const [expandedRows, setExpandedRows] = useState>(new Set()); + + const toggleRow = (tokenAddress: string) => { + const newExpandedRows = new Set(expandedRows); + if (newExpandedRows.has(tokenAddress)) { + newExpandedRows.delete(tokenAddress); + } else { + newExpandedRows.add(tokenAddress); + } + setExpandedRows(newExpandedRows); + }; + + const formatDate = (timestamp: number) => { + return new Date(timestamp * 1000).toLocaleString(); + }; + + const getEtherscanUrl = (tokenAddress: string) => { + return `https://etherscan.io/token/${tokenAddress}`; + }; + + return ( +
+ + + + + + + + + + + + + + + {performance.map((token) => ( + + + + + + + + + + + + {expandedRows.has(token.token_address) && ( + + + + )} + + ))} + +
+ Token + + PNL + + Total Trades + + Total Buy + + Total Sell + + Avg Buy Price + + Current Price + + Actions +
+
+ + {token.token_symbol} + +
+ ({token.token_name}) +
+
+
+
= 0 ? "text-green-600" : "text-red-600" + }`} + > + {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(token.pnl)} +
+
+ {token.total_trades} + + {token.total_buy_amount.toFixed(4)} + + {token.total_sell_amount.toFixed(4)} + + {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 4, + }).format(token.avg_buy_price)} + + {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 4, + }).format(token.current_price)} + + +
+
+
+

+ First Trade:{" "} + {formatDate(token.first_trade_timestamp)} +

+

+ Last Trade:{" "} + {formatDate(token.last_trade_timestamp)} +

+

+ Total Buy Volume: + {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(token.total_buy_volume)} +

+

+ Total Sell Volume: + {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(token.total_sell_volume)} +

+
+
+

+ Realized Profit: + {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(token.realized_profit)} +

+

+ Unrealized Profit: + {new Intl.NumberFormat(undefined, { + style: "currency", + currency: "USD", + maximumFractionDigits: 2, + }).format(token.unrealized_profit)} +

+
+
+
+
+ ); +}; + +export default TokenPerformanceTable; diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/WalletSearch.tsx b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/WalletSearch.tsx new file mode 100644 index 0000000..284a256 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/components/WalletSearch.tsx @@ -0,0 +1,64 @@ +import React, { useState, useEffect } from "react"; +import { viem } from "@quicknode/sdk"; + +interface WalletSearchProps { + onSearch: (address: string) => void; +} + +const WalletSearch: React.FC = ({ onSearch }) => { + const [address, setAddress] = useState(""); + const [isValidAddress, setIsValidAddress] = useState(false); + + useEffect(() => { + setIsValidAddress(viem.isAddress(address)); + }, [address]); + + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + if (isValidAddress) { + onSearch(address.trim()); + } + }; + + return ( +
+
+
+ + setAddress(e.target.value)} + /> +
+
+ {!isValidAddress && address && ( +

+ Please enter a valid EVM wallet address. +

+ )} +
+ +
+
+ ); +}; + +export default WalletSearch; diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/index.css b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/index.css new file mode 100644 index 0000000..a31e444 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/index.css @@ -0,0 +1,3 @@ +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/interfaces/OverallStats.ts b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/interfaces/OverallStats.ts new file mode 100644 index 0000000..3c93595 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/interfaces/OverallStats.ts @@ -0,0 +1,25 @@ +export interface OverallStats { + first_trade_timestamp: number; + last_trade_timestamp: number; + pnl: number; + realized_investment: number; + realized_profit: number; + realized_return: number; + realized_value: number; + total_buy_volume: number; + total_buys: number; + total_investment: number; + total_profit: number; + total_return: number; + total_sell_volume: number; + total_sells: number; + total_tokens_traded: number; + total_trades: number; + total_value: number; + unrealized_investment: number; + unrealized_profit: number; + unrealized_return: number; + unrealized_value: number; + wallet_address: string; + win_rate: number; +} diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/interfaces/TokenPerformance.ts b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/interfaces/TokenPerformance.ts new file mode 100644 index 0000000..f803c2d --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/interfaces/TokenPerformance.ts @@ -0,0 +1,35 @@ +export interface TokenPerformanceResult { + data: TokenPerformance[] +} + +export interface TokenPerformance { + avg_buy_price: number; + avg_sell_price?: number; + current_price: number; + first_trade_timestamp: number; + last_trade_timestamp: number; + pnl: number; + realized_investment: number; + realized_profit: number; + realized_return?: number; + realized_value: number; + token_address: string; + token_name: string; + token_symbol: string; + total_buy_amount: number; + total_buy_volume: number; + total_buys: number; + total_investment: number; + total_profit: number; + total_return?: number; + total_sell_amount: number; + total_sell_volume: number; + total_sells: number; + total_trades: number; + total_value: number; + trading_balance: number; + unrealized_investment: number; + unrealized_profit: number; + unrealized_return?: number; + unrealized_value: number; +} diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/main.tsx b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/main.tsx new file mode 100644 index 0000000..6f4ac9b --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' +import './index.css' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/services/api.ts b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/services/api.ts new file mode 100644 index 0000000..9871d86 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/services/api.ts @@ -0,0 +1,48 @@ +import axios from "axios"; +import { OverallStats } from "../interfaces/OverallStats"; +import { TokenPerformanceResult } from "../interfaces/TokenPerformance"; + +const QUICKNODE_ENDPOINT = import.meta.env.VITE_QUICKNODE_ENDPOINT as string; + +const makeRpcCall = async (method: string, params: any) => { + const response = await axios.post( + QUICKNODE_ENDPOINT, + { + method, + params, + id: 1, + jsonrpc: "2.0", + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + + if (response.data.error) { + throw new Error(response.data.error.message); + } + + return response.data.result; +}; + +export const getWalletLatestTotalPerformance = async ( + walletAddress: string +): Promise => { + const result = await makeRpcCall("v1/getWalletLatestTotalPerformance", { + wallet_address: walletAddress, + max_size_ok: "true", + }); + return result as OverallStats; +}; + +export const getWalletLatestPerformancePerToken = async ( + walletAddress: string +): Promise => { + const result = await makeRpcCall("v1/getWalletLatestPerformancePerToken", { + wallet_address: walletAddress, + max_size_ok: "true", + }); + return result as TokenPerformanceResult; +}; diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/src/vite-env.d.ts b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/tailwind.config.js b/sample-dapps/ethereum-dex-trade-performance-analyzer/tailwind.config.js new file mode 100644 index 0000000..c8747b8 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/tailwind.config.js @@ -0,0 +1,23 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: { + colors: { + primary: { + 50: "#e6f0ff", + 100: "#cce0ff", + 200: "#99c2ff", + 300: "#66a3ff", + 400: "#3385ff", + 500: "#0066ff", + 600: "#0052cc", + 700: "#003d99", + 800: "#002966", + 900: "#001433", + }, + }, + }, + }, + plugins: [], +}; diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.app.json b/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.app.json new file mode 100644 index 0000000..f0a2350 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.app.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.json b/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.node.json b/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.node.json new file mode 100644 index 0000000..0d3d714 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/tsconfig.node.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/sample-dapps/ethereum-dex-trade-performance-analyzer/vite.config.ts b/sample-dapps/ethereum-dex-trade-performance-analyzer/vite.config.ts new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/sample-dapps/ethereum-dex-trade-performance-analyzer/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +})