-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserver.js
More file actions
268 lines (223 loc) · 10.6 KB
/
server.js
File metadata and controls
268 lines (223 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// server.js
const express = require("express");
const app = express();
const path = require("path");
require("dotenv").config();
// Static files and views
app.use(express.static(path.join(__dirname, "public")));
app.set("view engine", "ejs");
app.set("views", path.join(__dirname, "views"));
// Body parser
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Routes
const walletRoutes = require("./routes/walletRoutes");
app.use("/api/wallet", walletRoutes);
// Default route
app.get("/", (req, res) => {
res.render("index");
});
// Server start
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
import express from 'express';
import numbro from 'numbro';
import dotenv from 'dotenv';
import path from 'path';
import { fileURLToPath } from 'url';
// Load environment variables from .env file
dotenv.config();
const SIM_API_KEY = process.env.SIM_API_KEY;
// Check if the API key is set
if (!SIM_API_KEY) {
console.error("FATAL ERROR: SIM_API_KEY is not set in your environment variables. - server.js:16");
process.exit(1); // Exit the process if no API key is found
}
// Set up __dirname for ES modules (needed to resolve file paths)
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Initialize Express
const app = express();
// Configure Express settings
app.set('view engine', 'ejs'); // Set EJS as the template engine
app.set('views', path.join(__dirname, 'views')); // Set the directory for EJS templates
app.use(express.static(path.join(__dirname, 'public'))); // Serve static files from the 'public' directory
// Define the main route for the wallet page
app.get('/', async (req, res) => {
// Get wallet address and active tab from the URL query parameters
const {
walletAddress = '',
tab = 'tokens' // Default to 'tokens' tab if no tab is specified
} = req.query;
// Initialize variables to hold data
let tokens = [];
let totalWalletUSDValue = 0;
let activities = []; // Placeholder for future activity data
let collectibles = []; // Placeholder for future NFT data
// --- LOGIC FOR FETCHING TOKEN BALANCES ---
// Only fetch data if a wallet address is provided
if (walletAddress) {
try {
console.log(`Fetching balances for wallet: ${walletAddress} - server.js:50`);
// Construct the API URL for the Balances API
const balancesApiUrl = `https://api.sim.xyz/v1/wallets/${walletAddress}/balances?chainId=1`; // chainId=1 for Ethereum Mainnet
// Make the API request using fetch
const balancesResponse = await fetch(balancesApiUrl, {
method: 'GET',
headers: {
'Authorization': `Bearer ${SIM_API_KEY}`,
'Content-Type': 'application/json'
}
});
// Check if the request was successful
if (!balancesResponse.ok) {
// If the response is not OK, throw an error with the status text
const errorBody = await balancesResponse.text(); // Get more error details if available
throw new Error(`Balances API request failed with status ${balancesResponse.status}: ${balancesResponse.statusText}. Details: ${errorBody}`);
}
const balancesData = await balancesResponse.json();
console.log("Balances API Response received successfully. - server.js:71");
// The API response structure might be { results: [...] } or just an array
// We'll check for that and assign the tokens array accordingly
tokens = balancesData.results || balancesData || [];
// Calculate the total USD value of the wallet
let totalValue = 0;
tokens.forEach(token => {
// Ensure value_usd is a number before adding
if (typeof token.value_usd === 'number') {
totalValue += token.value_usd;
}
});
totalWalletUSDValue = numbro(totalValue).formatCurrency('$0,0.00');
// Format the token amounts and USD values for better display
tokens = tokens.map(token => {
// Format amount to a readable number, avoiding scientific notation for very small numbers
const amount = parseFloat(token.amount);
let amountFormatted;
if (isNaN(amount)) {
amountFormatted = 'N/A';
} else if (amount < 0.0001 && amount > 0) {
amountFormatted = '< 0.0001'; // For very small amounts
} else {
amountFormatted = numbro(amount).format('0,0.0000');
}
return {
...token,
amountFormatted: amountFormatted,
valueUSDFormatted: numbro(token.value_usd).formatCurrency('$0,0.00')
};
});
} catch (error) {
console.error("Failed to fetch token balances: - server.js:107", error.message);
// In case of an error, we'll keep tokens empty and show an error message on the frontend
totalWalletUSDValue = 'Error';
}
}
// --- END OF TOKEN BALANCES LOGIC ---
// --- LOGIC FOR FETCHING ACTIVITIES ---
// Only fetch data if wallet address is provided and tab is 'activity'
if (walletAddress && tab === 'activity') {
try {
console.log(`Fetching activities for wallet: ${walletAddress} - server.js:119`);
// Construct the API URL for the Activity API
const activityApiUrl = `https://api.sim.xyz/v1/wallets/${walletAddress}/activity?chainId=1`;
// Make the API request
const activityResponse = await fetch(activityApiUrl, {
headers: { 'Authorization': `Bearer ${SIM_API_KEY}` }
});
if (!activityResponse.ok) {
throw new Error(`Activity API failed: ${activityResponse.statusText}`);
}
const activityData = await activityResponse.json();
console.log("Activity API Response received successfully. - server.js:133");
// The API response is usually an array of activities
activities = activityData.results || activityData || [];
// Format activities for better display
activities = activities.map(activity => {
// Convert timestamp to a readable date
const date = new Date(activity.timestamp * 1000); // Timestamp is usually in seconds
const formattedDate = date.toLocaleDateString();
const formattedTime = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
// Determine a simple icon or label based on activity type
let activityLabel = 'Unknown';
if (activity.method === 'transfer' && activity.direction === 'in') {
activityLabel = 'Received';
} else if (activity.method === 'transfer' && activity.direction === 'out') {
activityLabel = 'Sent';
} else if (activity.method === 'swap') {
activityLabel = 'Swap';
} else if (activity.method === 'approval') {
activityLabel = 'Approval';
}
return {
...activity,
formattedDate,
formattedTime,
activityLabel
};
});
} catch (error) {
console.error("Failed to fetch activity data: - server.js:166", error.message);
// In case of error, activities array will remain empty
}
}
// --- END OF ACTIVITY LOGIC ---
// --- LOGIC FOR FETCHING COLLECTIBLES (NFTs) ---
// Only fetch data if wallet address is provided and tab is 'collectibles'
if (walletAddress && tab === 'collectibles') {
try {
console.log(`Fetching collectibles for wallet: ${walletAddress} - server.js:177`);
// Construct the API URL for the Collectibles API
const collectiblesApiUrl = `https://api.sim.xyz/v1/wallets/${walletAddress}/collectibles?chainId=1`;
// Make the API request
const collectiblesResponse = await fetch(collectiblesApiUrl, {
headers: { 'Authorization': `Bearer ${SIM_API_KEY}` }
});
if (!collectiblesResponse.ok) {
throw new Error(`Collectibles API failed: ${collectiblesResponse.statusText}`);
}
const collectiblesData = await collectiblesResponse.json();
console.log("Collectibles API Response received successfully. - server.js:191");
// The API response is usually an array of collectibles
collectibles = collectiblesData.results || collectiblesData || [];
// Format collectibles for better display
collectibles = collectibles.map(collectible => {
// Extract image URL from metadata
let imageUrl = '';
if (collectible.metadata && collectible.metadata.image) {
imageUrl = collectible.metadata.image;
// Sometimes image URLs are IPFS, we can use a public gateway for them
if (imageUrl.startsWith('ipfs://')) {
imageUrl = imageUrl.replace('ipfs://', 'https://ipfs.io/ipfs/');
}
}
// Extract name from metadata
const name = (collectible.metadata && collectible.metadata.name) ? collectible.metadata.name : `NFT #${collectible.token_id}`;
return {
...collectible,
formattedName: name,
imageUrl: imageUrl
};
});
} catch (error) {
console.error("Failed to fetch collectibles data: - server.js:219", error.message);
// In case of error, collectibles array will remain empty
}
}
// --- END OF COLLECTIBLES LOGIC ---
// Render the 'wallet.ejs' template and pass the data to it
res.render('wallet', {
walletAddress: walletAddress,
currentTab: tab,
totalWalletUSDValue: totalWalletUSDValue,
tokens: tokens,
activities: activities,
collectibles: collectibles
});
});
// Start the server on port 3001
app.listen(3001, () => {
console.log(`Server running at http://localhost:3001 - server.js:239`);
});