Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CON-182] Ethereum Address Appearances #64

Merged
merged 4 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sample-dapps/ethereum-address-appearances/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VITE_QUICKNODE_ENDPOINT = "YOUR_QUICKNODE_ETHEREUM_ENDPOINT_URL"
18 changes: 18 additions & 0 deletions sample-dapps/ethereum-address-appearances/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
plugins: ['react-refresh'],
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
26 changes: 26 additions & 0 deletions sample-dapps/ethereum-address-appearances/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

.env
97 changes: 97 additions & 0 deletions sample-dapps/ethereum-address-appearances/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Ethereum Address Appearances Application

## Introduction

This application is designed to fetch and analyze Ethereum transactions associated with a specific address, leveraging the capabilities of QuickNode's [Address Appearances API](https://marketplace.quicknode.com/add-on/address-appearances-api). The primary focus of this application is to provide users with transaction appearances using the **Address Appearances API**. However, if you would like to compare these results with Etherscan's API, you can provide an Etherscan API key to enable the comparison feature.

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/improve-your-ethereum-audits-with-address-appearances-api).

### Tech Stack
- Frontend Framework/Library: React
- Language: TypeScript
- Build Tool/Development Server: Vite
- Styling: Tailwind CSS

## Features

- **Transaction Appearances**: Provides detailed transaction appearances using QuickNode's Address Appearances API.
- **Optional Transaction Comparison**: Enables comparison with Etherscan API if an Etherscan API key is provided.

## 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 [Address Appearances API](https://marketplace.quicknode.com/add-on/address-appearances-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-address-appearances
```

3. Install the necessary dependencies:
```bash
npm install
```

### Setting Environment Variables

Rename `.env.example` to `.env` and replace the `YOUR_QUICKNODE_ETHEREUM_ENDPOINT_URL` placeholder with your QuickNode Ethereum Node Endpoint. Make sure that the [Address Appearances API](https://marketplace.quicknode.com/add-on/address-appearances-api) is enabled.

```env
VITE_QUICKNODE_ENDPOINT = "YOUR_QUICKNODE_ETHEREUM_ENDPOINT_URL"
```

If you provide an Etherscan API key in the .env file like the one below, the app displays appearance results from both sources for a specified address.

```env
VITE_QUICKNODE_ENDPOINT="YOUR_QUICKNODE_ETHEREUM_ENDPOINT_URL"
VITE_ETHERSCAN_API_KEY="YOUR_ETHERSCAN_API_KEY"
```

> 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 an Ethereum address.
2. Press Generate.
3. View the transaction appearances.

The **Ethereum Address Appearances Application** will query the Ethereum blockchain for the address's transactions, fetch the data using QuickNode's Address Appearances API, and display the results.

![Results with Address Appearances API](public/results-address-appearances.png)

If an Etherscan API key is provided, the app will display the comparison of transactions found by QuickNode's Address Appearances API and Etherscan API.

![Results with both API](public/image.png)

## Conclusion

QuickNode's [Address Appearances API](https://marketplace.quicknode.com/add-on/address-appearances-api) excels in identifying more transaction appearances compared to other sources. This enhanced capability provides developers and businesses with more comprehensive and accurate transaction data. By leveraging this API, users can gain deeper insights into blockchain interactions.

Whether for audit purposes, regulatory compliance, or market analysis, QuickNode's Address Appearances API ensures you have the most detailed and accurate transaction data available. To discover more about how QuickNode assists companies and individuals in extracting comprehensive blockchain data, please [contact us](https://www.quicknode.com/contact-us); we're eager to engage with you!
13 changes: 13 additions & 0 deletions sample-dapps/ethereum-address-appearances/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ethereum Transactions Index Comparison by QuickNode</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
35 changes: 35 additions & 0 deletions sample-dapps/ethereum-address-appearances/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "ethereum-address-appearances",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@quicknode/sdk": "^2.2.2",
"axios": "^1.6.8",
"chart.js": "^4.4.2",
"dotenv": "^16.4.5",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.19",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.6",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.3",
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
}
6 changes: 6 additions & 0 deletions sample-dapps/ethereum-address-appearances/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions sample-dapps/ethereum-address-appearances/public/vite.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions sample-dapps/ethereum-address-appearances/src/App.css
Original file line number Diff line number Diff line change
@@ -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;
}
100 changes: 100 additions & 0 deletions sample-dapps/ethereum-address-appearances/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useState, useEffect } from "react";
import Header from "./components/Header";
import Footer from "./components/Footer";
import AddressInputForm from "./components/AddressInputForm";
import ComparisonTable from "./components/ComparisonTable";
import TransactionSummary from "./components/TransactionSummary";
import AddressAppearancesResults from "./components/AddressAppearancesResults"; // New component for Address Appearances results

import compareData from "./helpers/compareData";
import fetchTransactions from "./helpers/fetchData";
import {
Appearance,
SimplifiedEtherscanTransaction,
CombinedTransactionData,
} from "./interfaces";

const ETHERSCAN_API_KEY = import.meta.env.VITE_ETHERSCAN_API_KEY || undefined;

const App: React.FC = () => {
const [address, setAddress] = useState<string>("");
const [errorMsg, setErrorMsg] = useState<string>("");
const [customData, setCustomData] = useState<Appearance[]>([]);
const [etherscanData, setEtherscanData] = useState<{
[key: string]: SimplifiedEtherscanTransaction[];
}>({});
const [comparisonTable, setComparisonTable] = useState<
CombinedTransactionData[]
>([]);
const [loading, setLoading] = useState<boolean>(false);
const [customTotal, setCustomTotal] = useState<number>(0);
const [etherscanTotals, setEtherscanTotals] = useState<{
[key: string]: number;
}>({});

const handleFormSubmit = async (address: string) => {
try {
setLoading(true);
setCustomData([]);
setEtherscanData({});
setErrorMsg("");

const { customMethodData, esData, customTotal, etherscanTotals } =
await fetchTransactions(address);

setCustomData(customMethodData);
setEtherscanData(esData);
setCustomTotal(customTotal);
setEtherscanTotals(etherscanTotals);
} catch (error) {
console.error("Error getting data:", error);
setErrorMsg("Error getting data");
} finally {
setLoading(false);
}
};

useEffect(() => {
if (customData.length > 0) {
if (ETHERSCAN_API_KEY && Object.keys(etherscanData).length > 0) {
const comparisonResult = compareData(customData, etherscanData);
setComparisonTable(comparisonResult);
} else {
setComparisonTable([]);
}
}
}, [customData, etherscanData]);

return (
<div className="flex flex-col min-h-screen">
<Header />
<AddressInputForm
onSubmit={handleFormSubmit}
setAddress={setAddress}
isLoading={loading}
/>
{errorMsg && (
<div className="mt-4 mx-auto text-red-600 bg-red-100 border border-red-400 rounded p-2 w-1/2 max-w-sm text-center justify-center">
{errorMsg}
</div>
)}

{ETHERSCAN_API_KEY && comparisonTable.length > 0 && customTotal > 0 && (
<div>
<ComparisonTable data={comparisonTable} />
<TransactionSummary
address={address}
customTotal={customTotal}
etherscanTotals={etherscanTotals}
/>
</div>
)}
{!ETHERSCAN_API_KEY && customData.length > 0 && (
<AddressAppearancesResults data={customData} />
)}
<Footer />
</div>
);
};

export default App;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading