diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dba87bc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/node_modules
+
+/package-lock.json
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..a7cea0b
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["Vue.volar"]
+}
diff --git a/ERC20.ts b/ERC20.ts
index 3ce8032..17de1ab 100644
--- a/ERC20.ts
+++ b/ERC20.ts
@@ -1,290 +1,290 @@
export default {
- abi: [
- {
- inputs: [
- {
- internalType: 'string',
- name: 'name_',
- type: 'string',
- },
- {
- internalType: 'string',
- name: 'symbol_',
- type: 'string',
- },
- ],
- stateMutability: 'nonpayable',
- type: 'constructor',
- },
- {
- anonymous: false,
- inputs: [
- {
- indexed: true,
- internalType: 'address',
- name: 'owner',
- type: 'address',
- },
- {
- indexed: true,
- internalType: 'address',
- name: 'spender',
- type: 'address',
- },
- {
- indexed: false,
- internalType: 'uint256',
- name: 'value',
- type: 'uint256',
- },
- ],
- name: 'Approval',
- type: 'event',
- },
- {
- anonymous: false,
- inputs: [
- {
- indexed: true,
- internalType: 'address',
- name: 'from',
- type: 'address',
- },
- {
- indexed: true,
- internalType: 'address',
- name: 'to',
- type: 'address',
- },
- {
- indexed: false,
- internalType: 'uint256',
- name: 'value',
- type: 'uint256',
- },
- ],
- name: 'Transfer',
- type: 'event',
- },
- {
- inputs: [
- {
- internalType: 'address',
- name: 'owner',
- type: 'address',
- },
- {
- internalType: 'address',
- name: 'spender',
- type: 'address',
- },
- ],
- name: 'allowance',
- outputs: [
- {
- internalType: 'uint256',
- name: '',
- type: 'uint256',
- },
- ],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [
- {
- internalType: 'address',
- name: 'spender',
- type: 'address',
- },
- {
- internalType: 'uint256',
- name: 'amount',
- type: 'uint256',
- },
- ],
- name: 'approve',
- outputs: [
- {
- internalType: 'bool',
- name: '',
- type: 'bool',
- },
- ],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [
- {
- internalType: 'address',
- name: 'account',
- type: 'address',
- },
- ],
- name: 'balanceOf',
- outputs: [
- {
- internalType: 'uint256',
- name: '',
- type: 'uint256',
- },
- ],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [],
- name: 'decimals',
- outputs: [
- {
- internalType: 'uint8',
- name: '',
- type: 'uint8',
- },
- ],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [
- {
- internalType: 'address',
- name: 'spender',
- type: 'address',
- },
- {
- internalType: 'uint256',
- name: 'subtractedValue',
- type: 'uint256',
- },
- ],
- name: 'decreaseAllowance',
- outputs: [
- {
- internalType: 'bool',
- name: '',
- type: 'bool',
- },
- ],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [
- {
- internalType: 'address',
- name: 'spender',
- type: 'address',
- },
- {
- internalType: 'uint256',
- name: 'addedValue',
- type: 'uint256',
- },
- ],
- name: 'increaseAllowance',
- outputs: [
- {
- internalType: 'bool',
- name: '',
- type: 'bool',
- },
- ],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [],
- name: 'name',
- outputs: [
- {
- internalType: 'string',
- name: '',
- type: 'string',
- },
- ],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [],
- name: 'symbol',
- outputs: [
- {
- internalType: 'string',
- name: '',
- type: 'string',
- },
- ],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [],
- name: 'totalSupply',
- outputs: [
- {
- internalType: 'uint256',
- name: '',
- type: 'uint256',
- },
- ],
- stateMutability: 'view',
- type: 'function',
- },
- {
- inputs: [
- {
- internalType: 'address',
- name: 'to',
- type: 'address',
- },
- {
- internalType: 'uint256',
- name: 'amount',
- type: 'uint256',
- },
- ],
- name: 'transfer',
- outputs: [
- {
- internalType: 'bool',
- name: '',
- type: 'bool',
- },
- ],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- {
- inputs: [
- {
- internalType: 'address',
- name: 'from',
- type: 'address',
- },
- {
- internalType: 'address',
- name: 'to',
- type: 'address',
- },
- {
- internalType: 'uint256',
- name: 'amount',
- type: 'uint256',
- },
- ],
- name: 'transferFrom',
- outputs: [
- {
- internalType: 'bool',
- name: '',
- type: 'bool',
- },
- ],
- stateMutability: 'nonpayable',
- type: 'function',
- },
- ],
-}
+ abi: [
+ {
+ inputs: [
+ {
+ internalType: 'string',
+ name: 'name_',
+ type: 'string',
+ },
+ {
+ internalType: 'string',
+ name: 'symbol_',
+ type: 'string',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'constructor',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'owner',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'spender',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ ],
+ name: 'Approval',
+ type: 'event',
+ },
+ {
+ anonymous: false,
+ inputs: [
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ indexed: true,
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ indexed: false,
+ internalType: 'uint256',
+ name: 'value',
+ type: 'uint256',
+ },
+ ],
+ name: 'Transfer',
+ type: 'event',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'owner',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: 'spender',
+ type: 'address',
+ },
+ ],
+ name: 'allowance',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'spender',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'approve',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'account',
+ type: 'address',
+ },
+ ],
+ name: 'balanceOf',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'decimals',
+ outputs: [
+ {
+ internalType: 'uint8',
+ name: '',
+ type: 'uint8',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'spender',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'subtractedValue',
+ type: 'uint256',
+ },
+ ],
+ name: 'decreaseAllowance',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'spender',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'addedValue',
+ type: 'uint256',
+ },
+ ],
+ name: 'increaseAllowance',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'name',
+ outputs: [
+ {
+ internalType: 'string',
+ name: '',
+ type: 'string',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'symbol',
+ outputs: [
+ {
+ internalType: 'string',
+ name: '',
+ type: 'string',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [],
+ name: 'totalSupply',
+ outputs: [
+ {
+ internalType: 'uint256',
+ name: '',
+ type: 'uint256',
+ },
+ ],
+ stateMutability: 'view',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'transfer',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ {
+ inputs: [
+ {
+ internalType: 'address',
+ name: 'from',
+ type: 'address',
+ },
+ {
+ internalType: 'address',
+ name: 'to',
+ type: 'address',
+ },
+ {
+ internalType: 'uint256',
+ name: 'amount',
+ type: 'uint256',
+ },
+ ],
+ name: 'transferFrom',
+ outputs: [
+ {
+ internalType: 'bool',
+ name: '',
+ type: 'bool',
+ },
+ ],
+ stateMutability: 'nonpayable',
+ type: 'function',
+ },
+ ],
+};
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..136fa9b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,69 @@
+# Vue.js Token Balance Viewer
+
+This project is a Vue.js application that fetches and displays token balances from various networks. The application allows users to select a network and view the corresponding token balances. If there is an error while fetching the balance, an error message is displayed directly in the table cell for that token.
+
+## Getting Started
+
+To get started with this project, ensure you have the following prerequisites installed on your machine:
+
+- **Node.js** (v14 or higher)
+- **npm** (v6 or higher) or **yarn** (v1.22 or higher)
+
+## Installation
+
+Follow these steps to install and set up the project:
+
+1. **Clone the Repository:**
+
+ ```bash
+ git clone https://github.com/your-username/your-repo-name.git
+ cd your-repo-name
+ ```
+2. **Install Dependencies:**
+
+ ```bash
+ npm install
+ npm run serve
+ ```
+3. **Usage:**
+
+- Select a Network:
+Use the dropdown menu to select a network. The application will automatically fetch the corresponding tokens for the selected network.
+
+- View Token Balances:
+Once the tokens are fetched, the balances are displayed in a table. If there is an error while fetching a balance, an error message will be shown in place of the balance.
+
+- Error Handling:
+If a balance cannot be fetched, an error message ("Error fetching balance") will be displayed directly in the table cell for that token.
+
+4. **Considerations and Trade-offs:**
+
+- Incremental Updates for Better User Experience: The application updates the token balances incrementally to provide a better user experience. Each token balance is fetched individually, and if an error occurs for a particular token, it is handled gracefully without affecting the rest of the tokens.
+
+- Error Messages Displayed Inline: Instead of showing a generic error message for all tokens, the application displays an error message inline for each token that fails to fetch its balance. This approach allows the user to see which specific tokens have issues.
+
+- Responsiveness: The table and dropdown are made responsive using CSS media queries to ensure a good user experience across different screen sizes.
+
+- Consideration for API Rate Limits: The application handles balance fetching in a loop, which might be subject to API rate limits. For production use, consider implementing a backoff strategy or batching requests to comply with rate limits.
+
+- Trade-off Between Real-time Updates and API Performance: Fetching token balances in real-time ensures users always have the most up-to-date information, but it can be resource-intensive and may impact performance. Caching or debouncing the requests might be necessary for high-traffic scenarios.
+
+5. **Technologies Used:**
+
+ - Vue.js 3: The progressive JavaScript framework used for building the user interface.
+
+ - Vuex: State management pattern + library for Vue.js applications.
+
+ - Axios: Promise-based HTTP client for making API requests.
+
+ - Ethers.js: A library for interacting with the Ethereum blockchain.
+
+ - TypeScript: A statically typed superset of JavaScript.
+
+ - CSS3: For responsive styling and layout.
+
+## License
+This project is licensed under the MIT License. See the LICENSE file for more information.
+
+## Contributing
+This `README.md` is now fully formatted in Markdown and ready for you to copy and paste into your project.
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..a888544
--- /dev/null
+++ b/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ Vite App
+
+
+
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..2499df6
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "Frontend-Coding-Challenge",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "run-p type-check \"build-only {@}\" --",
+ "preview": "vite preview",
+ "build-only": "vite build",
+ "type-check": "vue-tsc --build --force"
+ },
+ "dependencies": {
+ "axios": "^1.7.5",
+ "ethers": "^5.7.2",
+ "vue": "^3.4.29",
+ "vue-router": "^4.4.3",
+ "vuex": "^4.0.2"
+ },
+ "devDependencies": {
+ "@tsconfig/node20": "^20.1.4",
+ "@types/node": "^20.14.5",
+ "@vitejs/plugin-vue": "^5.0.5",
+ "@vue/tsconfig": "^0.5.1",
+ "npm-run-all2": "^6.2.0",
+ "typescript": "~5.4.0",
+ "vite": "^5.3.1",
+ "vue-tsc": "^2.0.21"
+ }
+}
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..df36fcf
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..11ae674
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/base.css b/src/assets/base.css
new file mode 100644
index 0000000..8816868
--- /dev/null
+++ b/src/assets/base.css
@@ -0,0 +1,86 @@
+/* color palette from */
+:root {
+ --vt-c-white: #ffffff;
+ --vt-c-white-soft: #f8f8f8;
+ --vt-c-white-mute: #f2f2f2;
+
+ --vt-c-black: #181818;
+ --vt-c-black-soft: #222222;
+ --vt-c-black-mute: #282828;
+
+ --vt-c-indigo: #2c3e50;
+
+ --vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
+ --vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
+ --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
+ --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
+
+ --vt-c-text-light-1: var(--vt-c-indigo);
+ --vt-c-text-light-2: rgba(60, 60, 60, 0.66);
+ --vt-c-text-dark-1: var(--vt-c-white);
+ --vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
+}
+
+/* semantic color variables for this project */
+:root {
+ --color-background: var(--vt-c-white);
+ --color-background-soft: var(--vt-c-white-soft);
+ --color-background-mute: var(--vt-c-white-mute);
+
+ --color-border: var(--vt-c-divider-light-2);
+ --color-border-hover: var(--vt-c-divider-light-1);
+
+ --color-heading: var(--vt-c-text-light-1);
+ --color-text: var(--vt-c-text-light-1);
+
+ --section-gap: 160px;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --color-background: var(--vt-c-black);
+ --color-background-soft: var(--vt-c-black-soft);
+ --color-background-mute: var(--vt-c-black-mute);
+
+ --color-border: var(--vt-c-divider-dark-2);
+ --color-border-hover: var(--vt-c-divider-dark-1);
+
+ --color-heading: var(--vt-c-text-dark-1);
+ --color-text: var(--vt-c-text-dark-2);
+ }
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+ margin: 0;
+ font-weight: normal;
+}
+
+body {
+ min-height: 100vh;
+ color: var(--color-text);
+ background: var(--color-background);
+ transition:
+ color 0.5s,
+ background-color 0.5s;
+ line-height: 1.6;
+ font-family:
+ Inter,
+ -apple-system,
+ BlinkMacSystemFont,
+ 'Segoe UI',
+ Roboto,
+ Oxygen,
+ Ubuntu,
+ Cantarell,
+ 'Fira Sans',
+ 'Droid Sans',
+ 'Helvetica Neue',
+ sans-serif;
+ font-size: 15px;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
diff --git a/src/assets/logo.svg b/src/assets/logo.svg
new file mode 100644
index 0000000..7565660
--- /dev/null
+++ b/src/assets/logo.svg
@@ -0,0 +1 @@
+
diff --git a/src/assets/main.css b/src/assets/main.css
new file mode 100644
index 0000000..36fb845
--- /dev/null
+++ b/src/assets/main.css
@@ -0,0 +1,35 @@
+@import './base.css';
+
+#app {
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ font-weight: normal;
+}
+
+a,
+.green {
+ text-decoration: none;
+ color: hsla(160, 100%, 37%, 1);
+ transition: 0.4s;
+ padding: 3px;
+}
+
+@media (hover: hover) {
+ a:hover {
+ background-color: hsla(160, 100%, 37%, 0.2);
+ }
+}
+
+@media (min-width: 1024px) {
+ body {
+ display: flex;
+ place-items: center;
+ }
+
+ #app {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ padding: 0 2rem;
+ }
+}
diff --git a/src/components/NetworkDropdown.vue b/src/components/NetworkDropdown.vue
new file mode 100644
index 0000000..3162565
--- /dev/null
+++ b/src/components/NetworkDropdown.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/TokenTable.vue b/src/components/TokenTable.vue
new file mode 100644
index 0000000..afde274
--- /dev/null
+++ b/src/components/TokenTable.vue
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
+ | Chain ID |
+ Symbol |
+ Name |
+ Balance |
+
+
+
+
+ | {{ token.chain_id }} |
+ {{ token.symbol }} |
+ {{ token.name }} |
+ {{ balances[token.symbol] || 'Loading...' }} |
+
+
+
+
No tokens available for the selected network.
+
+
+
+
+
+
diff --git a/src/env.d.ts b/src/env.d.ts
new file mode 100644
index 0000000..b3a7a33
--- /dev/null
+++ b/src/env.d.ts
@@ -0,0 +1,13 @@
+// src/env.d.ts
+
+///
+
+interface ImportMetaEnv {
+ readonly VITE_APP_TITLE: 'DEneme'; // Example custom environment variable
+ readonly VITE_API_URL: 'http://localhost:5173/'; // Your API URL or other env variables
+ // Add more environment variables as needed...
+}
+
+interface ImportMeta {
+ readonly env: ImportMetaEnv;
+}
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..d5fd1ff
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,10 @@
+import { createApp } from 'vue';
+import App from './App.vue';
+import router from './router'; // Import your router configuration
+import store from './store/index'; // Import Vuex store
+
+const app = createApp(App);
+
+app.use(store); // Use Vuex store
+app.use(router);
+app.mount('#app');
diff --git a/src/router/index.ts b/src/router/index.ts
new file mode 100644
index 0000000..791e622
--- /dev/null
+++ b/src/router/index.ts
@@ -0,0 +1,17 @@
+import { createRouter, createWebHistory } from 'vue-router';
+import HomeView from '../views/HomeView.vue';
+
+const routes = [
+ {
+ path: '/',
+ name: 'home',
+ component: HomeView,
+ },
+];
+
+const router = createRouter({
+ history: createWebHistory(import.meta.env.VITE_API_URL),
+ routes,
+});
+
+export default router;
diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts
new file mode 100644
index 0000000..0845e2c
--- /dev/null
+++ b/src/shims-vue.d.ts
@@ -0,0 +1,7 @@
+// src/shims-vue.d.ts
+
+declare module '*.vue' {
+ import { DefineComponent } from 'vue';
+ const component: DefineComponent<{}, {}, any>;
+ export default component;
+}
diff --git a/src/store/index.ts b/src/store/index.ts
new file mode 100644
index 0000000..39bb855
--- /dev/null
+++ b/src/store/index.ts
@@ -0,0 +1,120 @@
+import { createStore } from 'vuex';
+import axios from 'axios';
+import { BigNumber, ethers } from 'ethers';
+
+import ERC20 from '../../ERC20';
+
+import { NetworkStoreState } from '@/types/store-type';
+import { Environments, Tokens } from '@/types/fetch-type';
+
+const store = createStore({
+ state: {
+ selectedNetworkId: 0,
+ network: {
+ parentenv: '',
+ env: '',
+ type: '',
+ chain_instance: '',
+ chain_id: 0,
+ chain_name: '',
+ chain_display_name: '',
+ native_token_name: '',
+ native_token_symbol: '',
+ min_native_balance: '',
+ lzchain_id: 0,
+ lzscanner_url: '',
+ chain_wss: '',
+ explorer: '',
+ token_url: '',
+ },
+ networks: [],
+ isLoading: false,
+ tokens: [],
+ balances: {},
+ },
+ mutations: {
+ setSelectedNetworkId(state, environmentId: number) {
+ state.selectedNetworkId = environmentId;
+ state.network = { ...state.networks.filter((env) => env.chain_id === environmentId)[0] };
+ },
+ setLoading(state, loading: boolean) {
+ state.isLoading = loading;
+ },
+ setNetworks(state, networks: Environments[]) {
+ state.networks = networks;
+ },
+ setTokens(state, tokens: Tokens[]) {
+ state.tokens = tokens;
+ },
+ setTokenBalances(state, balances: Record) {
+ state.balances = balances;
+ },
+ },
+ actions: {
+ async fetchNetworks({ commit, state }) {
+ if (state.networks.length === 0) {
+ commit('setLoading', true);
+ try {
+ const response = await axios.get('/api/privapi/trading/environments', {
+ headers: {
+ Accept: 'application/json',
+ },
+ });
+ commit('setNetworks', response.data);
+ } finally {
+ commit('setLoading', false);
+ }
+ }
+ },
+ async fetchTokens({ commit }, networkId) {
+ commit('setLoading', true);
+ try {
+ const response = await axios.get('/api/privapi/trading/tokens', {
+ headers: {
+ Accept: 'application/json',
+ },
+ });
+ const filteredTokens = response.data.filter((token: Tokens) => token.chain_id === networkId);
+ commit('setTokens', filteredTokens);
+ } finally {
+ commit('setLoading', false);
+ }
+ },
+ async fetchTokenBalances({ commit, state }, walletAddress: string) {
+ const balances: Record = { ...state.balances };
+
+ for (const token of state.tokens) {
+ const chainReaderUrl = state.network.chain_instance;
+ const provider = new ethers.providers.JsonRpcProvider(chainReaderUrl);
+
+ let balance: BigNumber;
+ let symbol = token.symbol;
+
+ try {
+ if (token.isnative) {
+ balance = await provider.getBalance(walletAddress);
+ } else {
+ const tokenERC20Contract = new ethers.Contract(token.address, ERC20.abi, provider);
+ balance = await tokenERC20Contract.balanceOf(walletAddress);
+ }
+
+ const displayValue = ethers.utils.formatUnits(balance, token.evmdecimals);
+ balances[symbol] = displayValue;
+ } catch (balanceError) {
+ balances[symbol] = `Error fetching balance`;
+ }
+
+ commit('setTokenBalances', { ...balances });
+ }
+ },
+ },
+ getters: {
+ selectedNetworkId: (state) => state.selectedNetworkId,
+ networks: (state) => state.networks,
+ isLoading: (state) => state.isLoading,
+ tokens: (state) => state.tokens,
+ balances: (state) => state.balances,
+ },
+});
+
+export default store;
diff --git a/src/types/fetch-type.ts b/src/types/fetch-type.ts
new file mode 100644
index 0000000..64ad644
--- /dev/null
+++ b/src/types/fetch-type.ts
@@ -0,0 +1,34 @@
+export type Tokens = {
+ address: string;
+ auctionendtime: number | null;
+ auctionmode: number;
+ chain_id: number;
+ env: string;
+ evmdecimals: number;
+ isnative: boolean;
+ isvirtual: boolean;
+ min_depositamnt: string;
+ name: string;
+ old_symbol: string | null;
+ status: string;
+ subnet_symbol: string;
+ symbol: string;
+};
+
+export type Environments = {
+ parentenv: string;
+ env: string;
+ type: string;
+ chain_instance: string;
+ chain_id: number;
+ chain_name: string;
+ chain_display_name: string;
+ native_token_name: string;
+ native_token_symbol: string;
+ min_native_balance: string;
+ lzchain_id: number;
+ lzscanner_url: string;
+ chain_wss: string;
+ explorer: string;
+ token_url: string;
+};
diff --git a/src/types/store-type.ts b/src/types/store-type.ts
new file mode 100644
index 0000000..bdc9cba
--- /dev/null
+++ b/src/types/store-type.ts
@@ -0,0 +1,10 @@
+import { Environments, Tokens } from './fetch-type';
+
+export type NetworkStoreState = {
+ selectedNetworkId: number;
+ network: Environments;
+ networks: Environments[];
+ isLoading: boolean;
+ tokens: Tokens[];
+ balances: Record;
+};
diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue
new file mode 100644
index 0000000..ead6360
--- /dev/null
+++ b/src/views/HomeView.vue
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..e922678
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "target": "esnext", // Use 'esnext' to support modern JavaScript features
+ "module": "esnext", // Compatible module setting for 'import.meta'
+ "strict": true,
+ "jsx": "preserve",
+ "moduleResolution": "node",
+ "skipLibCheck": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"]
+ }
+ },
+ "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
+ "exclude": ["node_modules"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..2cc4a2a
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,22 @@
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import path from 'path';
+
+export default defineConfig({
+ plugins: [vue()],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
+ server: {
+ proxy: {
+ '/api': {
+ target: 'https://api.dexalot-test.com',
+ changeOrigin: true,
+ secure: true,
+ rewrite: (path) => path.replace(/^\/api/, ''),
+ },
+ },
+ },
+});