@economicagents/graph 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +204 -0
- package/README.md +55 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +81 -0
- package/dist/fleet-alerts.d.ts +23 -0
- package/dist/fleet-alerts.d.ts.map +1 -0
- package/dist/fleet-alerts.js +204 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +398 -0
- package/dist/queries.d.ts +72 -0
- package/dist/queries.d.ts.map +1 -0
- package/dist/queries.js +329 -0
- package/dist/recommendations.d.ts +30 -0
- package/dist/recommendations.d.ts.map +1 -0
- package/dist/recommendations.js +71 -0
- package/dist/store.d.ts +55 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +288 -0
- package/dist/types.d.ts +24 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +56 -0
package/dist/store.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite store for economic graph. Uses better-sqlite3 (optional dependency).
|
|
3
|
+
* Falls back to sql.js (pure JS) when better-sqlite3 native bindings are unavailable.
|
|
4
|
+
*/
|
|
5
|
+
import { createRequire } from "module";
|
|
6
|
+
import { mkdirSync } from "fs";
|
|
7
|
+
import { join } from "path";
|
|
8
|
+
const require = createRequire(import.meta.url);
|
|
9
|
+
let db = null;
|
|
10
|
+
let dbPath = null;
|
|
11
|
+
let sqliteAvailable = null;
|
|
12
|
+
let sqlJsFallback = null;
|
|
13
|
+
/** For tests: set sql.js as fallback when better-sqlite3 native bindings are unavailable. */
|
|
14
|
+
export function setSqlJsFallback(SQL) {
|
|
15
|
+
sqlJsFallback = SQL;
|
|
16
|
+
}
|
|
17
|
+
function wrapSqlJsDb(nativeDb) {
|
|
18
|
+
return {
|
|
19
|
+
exec(sql) {
|
|
20
|
+
nativeDb.exec(sql);
|
|
21
|
+
},
|
|
22
|
+
prepare(sql) {
|
|
23
|
+
return {
|
|
24
|
+
run: (...args) => {
|
|
25
|
+
const stmt = nativeDb.prepare(sql);
|
|
26
|
+
stmt.bind(args);
|
|
27
|
+
while (stmt.step()) {
|
|
28
|
+
/* consume rows */
|
|
29
|
+
}
|
|
30
|
+
stmt.free();
|
|
31
|
+
return { changes: 1 };
|
|
32
|
+
},
|
|
33
|
+
get: (...args) => {
|
|
34
|
+
const stmt = nativeDb.prepare(sql);
|
|
35
|
+
stmt.bind(args);
|
|
36
|
+
const row = stmt.step() ? stmt.getAsObject() : undefined;
|
|
37
|
+
stmt.free();
|
|
38
|
+
return row;
|
|
39
|
+
},
|
|
40
|
+
all: (...args) => {
|
|
41
|
+
const stmt = nativeDb.prepare(sql);
|
|
42
|
+
stmt.bind(args);
|
|
43
|
+
const rows = [];
|
|
44
|
+
while (stmt.step())
|
|
45
|
+
rows.push(stmt.getAsObject());
|
|
46
|
+
stmt.free();
|
|
47
|
+
return rows;
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
close: () => nativeDb.close(),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Exported for tests: true when better-sqlite3 (disk persistence) is active; false when using sql.js fallback. */
|
|
55
|
+
export function isUsingBetterSqlite3() {
|
|
56
|
+
if (sqliteAvailable === null)
|
|
57
|
+
isSqliteAvailable();
|
|
58
|
+
return sqliteAvailable === true;
|
|
59
|
+
}
|
|
60
|
+
/** Exported for tests: whether SQLite (better-sqlite3 or sql.js fallback) is available. */
|
|
61
|
+
export function isSqliteAvailable() {
|
|
62
|
+
if (sqliteAvailable === null) {
|
|
63
|
+
try {
|
|
64
|
+
const Database = require("better-sqlite3");
|
|
65
|
+
const testDb = new Database(":memory:");
|
|
66
|
+
testDb.close();
|
|
67
|
+
sqliteAvailable = true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
sqliteAvailable = false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (sqliteAvailable)
|
|
74
|
+
return true;
|
|
75
|
+
return sqlJsFallback !== null;
|
|
76
|
+
}
|
|
77
|
+
function getDb(graphPath) {
|
|
78
|
+
const resolvedPath = join(graphPath, "graph.db");
|
|
79
|
+
if (db && dbPath === resolvedPath)
|
|
80
|
+
return db;
|
|
81
|
+
if (db) {
|
|
82
|
+
db.close();
|
|
83
|
+
db = null;
|
|
84
|
+
dbPath = null;
|
|
85
|
+
}
|
|
86
|
+
if (!isSqliteAvailable()) {
|
|
87
|
+
throw new Error("better-sqlite3 or sql.js required for graph. Install: npm install better-sqlite3");
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
if (sqliteAvailable) {
|
|
91
|
+
const Database = require("better-sqlite3");
|
|
92
|
+
mkdirSync(graphPath, { recursive: true });
|
|
93
|
+
db = new Database(resolvedPath);
|
|
94
|
+
dbPath = resolvedPath;
|
|
95
|
+
}
|
|
96
|
+
else if (sqlJsFallback) {
|
|
97
|
+
// sql.js creates in-memory DB; graphPath is ignored. Use better-sqlite3 for production persistence.
|
|
98
|
+
const nativeDb = new sqlJsFallback.Database();
|
|
99
|
+
db = wrapSqlJsDb(nativeDb);
|
|
100
|
+
dbPath = resolvedPath;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
throw new Error("No SQLite backend available");
|
|
104
|
+
}
|
|
105
|
+
initSchema(db);
|
|
106
|
+
return db;
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
110
|
+
throw new Error(`Graph database init failed: ${msg}. Install better-sqlite3 for production: npm install better-sqlite3`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function initSchema(database) {
|
|
114
|
+
database.exec(`
|
|
115
|
+
CREATE TABLE IF NOT EXISTS accounts (
|
|
116
|
+
address TEXT PRIMARY KEY,
|
|
117
|
+
owner TEXT NOT NULL,
|
|
118
|
+
firstSeenBlock INTEGER NOT NULL
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
CREATE TABLE IF NOT EXISTS payments (
|
|
122
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
123
|
+
fromAddr TEXT NOT NULL,
|
|
124
|
+
toAddr TEXT NOT NULL,
|
|
125
|
+
amount TEXT NOT NULL,
|
|
126
|
+
token TEXT NOT NULL,
|
|
127
|
+
blockNumber INTEGER NOT NULL,
|
|
128
|
+
txHash TEXT NOT NULL,
|
|
129
|
+
logIndex INTEGER,
|
|
130
|
+
source TEXT NOT NULL
|
|
131
|
+
);
|
|
132
|
+
CREATE INDEX IF NOT EXISTS idx_payments_from ON payments(fromAddr);
|
|
133
|
+
CREATE INDEX IF NOT EXISTS idx_payments_to ON payments(toAddr);
|
|
134
|
+
CREATE INDEX IF NOT EXISTS idx_payments_block ON payments(blockNumber);
|
|
135
|
+
|
|
136
|
+
CREATE TABLE IF NOT EXISTS user_ops (
|
|
137
|
+
userOpHash TEXT PRIMARY KEY,
|
|
138
|
+
sender TEXT NOT NULL,
|
|
139
|
+
success INTEGER NOT NULL,
|
|
140
|
+
actualGasCost TEXT,
|
|
141
|
+
blockNumber INTEGER,
|
|
142
|
+
txHash TEXT
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
CREATE TABLE IF NOT EXISTS facilities (
|
|
146
|
+
address TEXT PRIMARY KEY,
|
|
147
|
+
lender TEXT NOT NULL,
|
|
148
|
+
borrower TEXT NOT NULL,
|
|
149
|
+
firstSeenBlock INTEGER NOT NULL
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
CREATE TABLE IF NOT EXISTS escrows (
|
|
153
|
+
address TEXT PRIMARY KEY,
|
|
154
|
+
consumer TEXT NOT NULL,
|
|
155
|
+
provider TEXT NOT NULL,
|
|
156
|
+
firstSeenBlock INTEGER NOT NULL
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
CREATE TABLE IF NOT EXISTS splitters (
|
|
160
|
+
address TEXT PRIMARY KEY,
|
|
161
|
+
token TEXT NOT NULL,
|
|
162
|
+
firstSeenBlock INTEGER NOT NULL
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
CREATE TABLE IF NOT EXISTS slas (
|
|
166
|
+
address TEXT PRIMARY KEY,
|
|
167
|
+
provider TEXT NOT NULL,
|
|
168
|
+
consumer TEXT NOT NULL,
|
|
169
|
+
firstSeenBlock INTEGER NOT NULL
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
CREATE TABLE IF NOT EXISTS sla_events (
|
|
173
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
174
|
+
slaAddress TEXT NOT NULL,
|
|
175
|
+
eventType TEXT NOT NULL,
|
|
176
|
+
provider TEXT NOT NULL,
|
|
177
|
+
consumer TEXT,
|
|
178
|
+
amount TEXT,
|
|
179
|
+
requestHash TEXT,
|
|
180
|
+
blockNumber INTEGER NOT NULL,
|
|
181
|
+
txHash TEXT
|
|
182
|
+
);
|
|
183
|
+
CREATE INDEX IF NOT EXISTS idx_sla_events_sla ON sla_events(slaAddress);
|
|
184
|
+
CREATE INDEX IF NOT EXISTS idx_sla_events_provider ON sla_events(provider);
|
|
185
|
+
|
|
186
|
+
CREATE TABLE IF NOT EXISTS sync_state (
|
|
187
|
+
contract_key TEXT PRIMARY KEY,
|
|
188
|
+
lastBlock INTEGER NOT NULL
|
|
189
|
+
);
|
|
190
|
+
`);
|
|
191
|
+
}
|
|
192
|
+
export function ensureGraphDir(graphPath) {
|
|
193
|
+
mkdirSync(graphPath, { recursive: true });
|
|
194
|
+
}
|
|
195
|
+
export function getDatabase(graphPath) {
|
|
196
|
+
return getDb(graphPath);
|
|
197
|
+
}
|
|
198
|
+
export function closeDatabase() {
|
|
199
|
+
if (db) {
|
|
200
|
+
db.close();
|
|
201
|
+
db = null;
|
|
202
|
+
dbPath = null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
export function insertAccount(database, address, owner, firstSeenBlock) {
|
|
206
|
+
database
|
|
207
|
+
.prepare("INSERT OR IGNORE INTO accounts (address, owner, firstSeenBlock) VALUES (?, ?, ?)")
|
|
208
|
+
.run(address.toLowerCase(), owner.toLowerCase(), firstSeenBlock);
|
|
209
|
+
}
|
|
210
|
+
export function insertPayment(database, fromAddr, toAddr, amount, token, blockNumber, txHash, logIndex, source) {
|
|
211
|
+
database
|
|
212
|
+
.prepare(`INSERT INTO payments (fromAddr, toAddr, amount, token, blockNumber, txHash, logIndex, source)
|
|
213
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
214
|
+
.run(fromAddr.toLowerCase(), toAddr.toLowerCase(), amount, token.toLowerCase(), blockNumber, txHash, logIndex, source);
|
|
215
|
+
}
|
|
216
|
+
export function insertUserOp(database, userOpHash, sender, success, actualGasCost, blockNumber, txHash) {
|
|
217
|
+
database
|
|
218
|
+
.prepare(`INSERT OR REPLACE INTO user_ops (userOpHash, sender, success, actualGasCost, blockNumber, txHash)
|
|
219
|
+
VALUES (?, ?, ?, ?, ?, ?)`)
|
|
220
|
+
.run(userOpHash, sender.toLowerCase(), success ? 1 : 0, actualGasCost, blockNumber, txHash);
|
|
221
|
+
}
|
|
222
|
+
export function insertFacility(database, address, lender, borrower, firstSeenBlock) {
|
|
223
|
+
database
|
|
224
|
+
.prepare("INSERT OR IGNORE INTO facilities (address, lender, borrower, firstSeenBlock) VALUES (?, ?, ?, ?)")
|
|
225
|
+
.run(address.toLowerCase(), lender.toLowerCase(), borrower.toLowerCase(), firstSeenBlock);
|
|
226
|
+
}
|
|
227
|
+
export function insertEscrow(database, address, consumer, provider, firstSeenBlock) {
|
|
228
|
+
database
|
|
229
|
+
.prepare("INSERT OR IGNORE INTO escrows (address, consumer, provider, firstSeenBlock) VALUES (?, ?, ?, ?)")
|
|
230
|
+
.run(address.toLowerCase(), consumer.toLowerCase(), provider.toLowerCase(), firstSeenBlock);
|
|
231
|
+
}
|
|
232
|
+
export function insertSplitter(database, address, token, firstSeenBlock) {
|
|
233
|
+
database
|
|
234
|
+
.prepare("INSERT OR IGNORE INTO splitters (address, token, firstSeenBlock) VALUES (?, ?, ?)")
|
|
235
|
+
.run(address.toLowerCase(), token.toLowerCase(), firstSeenBlock);
|
|
236
|
+
}
|
|
237
|
+
export function insertSLA(database, address, provider, consumer, firstSeenBlock) {
|
|
238
|
+
database
|
|
239
|
+
.prepare("INSERT OR IGNORE INTO slas (address, provider, consumer, firstSeenBlock) VALUES (?, ?, ?, ?)")
|
|
240
|
+
.run(address.toLowerCase(), provider.toLowerCase(), consumer.toLowerCase(), firstSeenBlock);
|
|
241
|
+
}
|
|
242
|
+
export function getSyncState(database, key) {
|
|
243
|
+
const row = database
|
|
244
|
+
.prepare("SELECT lastBlock FROM sync_state WHERE contract_key = ?")
|
|
245
|
+
.get(key);
|
|
246
|
+
return row?.lastBlock ?? null;
|
|
247
|
+
}
|
|
248
|
+
export function setSyncState(database, key, lastBlock) {
|
|
249
|
+
database
|
|
250
|
+
.prepare("INSERT OR REPLACE INTO sync_state (contract_key, lastBlock) VALUES (?, ?)")
|
|
251
|
+
.run(key, lastBlock);
|
|
252
|
+
}
|
|
253
|
+
export function getAccountAddresses(database) {
|
|
254
|
+
const rows = database
|
|
255
|
+
.prepare("SELECT address FROM accounts")
|
|
256
|
+
.all();
|
|
257
|
+
return rows.map((r) => r.address);
|
|
258
|
+
}
|
|
259
|
+
export function getFacilityAddresses(database) {
|
|
260
|
+
const rows = database
|
|
261
|
+
.prepare("SELECT address FROM facilities")
|
|
262
|
+
.all();
|
|
263
|
+
return rows.map((r) => r.address);
|
|
264
|
+
}
|
|
265
|
+
export function getEscrowAddresses(database) {
|
|
266
|
+
const rows = database
|
|
267
|
+
.prepare("SELECT address FROM escrows")
|
|
268
|
+
.all();
|
|
269
|
+
return rows.map((r) => r.address);
|
|
270
|
+
}
|
|
271
|
+
export function getSplitterAddresses(database) {
|
|
272
|
+
const rows = database
|
|
273
|
+
.prepare("SELECT address FROM splitters")
|
|
274
|
+
.all();
|
|
275
|
+
return rows.map((r) => r.address);
|
|
276
|
+
}
|
|
277
|
+
export function getSLAAddresses(database) {
|
|
278
|
+
const rows = database
|
|
279
|
+
.prepare("SELECT address FROM slas")
|
|
280
|
+
.all();
|
|
281
|
+
return rows.map((r) => r.address);
|
|
282
|
+
}
|
|
283
|
+
export function insertSLAEvent(database, slaAddress, eventType, provider, consumer, amount, requestHash, blockNumber, txHash) {
|
|
284
|
+
database
|
|
285
|
+
.prepare(`INSERT INTO sla_events (slaAddress, eventType, provider, consumer, amount, requestHash, blockNumber, txHash)
|
|
286
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
287
|
+
.run(slaAddress.toLowerCase(), eventType, provider.toLowerCase(), consumer?.toLowerCase() ?? null, amount, requestHash, blockNumber, txHash);
|
|
288
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
export interface GraphConfig {
|
|
3
|
+
rpcUrl: string;
|
|
4
|
+
chainId: number;
|
|
5
|
+
graphPath: string;
|
|
6
|
+
aepAccountFactoryAddress: Address;
|
|
7
|
+
entryPointAddress: Address;
|
|
8
|
+
creditFacilityFactoryAddress?: Address;
|
|
9
|
+
escrowFactoryAddress?: Address;
|
|
10
|
+
revenueSplitterFactoryAddress?: Address;
|
|
11
|
+
slaFactoryAddress?: Address;
|
|
12
|
+
usdcAddress: Address;
|
|
13
|
+
}
|
|
14
|
+
export interface SyncResult {
|
|
15
|
+
accountsAdded: number;
|
|
16
|
+
paymentsAdded: number;
|
|
17
|
+
userOpsAdded: number;
|
|
18
|
+
creditEventsAdded: number;
|
|
19
|
+
escrowEventsAdded: number;
|
|
20
|
+
splitterEventsAdded: number;
|
|
21
|
+
slaEventsAdded: number;
|
|
22
|
+
}
|
|
23
|
+
export type PaymentSource = "transfer" | "credit_draw" | "credit_repay" | "escrow_fund" | "escrow_release" | "splitter_distribute";
|
|
24
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEpC,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,wBAAwB,EAAE,OAAO,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,6BAA6B,CAAC,EAAE,OAAO,CAAC;IACxC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GACrB,UAAU,GACV,aAAa,GACb,cAAc,GACd,aAAa,GACb,gBAAgB,GAChB,qBAAqB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@economicagents/graph",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Economic graph for AEP - transaction graph, payments, credit events, credit scoring, recommendations",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/economicagents/AEP.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": "https://github.com/economicagents/AEP/issues",
|
|
11
|
+
"homepage": "https://github.com/economicagents/AEP#readme",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"aep",
|
|
14
|
+
"agent",
|
|
15
|
+
"graph",
|
|
16
|
+
"analytics",
|
|
17
|
+
"credit-score"
|
|
18
|
+
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"type": "module",
|
|
23
|
+
"main": "dist/index.js",
|
|
24
|
+
"types": "dist/index.d.ts",
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"import": "./dist/index.js"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"aep-graph": "./dist/cli.js"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"viem": "^2.21.0"
|
|
36
|
+
},
|
|
37
|
+
"optionalDependencies": {
|
|
38
|
+
"better-sqlite3": "^12.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
42
|
+
"sql.js": "^1.14.0",
|
|
43
|
+
"typescript": "^5.6.0",
|
|
44
|
+
"vitest": "^2.1.0"
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"dist"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc",
|
|
51
|
+
"dev": "tsc --watch",
|
|
52
|
+
"sync": "node dist/cli.js sync",
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest"
|
|
55
|
+
}
|
|
56
|
+
}
|