Skip to content

Commit f99b179

Browse files
committed
A few improvements
1 parent b8a7d2b commit f99b179

File tree

4 files changed

+117
-30
lines changed

4 files changed

+117
-30
lines changed

backend/clean.ts

Lines changed: 115 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
#!/usr/bin/env bun
22

33
/**
4-
* Clean stats data from Arkiv since a given timestamp.
4+
* Clean stats data from Arkiv since a given timestamp or delete a specific entity.
55
*
66
* Usage:
77
* bun clean.ts --timestamp TIMESTAMP # Clean all stats (hourly and daily) since timestamp
88
* bun clean.ts --timestamp TIMESTAMP --type hourly # Clean only hourly stats since timestamp
99
* bun clean.ts --timestamp TIMESTAMP --type daily # Clean only daily stats since timestamp
10+
* bun clean.ts --entity-key ENTITY_KEY # Delete a specific entity by its key
11+
*
12+
* Options:
13+
* --dry-run # Dry run, don't delete anything
1014
*
1115
* Timestamp can be provided as:
1216
* - Unix timestamp in seconds (e.g., 1704067200)
@@ -24,8 +28,7 @@ import { kaolin, localhost } from "@arkiv-network/sdk/chains";
2428
import { eq, gt } from "@arkiv-network/sdk/query";
2529
import type { Chain } from "viem";
2630
import { defineChain } from "viem";
27-
28-
const DATA_VERSION = "0.12";
31+
import { DATA_VERSION } from "./src/arkiv";
2932

3033
type AggregatedDataType = "hourly" | "daily";
3134

@@ -105,6 +108,7 @@ async function getStatsEntitiesSinceTimestamp(
105108
.buildQuery()
106109
.where(whereConditions)
107110
.withAttributes()
111+
.withPayload()
108112
.limit(limit);
109113

110114
const entities: Entity[] = [];
@@ -119,6 +123,33 @@ async function getStatsEntitiesSinceTimestamp(
119123
return entities;
120124
}
121125

126+
/**
127+
* Delete a single entity by its key
128+
*/
129+
async function deleteEntityByKey(
130+
entityKey: string,
131+
dryRun?: boolean,
132+
): Promise<void> {
133+
const key = entityKey.startsWith("0x")
134+
? (entityKey as `0x${string}`)
135+
: (`0x${entityKey}` as `0x${string}`);
136+
137+
console.log(`\n🗑️ Deleting entity: ${key}`);
138+
139+
if (dryRun) {
140+
console.log(" (Dry run - would delete this entity)");
141+
return;
142+
}
143+
144+
try {
145+
await arkivWalletClient.deleteEntity({ entityKey: key });
146+
console.log(`\n✅ Successfully deleted entity: ${key}`);
147+
} catch (error) {
148+
console.error(`\n❌ Failed to delete entity ${key}:`, error);
149+
throw error;
150+
}
151+
}
152+
122153
/**
123154
* Delete entities in batches
124155
*/
@@ -142,7 +173,7 @@ async function deleteEntities(entities: Entity[]): Promise<void> {
142173
}));
143174

144175
try {
145-
const receipt = await arkivWalletClient.mutateEntities({
176+
await arkivWalletClient.mutateEntities({
146177
deletes,
147178
});
148179
deletedCount += batch.length;
@@ -164,7 +195,11 @@ async function deleteEntities(entities: Entity[]): Promise<void> {
164195
/**
165196
* Main cleanup function
166197
*/
167-
async function cleanStats(timestamp: number, statsType?: AggregatedDataType) {
198+
async function cleanStats(
199+
timestamp: number,
200+
statsType?: AggregatedDataType,
201+
dryRun?: boolean,
202+
) {
168203
const timestampDate = new Date(timestamp * 1000);
169204
console.log(`\n🧹 Cleaning stats data since ${timestampDate.toISOString()}`);
170205
if (statsType) {
@@ -201,6 +236,14 @@ async function cleanStats(timestamp: number, statsType?: AggregatedDataType) {
201236
console.log(` - Daily stats: ${dailyCount}`);
202237
}
203238

239+
if (dryRun) {
240+
// show all entities
241+
entities.forEach((entity) => {
242+
console.log(entity.key);
243+
console.log(entity.toText());
244+
});
245+
process.exit(0);
246+
}
204247
// Confirm deletion
205248
console.log("\n⚠️ This will permanently delete the above entities.");
206249
console.log(" Press Ctrl+C to cancel, or wait 3 seconds to continue...");
@@ -221,44 +264,89 @@ async function cleanStats(timestamp: number, statsType?: AggregatedDataType) {
221264
//
222265
function printUsageAndExit() {
223266
console.error(
224-
"Usage: bun clean.ts --timestamp TIMESTAMP [--type hourly|daily]\n" +
267+
"Usage: bun clean.ts [--timestamp TIMESTAMP | --entity-key KEY] [options]\n" +
225268
" --timestamp TIMESTAMP Unix timestamp (seconds) or ISO 8601 date string\n" +
226-
" --type TYPE Optional: 'hourly' or 'daily' (default: both)",
269+
" --entity-key KEY Entity key to delete (0x-prefixed hex string)\n" +
270+
" --type TYPE Optional: 'hourly' or 'daily' (only with --timestamp, default: both)\n" +
271+
" --dry-run Dry run, don't delete anything",
227272
);
228273
process.exit(1);
229274
}
230275

231276
const args = process.argv.slice(2);
232277
const timestampIdx = args.indexOf("--timestamp");
278+
const entityKeyIdx = args.indexOf("--entity-key");
233279
const typeIdx = args.indexOf("--type");
280+
const dryRun = args.includes("--dry-run");
234281

235-
if (timestampIdx === -1) {
236-
console.error("❌ Missing required argument: --timestamp");
282+
// Validate that either timestamp or entity-key is provided, but not both
283+
if (timestampIdx === -1 && entityKeyIdx === -1) {
284+
console.error("❌ Missing required argument: --timestamp or --entity-key");
237285
printUsageAndExit();
238286
}
239287

240-
const timestampStr = args[timestampIdx + 1];
241-
if (!timestampStr) {
242-
console.error("❌ Missing timestamp value");
288+
if (timestampIdx !== -1 && entityKeyIdx !== -1) {
289+
console.error(
290+
"❌ Cannot use both --timestamp and --entity-key. Use one or the other.",
291+
);
243292
printUsageAndExit();
244293
}
245294

246-
let statsType: AggregatedDataType | undefined;
247-
if (typeIdx !== -1) {
248-
const typeValue = args[typeIdx + 1];
249-
if (typeValue !== "hourly" && typeValue !== "daily") {
250-
console.error("❌ Invalid stats type. Must be 'hourly' or 'daily'");
295+
// Handle entity-key mode
296+
if (entityKeyIdx !== -1) {
297+
const entityKey = args[entityKeyIdx + 1];
298+
if (!entityKey) {
299+
console.error("❌ Missing entity key value");
251300
printUsageAndExit();
252301
}
253-
statsType = typeValue;
254-
}
255302

256-
(async () => {
257-
try {
258-
const timestamp = parseTimestamp(timestampStr);
259-
await cleanStats(timestamp, statsType);
260-
} catch (error) {
261-
console.error("❌ Error:", error);
262-
process.exit(1);
303+
if (typeIdx !== -1) {
304+
console.error(
305+
"❌ --type option is only valid with --timestamp, not with --entity-key",
306+
);
307+
printUsageAndExit();
308+
}
309+
310+
(async () => {
311+
try {
312+
if (dryRun) {
313+
console.log("🧹 Dry run, not deleting anything");
314+
}
315+
await deleteEntityByKey(entityKey, dryRun);
316+
console.log("\n🎉 Entity deletion completed successfully!");
317+
} catch (error) {
318+
console.error("❌ Error:", error);
319+
process.exit(1);
320+
}
321+
})();
322+
} else {
323+
// Handle timestamp mode
324+
const timestampStr = args[timestampIdx + 1];
325+
if (!timestampStr) {
326+
console.error("❌ Missing timestamp value");
327+
printUsageAndExit();
263328
}
264-
})();
329+
330+
let statsType: AggregatedDataType | undefined;
331+
if (typeIdx !== -1) {
332+
const typeValue = args[typeIdx + 1];
333+
if (typeValue !== "hourly" && typeValue !== "daily") {
334+
console.error("❌ Invalid stats type. Must be 'hourly' or 'daily'");
335+
printUsageAndExit();
336+
}
337+
statsType = typeValue as AggregatedDataType;
338+
}
339+
340+
(async () => {
341+
try {
342+
if (dryRun) {
343+
console.log("🧹 Dry run, not deleting anything");
344+
}
345+
const timestamp = parseTimestamp(timestampStr);
346+
await cleanStats(timestamp, statsType, dryRun);
347+
} catch (error) {
348+
console.error("❌ Error:", error);
349+
process.exit(1);
350+
}
351+
})();
352+
}

backend/get-stats.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { formatGwei } from "viem";
2-
import { aggregateDataLastHour } from "./src/aggregate";
32
import {
43
getAggregatedDataSinceTimestamp,
54
getBlocksSinceTimestamp,

backend/src/arkiv.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020

2121
const MONTH_IN_SECONDS = 60 * 60 * 24 * 30; // 30 days
2222
const WEEK_IN_SECONDS = 60 * 60 * 24 * 7; // 7 days
23-
const DATA_VERSION = "0.12";
23+
export const DATA_VERSION = "0.13";
2424

2525
type AggregatedDataType = "hourly" | "daily";
2626

frontend/react-example/src/features/arkiv-client/constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ export const ARKIV_CHAIN: Chain = mendoza;
2020

2121
export const ENTITY_OWNER = "0xF46E23f6a6F6336D4C64D5D1c95599bF77a536f0";
2222

23-
export const PROTOCOL_VERSION = "0.12";
23+
export const PROTOCOL_VERSION = "0.13";

0 commit comments

Comments
 (0)