├── backend ├── src │ ├── config │ │ ├── index.ts │ │ ├── environments │ │ │ ├── index.ts │ │ │ ├── .env.example │ │ │ └── env.interface.ts │ │ └── swagger.config.ts │ ├── modules │ │ ├── coins │ │ │ ├── index.ts │ │ │ └── chainlink │ │ │ │ ├── contract │ │ │ │ ├── index.ts │ │ │ │ ├── chainlink-token.address.ts │ │ │ │ └── chainlink-token.abi.ts │ │ │ │ ├── index.ts │ │ │ │ ├── dto │ │ │ │ ├── index.ts │ │ │ │ ├── get-balance-param.dto.ts │ │ │ │ ├── get-balance-response.dto.ts │ │ │ │ ├── get-price-response.dto..ts │ │ │ │ ├── get-total-supply-response.dto.ts │ │ │ │ └── get-details-response.dto.ts │ │ │ │ ├── chainlink.service.ts │ │ │ │ ├── chainlink.module.ts │ │ │ │ ├── chainlink.service.spec.ts │ │ │ │ ├── chainlink.controller.ts │ │ │ │ └── chainlink.controller.spec.ts │ │ └── ethers │ │ │ ├── contract │ │ │ ├── index.ts │ │ │ └── chainlink-price-feed.abi.ts │ │ │ ├── index.ts │ │ │ ├── ethers.config.interface.ts │ │ │ ├── ethers.module.ts │ │ │ └── ethers.service.ts │ ├── common │ │ ├── middleware │ │ │ ├── index.ts │ │ │ └── logger.middleware.ts │ │ └── exception-filters │ │ │ ├── index.ts │ │ │ ├── http-exception.utils.ts │ │ │ └── http-exception.filter.ts │ ├── app.service.ts │ ├── app.controller.ts │ ├── app.module.ts │ └── main.ts ├── .prettierrc ├── tsconfig.build.json ├── nest-cli.json ├── vercel.json ├── tsconfig.json ├── .eslintrc.js ├── .gitignore ├── LICENCE ├── README.md └── package.json ├── frontend ├── src │ ├── config │ │ ├── index.ts │ │ └── Web3Provider.tsx │ ├── vite-env.d.ts │ ├── components │ │ ├── ui │ │ │ ├── index.ts │ │ │ ├── layouts │ │ │ │ ├── index.ts │ │ │ │ └── AppLayout.tsx │ │ │ ├── buttons │ │ │ │ ├── index.ts │ │ │ │ ├── ConnectWalletButton.tsx │ │ │ │ └── Button.tsx │ │ │ └── Modal.tsx │ │ ├── guards │ │ │ ├── index.ts │ │ │ └── PrivateRoutesGuard.tsx │ │ ├── index.ts │ │ ├── Header.tsx │ │ └── Footer.tsx │ ├── pages │ │ ├── Dashboard │ │ │ ├── hooks │ │ │ │ ├── index.ts │ │ │ │ └── useFetchTokenData.tsx │ │ │ ├── components │ │ │ │ ├── index.ts │ │ │ │ ├── TokenCard.tsx │ │ │ │ └── TokenInfoModal.tsx │ │ │ ├── types.ts │ │ │ └── index.tsx │ │ ├── index.ts │ │ ├── Home.tsx │ │ └── NotFound.tsx │ ├── utils │ │ ├── routes.ts │ │ └── index.ts │ ├── index.css │ ├── main.tsx │ └── App.tsx ├── vercel.json ├── postcss.config.js ├── .env.example ├── tailwind.config.js ├── vite.config.ts ├── tsconfig.node.json ├── index.html ├── .eslintrc.cjs ├── .gitignore ├── public │ └── ethereum-logo.svg ├── tsconfig.json ├── LICENCE ├── README.md └── package.json └── README.md /backend/src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './swagger.config'; 2 | -------------------------------------------------------------------------------- /backend/src/modules/coins/index.ts: -------------------------------------------------------------------------------- 1 | export * from './chainlink'; 2 | -------------------------------------------------------------------------------- /frontend/src/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Web3Provider'; 2 | -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /backend/src/common/middleware/index.ts: -------------------------------------------------------------------------------- 1 | export * from './logger.middleware'; 2 | -------------------------------------------------------------------------------- /frontend/src/components/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Modal } from './Modal' 2 | -------------------------------------------------------------------------------- /backend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } 5 | -------------------------------------------------------------------------------- /backend/src/common/exception-filters/index.ts: -------------------------------------------------------------------------------- 1 | export * from './http-exception.filter'; 2 | -------------------------------------------------------------------------------- /backend/src/modules/ethers/contract/index.ts: -------------------------------------------------------------------------------- 1 | export * from './chainlink-price-feed.abi'; 2 | -------------------------------------------------------------------------------- /frontend/src/components/ui/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AppLayout } from './AppLayout'; 2 | -------------------------------------------------------------------------------- /frontend/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }] 3 | } 4 | -------------------------------------------------------------------------------- /backend/src/config/environments/index.ts: -------------------------------------------------------------------------------- 1 | export { default as EnvironmentVariables } from './env.interface'; 2 | -------------------------------------------------------------------------------- /backend/src/modules/ethers/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ethers.module'; 2 | export * from './ethers.service'; 3 | -------------------------------------------------------------------------------- /frontend/src/components/guards/index.ts: -------------------------------------------------------------------------------- 1 | export { default as PrivateRoutesGuard } from './PrivateRoutesGuard'; 2 | -------------------------------------------------------------------------------- /frontend/src/pages/Dashboard/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export { default as useFetchTokenData } from './useFetchTokenData' 2 | -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Footer } from './Footer' 2 | export { default as Header } from './Header' 3 | -------------------------------------------------------------------------------- /backend/src/modules/coins/chainlink/contract/index.ts: -------------------------------------------------------------------------------- 1 | export * from './chainlink-token.abi'; 2 | export * from './chainlink-token.address'; 3 | -------------------------------------------------------------------------------- /backend/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | # API 2 | VITE_API_URL= 3 | 4 | # Ethereum 5 | VITE_RPC_PROVIDER_URL= 6 | 7 | # WalletConnect 8 | VITE_WALLETCONNECT_PROJECT_ID= 9 | -------------------------------------------------------------------------------- /backend/src/config/environments/.env.example: -------------------------------------------------------------------------------- 1 | # App Config 2 | APP=ERC20WalletWatcher-API 3 | PORT=8000 4 | VERSION=1.0 5 | 6 | # Ethereum 7 | RPC_PROVIDER_URL= 8 | -------------------------------------------------------------------------------- /frontend/src/pages/Dashboard/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TokenCard } from './TokenCard' 2 | export { default as TokenInfoModal } from './TokenInfoModal' 3 | -------------------------------------------------------------------------------- /backend/src/modules/coins/chainlink/index.ts: -------------------------------------------------------------------------------- 1 | export * from './chainlink.controller'; 2 | export * from './chainlink.module'; 3 | export * from './chainlink.service'; 4 | -------------------------------------------------------------------------------- /frontend/src/components/ui/buttons/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Button } from './Button'; 2 | export { default as ConnectWalletButton } from './ConnectWalletButton'; 3 | -------------------------------------------------------------------------------- /frontend/src/pages/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Dashboard } from './Dashboard' 2 | export { default as Home } from './Home' 3 | export { default as NotFound } from './NotFound' 4 | -------------------------------------------------------------------------------- /backend/src/modules/ethers/ethers.config.interface.ts: -------------------------------------------------------------------------------- 1 | export interface EthersModuleOptions { 2 | tokenAddress: string; 3 | tokenAbi: any[]; 4 | priceFeedAddress: string; 5 | } 6 | -------------------------------------------------------------------------------- /backend/src/config/environments/env.interface.ts: -------------------------------------------------------------------------------- 1 | export default interface EnvironmentVariables { 2 | APP: string; 3 | PORT: number; 4 | VERSION: string; 5 | RPC_PROVIDER_URL: string; 6 | } 7 | -------------------------------------------------------------------------------- /frontend/src/utils/routes.ts: -------------------------------------------------------------------------------- 1 | export const PublicRoutes = { 2 | HOME: '/', 3 | NOT_FOUND: '*' 4 | } as const 5 | 6 | export const PrivateRoutes = { 7 | DASHBOARD: '/dashboard' 8 | } as const 9 | -------------------------------------------------------------------------------- /frontend/src/pages/Dashboard/types.ts: -------------------------------------------------------------------------------- 1 | export type CoinsApiUrlSegment = 'chainlink' 2 | 3 | export enum CoinsApiResource { 4 | TotalSupply = 'total-supply', 5 | Balance = 'balance', 6 | Details = 'details' 7 | } 8 | -------------------------------------------------------------------------------- /backend/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/nest-cli", 3 | "collection": "@nestjs/schematics", 4 | "sourceRoot": "src", 5 | "compilerOptions": { 6 | "deleteOutDir": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: {} 6 | }, 7 | plugins: [] 8 | } 9 | -------------------------------------------------------------------------------- /backend/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHealthCheck() { 6 | return { statusCode: 200, message: 'server is alive' }; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | html { 7 | font-family: 'Roboto', system-ui, sans-serif; 8 | background-color: #05040c; 9 | } 10 | } -------------------------------------------------------------------------------- /backend/src/modules/coins/chainlink/contract/chainlink-token.address.ts: -------------------------------------------------------------------------------- 1 | export const chainlinkTokenAddress = 2 | '0x779877a7b0d9e8603169ddbd7836e478b4624789'; 3 | 4 | export const chainlinkUsdPriceFeedAddress = 5 | '0xc59E3633BAAC79493d908e63626716e204A45EdF'; 6 | -------------------------------------------------------------------------------- /backend/src/modules/coins/chainlink/dto/index.ts: -------------------------------------------------------------------------------- 1 | export * from './get-balance-param.dto'; 2 | export * from './get-balance-response.dto'; 3 | export * from './get-details-response.dto'; 4 | export * from './get-price-response.dto.'; 5 | export * from './get-total-supply-response.dto'; 6 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | resolve: { 8 | alias: { 9 | '@': '/src' 10 | } 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "strict": true 9 | }, 10 | "include": ["vite.config.ts"] 11 | } 12 | -------------------------------------------------------------------------------- /backend/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "builds": [ 4 | { 5 | "src": "dist/main.js", 6 | "use": "@vercel/node" 7 | } 8 | ], 9 | "routes": [ 10 | { 11 | "src": "/(.*)", 12 | "dest": "dist/main.js", 13 | "methods": ["GET", "POST", "PUT", "DELETE"] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /backend/src/modules/coins/chainlink/dto/get-balance-param.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString, Matches } from 'class-validator'; 2 | 3 | export class GetBalanceParamDto { 4 | @IsNotEmpty() 5 | @IsString() 6 | @Matches(/^0x[a-fA-F0-9]{40}$/, { 7 | message: 'address must be a valid Ethereum wallet address', 8 | }) 9 | address: string; 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/components/ui/layouts/AppLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Footer, Header } from '@/components' 2 | 3 | type Props = { 4 | children: React.ReactNode 5 | } 6 | 7 | export default function AppLayout({ children }: Props) { 8 | return ( 9 | <> 10 |
11 |
{children}
12 |