auramaxx 1.0.0-alpha.4
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 +26 -0
- package/README.md +112 -0
- package/bin/aurawallet.js +121 -0
- package/docs/ADAPTERS.md +467 -0
- package/docs/API.md +2679 -0
- package/docs/APPS.md +198 -0
- package/docs/ARCHITECTURE.md +350 -0
- package/docs/AUTH.md +698 -0
- package/docs/BEST-PRACTICES.md +121 -0
- package/docs/CLI.md +61 -0
- package/docs/DEVELOPING-APPS.md +452 -0
- package/docs/EXTENSION.md +97 -0
- package/docs/JOBS.md +33 -0
- package/docs/MCP.md +76 -0
- package/docs/PROTOCOL.md +142 -0
- package/docs/SETUP.md +219 -0
- package/docs/WORKSPACE.md +672 -0
- package/docs/agent-auth.md +63 -0
- package/docs/aura-file.md +48 -0
- package/docs/credentials.md +53 -0
- package/docs/external/getting-started.md +65 -0
- package/docs/external/overview.md +45 -0
- package/docs/external/use-cases.md +48 -0
- package/docs/external/why-aura.md +35 -0
- package/docs/jobs/connect-agent.md +77 -0
- package/docs/jobs/migrate-from-dotenv.md +79 -0
- package/docs/jobs/recover-from-lockout.md +72 -0
- package/docs/jobs/secure-ci.md +63 -0
- package/docs/oauth2.md +42 -0
- package/docs/passkeys.md +60 -0
- package/docs/security.md +540 -0
- package/docs/specs/aura-open-protocol.md +61 -0
- package/docs/specs/aura-provider-plugin.md +24 -0
- package/docs/specs/aura-registry-model.md +31 -0
- package/docs/specs/fixtures/invalid-bad-key.aura +1 -0
- package/docs/specs/fixtures/invalid-bad-unicode-escape.aura +1 -0
- package/docs/specs/fixtures/invalid-duplicate-key.aura +2 -0
- package/docs/specs/fixtures/valid-basic.aura +4 -0
- package/docs/specs/fixtures/valid-provider-ref.aura +1 -0
- package/docs/specs/fixtures/valid-quoted-escapes.aura +2 -0
- package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
- package/docs/totp.md +40 -0
- package/docs/wallet/AI.md +508 -0
- package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
- package/docs/wallet/README.md +47 -0
- package/docs/wallet/STRATEGY.md +89 -0
- package/next.config.ts +21 -0
- package/package.json +151 -0
- package/postcss.config.mjs +8 -0
- package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
- package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
- package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +447 -0
- package/public/logo-chevron.svg +31 -0
- package/public/logo-concentric.svg +31 -0
- package/public/logo-crosshatch.svg +39 -0
- package/public/logo-dashed.svg +39 -0
- package/public/logo-horizontal.svg +31 -0
- package/public/logo-m56.svg +64 -0
- package/public/logo.webp +0 -0
- package/scripts/add-app.js +245 -0
- package/scripts/init.sh +57 -0
- package/scripts/migrate-apikeys-to-credentials.ts +35 -0
- package/scripts/sandbox-agent-flow.sh +235 -0
- package/scripts/sandbox.sh +175 -0
- package/scripts/validate-job-docs.mjs +125 -0
- package/server/abi/SwapHelper.json +438 -0
- package/server/cli/approval.ts +447 -0
- package/server/cli/commands/app.ts +204 -0
- package/server/cli/commands/cron.ts +24 -0
- package/server/cli/commands/doctor.ts +1007 -0
- package/server/cli/commands/env.ts +456 -0
- package/server/cli/commands/init.ts +752 -0
- package/server/cli/commands/mcp.ts +125 -0
- package/server/cli/commands/restore.ts +314 -0
- package/server/cli/commands/shell-hook.ts +468 -0
- package/server/cli/commands/start.ts +62 -0
- package/server/cli/commands/status.ts +59 -0
- package/server/cli/commands/stop.ts +14 -0
- package/server/cli/commands/token.ts +180 -0
- package/server/cli/commands/unlock.ts +49 -0
- package/server/cli/commands/vault.ts +417 -0
- package/server/cli/index.ts +328 -0
- package/server/cli/lib/aura-parser.ts +64 -0
- package/server/cli/lib/credential-create.ts +74 -0
- package/server/cli/lib/credential-resolve.ts +254 -0
- package/server/cli/lib/dotenv-migrate.ts +116 -0
- package/server/cli/lib/dotenv-parser.ts +146 -0
- package/server/cli/lib/http.ts +91 -0
- package/server/cli/lib/init-steps.ts +76 -0
- package/server/cli/lib/local-agent-trust.ts +45 -0
- package/server/cli/lib/process.ts +136 -0
- package/server/cli/lib/prompt.ts +85 -0
- package/server/cli/lib/theme.ts +240 -0
- package/server/cli/socket.ts +570 -0
- package/server/cli/transport-client.ts +50 -0
- package/server/cron/index.ts +137 -0
- package/server/cron/job.ts +31 -0
- package/server/cron/jobs/balance-sync.ts +436 -0
- package/server/cron/jobs/incoming-scan.ts +506 -0
- package/server/cron/jobs/native-price.ts +70 -0
- package/server/cron/jobs/orphan-cleanup.ts +40 -0
- package/server/cron/jobs/strategy-runner.ts +175 -0
- package/server/cron/scheduler.ts +125 -0
- package/server/index.ts +406 -0
- package/server/lib/adapters/factory.ts +110 -0
- package/server/lib/adapters/index.ts +19 -0
- package/server/lib/adapters/router.ts +297 -0
- package/server/lib/adapters/telegram.ts +645 -0
- package/server/lib/adapters/types.ts +89 -0
- package/server/lib/adapters/webhook.ts +95 -0
- package/server/lib/address.ts +49 -0
- package/server/lib/agent-auth/contracts.ts +1194 -0
- package/server/lib/agent-profiles.ts +328 -0
- package/server/lib/ai.ts +285 -0
- package/server/lib/api-registry/contracts.ts +86 -0
- package/server/lib/api-registry/validation.ts +172 -0
- package/server/lib/apikey-migration.ts +189 -0
- package/server/lib/app-installer.ts +505 -0
- package/server/lib/app-tokens.ts +247 -0
- package/server/lib/auth.ts +314 -0
- package/server/lib/batch.ts +242 -0
- package/server/lib/cold.ts +874 -0
- package/server/lib/config.ts +381 -0
- package/server/lib/credential-access-audit.ts +85 -0
- package/server/lib/credential-access-policy.ts +110 -0
- package/server/lib/credential-health.ts +343 -0
- package/server/lib/credential-import.ts +487 -0
- package/server/lib/credential-scope.ts +87 -0
- package/server/lib/credential-shares.ts +190 -0
- package/server/lib/credential-transport.ts +342 -0
- package/server/lib/credential-vault.ts +77 -0
- package/server/lib/credentials.ts +333 -0
- package/server/lib/crypto.ts +8 -0
- package/server/lib/db.ts +15 -0
- package/server/lib/defaults.ts +366 -0
- package/server/lib/dex/index.ts +80 -0
- package/server/lib/dex/relay.ts +235 -0
- package/server/lib/dex/types.ts +59 -0
- package/server/lib/dex/uniswap.ts +370 -0
- package/server/lib/e2e-agent/artifacts.ts +36 -0
- package/server/lib/e2e-agent/contracts.ts +112 -0
- package/server/lib/e2e-agent/validation.ts +135 -0
- package/server/lib/encrypt.ts +128 -0
- package/server/lib/error.ts +20 -0
- package/server/lib/events.ts +205 -0
- package/server/lib/hot.ts +357 -0
- package/server/lib/key-fingerprint.ts +28 -0
- package/server/lib/logger.ts +331 -0
- package/server/lib/network.ts +137 -0
- package/server/lib/notifications.ts +219 -0
- package/server/lib/oauth2-refresh.ts +241 -0
- package/server/lib/oursecret.ts +54 -0
- package/server/lib/passkey-credential.ts +360 -0
- package/server/lib/passkey.ts +68 -0
- package/server/lib/permissions.ts +248 -0
- package/server/lib/pino.ts +24 -0
- package/server/lib/policy-preview.ts +138 -0
- package/server/lib/price.ts +338 -0
- package/server/lib/prices.ts +34 -0
- package/server/lib/project-scope.ts +239 -0
- package/server/lib/resolve-action.ts +427 -0
- package/server/lib/resolve.ts +36 -0
- package/server/lib/sessions.ts +632 -0
- package/server/lib/solana/connection.ts +26 -0
- package/server/lib/solana/jupiter.ts +128 -0
- package/server/lib/solana/transfer.ts +108 -0
- package/server/lib/solana/wallet.ts +136 -0
- package/server/lib/strategy/emits.ts +21 -0
- package/server/lib/strategy/engine.ts +1305 -0
- package/server/lib/strategy/executor.ts +115 -0
- package/server/lib/strategy/hook-context.ts +158 -0
- package/server/lib/strategy/hooks.ts +990 -0
- package/server/lib/strategy/index.ts +28 -0
- package/server/lib/strategy/installer.ts +305 -0
- package/server/lib/strategy/loader.ts +256 -0
- package/server/lib/strategy/message.ts +235 -0
- package/server/lib/strategy/repository.ts +218 -0
- package/server/lib/strategy/session-logger.ts +693 -0
- package/server/lib/strategy/sources.ts +288 -0
- package/server/lib/strategy/state.ts +189 -0
- package/server/lib/strategy/templates.ts +403 -0
- package/server/lib/strategy/tick.ts +404 -0
- package/server/lib/strategy/types.ts +230 -0
- package/server/lib/swap.ts +3 -0
- package/server/lib/temp.ts +86 -0
- package/server/lib/token-metadata.ts +86 -0
- package/server/lib/token-safety.ts +200 -0
- package/server/lib/token-search.ts +444 -0
- package/server/lib/totp.ts +194 -0
- package/server/lib/transactions.ts +123 -0
- package/server/lib/transport.ts +75 -0
- package/server/lib/txhistory/decoder.ts +262 -0
- package/server/lib/txhistory/enricher.ts +652 -0
- package/server/lib/txhistory/index.ts +391 -0
- package/server/lib/txhistory/signatures.ts +59 -0
- package/server/lib/verified-summary.ts +421 -0
- package/server/mcp/profile-policy.ts +30 -0
- package/server/mcp/server.ts +619 -0
- package/server/mcp/tools.ts +523 -0
- package/server/middleware/auth.ts +119 -0
- package/server/middleware/requestLogger.ts +84 -0
- package/server/routes/actions.ts +459 -0
- package/server/routes/adapters.ts +703 -0
- package/server/routes/addressbook.ts +113 -0
- package/server/routes/ai.ts +34 -0
- package/server/routes/apikeys.ts +295 -0
- package/server/routes/apps.ts +601 -0
- package/server/routes/auth.ts +457 -0
- package/server/routes/backup.ts +340 -0
- package/server/routes/batch.ts +270 -0
- package/server/routes/bookmarks.ts +162 -0
- package/server/routes/credential-shares.ts +198 -0
- package/server/routes/credential-vaults.ts +154 -0
- package/server/routes/credentials.ts +1290 -0
- package/server/routes/dashboard.ts +71 -0
- package/server/routes/defaults.ts +124 -0
- package/server/routes/fund.ts +229 -0
- package/server/routes/import.ts +352 -0
- package/server/routes/launch.ts +665 -0
- package/server/routes/lock.ts +54 -0
- package/server/routes/logs.ts +68 -0
- package/server/routes/nuke.ts +111 -0
- package/server/routes/passkey-credentials.ts +99 -0
- package/server/routes/passkey.ts +346 -0
- package/server/routes/portfolio.ts +217 -0
- package/server/routes/price.ts +63 -0
- package/server/routes/resolve.ts +31 -0
- package/server/routes/security.ts +45 -0
- package/server/routes/send-evm.ts +241 -0
- package/server/routes/send-solana.ts +281 -0
- package/server/routes/send.ts +178 -0
- package/server/routes/setup.ts +210 -0
- package/server/routes/strategy.ts +894 -0
- package/server/routes/swap-evm.ts +353 -0
- package/server/routes/swap-solana.ts +177 -0
- package/server/routes/swap.ts +356 -0
- package/server/routes/token.ts +247 -0
- package/server/routes/unlock.ts +403 -0
- package/server/routes/wallet-assets.ts +361 -0
- package/server/routes/wallet-transactions.ts +515 -0
- package/server/routes/wallet.ts +710 -0
- package/server/types.ts +146 -0
- package/skills/aurawallet/SKILL.md +739 -0
- package/skills/aurawallet-setup/SKILL.md +74 -0
- package/skills/security-review/SKILL.md +148 -0
- package/src/app/api/agent-requests/route.ts +30 -0
- package/src/app/api/apps/install/route.ts +126 -0
- package/src/app/api/apps/manifests/route.ts +16 -0
- package/src/app/api/apps/static/[...path]/route.ts +57 -0
- package/src/app/api/events/route.ts +92 -0
- package/src/app/api/page.tsx +212 -0
- package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
- package/src/app/api/workspace/[id]/apps/route.ts +81 -0
- package/src/app/api/workspace/[id]/export/route.ts +67 -0
- package/src/app/api/workspace/[id]/route.ts +168 -0
- package/src/app/api/workspace/auth.ts +34 -0
- package/src/app/api/workspace/config/route.ts +106 -0
- package/src/app/api/workspace/import/route.ts +127 -0
- package/src/app/api/workspace/route.ts +116 -0
- package/src/app/app/page.tsx +2122 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/docs/page.tsx +178 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +572 -0
- package/src/app/health/page.tsx +5 -0
- package/src/app/hello/page.tsx +15 -0
- package/src/app/icon.png +0 -0
- package/src/app/layout.tsx +34 -0
- package/src/app/page.tsx +986 -0
- package/src/app/providers.tsx +90 -0
- package/src/app/share/[token]/page.tsx +295 -0
- package/src/components/ChainSelector.tsx +144 -0
- package/src/components/HumanActionBar.tsx +695 -0
- package/src/components/NotificationDrawer.tsx +129 -0
- package/src/components/apps/AgentKeysApp.tsx +490 -0
- package/src/components/apps/App.tsx +153 -0
- package/src/components/apps/AppGrid.tsx +15 -0
- package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
- package/src/components/apps/DraggableApp.tsx +562 -0
- package/src/components/apps/IFrameApp.tsx +73 -0
- package/src/components/apps/LogsApp.tsx +360 -0
- package/src/components/apps/SendApp.tsx +394 -0
- package/src/components/apps/SetupWizardApp.tsx +1004 -0
- package/src/components/apps/SystemDefaultsApp.tsx +845 -0
- package/src/components/apps/ThirdPartyApp.tsx +428 -0
- package/src/components/apps/TokenApp.tsx +319 -0
- package/src/components/apps/TransactionsApp.tsx +438 -0
- package/src/components/apps/WalletDetailApp.tsx +1505 -0
- package/src/components/apps/index.ts +13 -0
- package/src/components/design-system/Button.tsx +53 -0
- package/src/components/design-system/ChainIndicator.tsx +65 -0
- package/src/components/design-system/ChainSelector.tsx +137 -0
- package/src/components/design-system/ConfirmationModal.tsx +106 -0
- package/src/components/design-system/ConfirmationPopover.tsx +81 -0
- package/src/components/design-system/Drawer.tsx +123 -0
- package/src/components/design-system/FilterDropdown.tsx +72 -0
- package/src/components/design-system/Modal.tsx +206 -0
- package/src/components/design-system/Popover.tsx +142 -0
- package/src/components/design-system/TextInput.tsx +85 -0
- package/src/components/design-system/Toggle.tsx +58 -0
- package/src/components/design-system/index.ts +11 -0
- package/src/components/docs/DocsThemeToggle.tsx +49 -0
- package/src/components/health/CredentialHealthDashboard.tsx +214 -0
- package/src/components/icons/ChainIcons.tsx +72 -0
- package/src/components/layout/AppStoreDrawer.tsx +369 -0
- package/src/components/layout/ContentArea.tsx +21 -0
- package/src/components/layout/TabBar.tsx +278 -0
- package/src/components/layout/WalletSidebar.tsx +1033 -0
- package/src/components/layout/index.ts +4 -0
- package/src/components/marketing/AuraWalletSpecOverlay.tsx +635 -0
- package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
- package/src/components/vault/ApiKeysConsole.tsx +1080 -0
- package/src/components/vault/AuditConsole.tsx +584 -0
- package/src/components/vault/CredentialDetail.tsx +455 -0
- package/src/components/vault/CredentialEmpty.tsx +55 -0
- package/src/components/vault/CredentialField.tsx +361 -0
- package/src/components/vault/CredentialForm.tsx +1212 -0
- package/src/components/vault/CredentialList.tsx +165 -0
- package/src/components/vault/CredentialRow.tsx +97 -0
- package/src/components/vault/CredentialShareModal.tsx +178 -0
- package/src/components/vault/CredentialVault.tsx +754 -0
- package/src/components/vault/CredentialWalletWidget.tsx +103 -0
- package/src/components/vault/ImportCredentialsModal.tsx +515 -0
- package/src/components/vault/LargeTypeModal.tsx +64 -0
- package/src/components/vault/PasswordGenerator.tsx +224 -0
- package/src/components/vault/TOTPDisplay.tsx +123 -0
- package/src/components/vault/VaultSidebar.tsx +413 -0
- package/src/components/vault/types.ts +54 -0
- package/src/context/AuthContext.tsx +337 -0
- package/src/context/PriceContext.tsx +113 -0
- package/src/context/ThemeContext.tsx +164 -0
- package/src/context/WebSocketContext.tsx +269 -0
- package/src/context/WorkspaceContext.tsx +668 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/useAgentActions.ts +368 -0
- package/src/hooks/useBalance.ts +103 -0
- package/src/hooks/useBalances.ts +129 -0
- package/src/instrumentation.ts +12 -0
- package/src/lib/api.ts +449 -0
- package/src/lib/app-loader.ts +148 -0
- package/src/lib/app-registry.ts +178 -0
- package/src/lib/app-sdk.ts +157 -0
- package/src/lib/audit-console-adapter.ts +151 -0
- package/src/lib/auth-client.ts +75 -0
- package/src/lib/config.ts +74 -0
- package/src/lib/crypto.ts +112 -0
- package/src/lib/db.ts +21 -0
- package/src/lib/docs.ts +390 -0
- package/src/lib/events.ts +361 -0
- package/src/lib/pino.ts +24 -0
- package/src/lib/theme-handlers.ts +168 -0
- package/src/lib/theme.ts +351 -0
- package/src/lib/tokenData.ts +378 -0
- package/src/lib/vault-crypto.ts +129 -0
- package/src/lib/websocket-server.ts +302 -0
- package/src/lib/websocket-setup.ts +79 -0
- package/src/lib/wordlist.ts +2050 -0
- package/src/lib/workspace-handlers.ts +285 -0
- package/start.sh +80 -0
- package/tailwind.config.ts +99 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token & Pool Metadata Enricher
|
|
3
|
+
* ===============================
|
|
4
|
+
* Resolves token/pool metadata from DB cache or on-chain,
|
|
5
|
+
* then enriches decoded events into human-readable summaries.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createPublicClient, http, erc20Abi, type Address } from 'viem';
|
|
9
|
+
import { base, mainnet } from 'viem/chains';
|
|
10
|
+
import { getRpcUrl } from '../config';
|
|
11
|
+
import { prisma } from '../db';
|
|
12
|
+
import { KNOWN_CONTRACTS, EVENT_SIGNATURES } from './signatures';
|
|
13
|
+
import type { DecodedEvent } from './decoder';
|
|
14
|
+
|
|
15
|
+
/** Serialize event params for JSON — converts BigInt values to strings */
|
|
16
|
+
function safeParams(params: Record<string, unknown>): Record<string, string> {
|
|
17
|
+
const out: Record<string, string> = {};
|
|
18
|
+
for (const [k, v] of Object.entries(params)) {
|
|
19
|
+
out[k] = typeof v === 'bigint' ? v.toString() : String(v);
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// --- Types ---
|
|
25
|
+
|
|
26
|
+
export interface TokenInfo {
|
|
27
|
+
address: string;
|
|
28
|
+
symbol: string;
|
|
29
|
+
name: string;
|
|
30
|
+
decimals: number;
|
|
31
|
+
icon?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface PoolInfo {
|
|
35
|
+
address: string;
|
|
36
|
+
token0: string;
|
|
37
|
+
token1: string;
|
|
38
|
+
fee?: number;
|
|
39
|
+
dex?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface EnrichedTransaction {
|
|
43
|
+
type: string;
|
|
44
|
+
summary: string;
|
|
45
|
+
txHash: string;
|
|
46
|
+
blockNumber: string; // stringified bigint
|
|
47
|
+
timestamp?: number;
|
|
48
|
+
protocol?: string;
|
|
49
|
+
details: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Map chain names to viem chain objects
|
|
53
|
+
const VIEM_CHAINS: Record<string, typeof base> = {
|
|
54
|
+
base,
|
|
55
|
+
ethereum: mainnet,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// --- Token Resolution ---
|
|
59
|
+
|
|
60
|
+
export async function resolveTokenMetadataBatch(
|
|
61
|
+
addresses: string[],
|
|
62
|
+
chain: string,
|
|
63
|
+
): Promise<Map<string, TokenInfo>> {
|
|
64
|
+
const result = new Map<string, TokenInfo>();
|
|
65
|
+
if (addresses.length === 0) return result;
|
|
66
|
+
|
|
67
|
+
const normalized = addresses.map(a => a.toLowerCase());
|
|
68
|
+
const unique = [...new Set(normalized)];
|
|
69
|
+
|
|
70
|
+
// 1. Check DB cache
|
|
71
|
+
const cached = await prisma.tokenMetadata.findMany({
|
|
72
|
+
where: {
|
|
73
|
+
tokenAddress: { in: unique },
|
|
74
|
+
chain,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const uncached: string[] = [];
|
|
79
|
+
for (const addr of unique) {
|
|
80
|
+
const hit = cached.find(c => c.tokenAddress === addr);
|
|
81
|
+
if (hit) {
|
|
82
|
+
result.set(addr, {
|
|
83
|
+
address: addr,
|
|
84
|
+
symbol: hit.symbol || 'UNKNOWN',
|
|
85
|
+
name: hit.name || 'Unknown Token',
|
|
86
|
+
decimals: hit.decimals,
|
|
87
|
+
icon: hit.icon || undefined,
|
|
88
|
+
});
|
|
89
|
+
} else {
|
|
90
|
+
uncached.push(addr);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (uncached.length === 0) return result;
|
|
95
|
+
|
|
96
|
+
// 2. Multicall for cache misses
|
|
97
|
+
try {
|
|
98
|
+
const rpcUrl = await getRpcUrl(chain);
|
|
99
|
+
const viemChain = VIEM_CHAINS[chain];
|
|
100
|
+
const client = createPublicClient({
|
|
101
|
+
chain: viemChain,
|
|
102
|
+
transport: http(rpcUrl),
|
|
103
|
+
batch: { multicall: true },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const contracts = uncached.flatMap(addr => [
|
|
107
|
+
{ address: addr as Address, abi: erc20Abi, functionName: 'symbol' as const },
|
|
108
|
+
{ address: addr as Address, abi: erc20Abi, functionName: 'name' as const },
|
|
109
|
+
{ address: addr as Address, abi: erc20Abi, functionName: 'decimals' as const },
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
const multicallResults = await client.multicall({
|
|
113
|
+
contracts,
|
|
114
|
+
allowFailure: true,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
for (let i = 0; i < uncached.length; i++) {
|
|
118
|
+
const addr = uncached[i];
|
|
119
|
+
const symbolResult = multicallResults[i * 3];
|
|
120
|
+
const nameResult = multicallResults[i * 3 + 1];
|
|
121
|
+
const decimalsResult = multicallResults[i * 3 + 2];
|
|
122
|
+
|
|
123
|
+
const symbol = symbolResult.status === 'success' ? String(symbolResult.result) : 'UNKNOWN';
|
|
124
|
+
const name = nameResult.status === 'success' ? String(nameResult.result) : 'Unknown Token';
|
|
125
|
+
const decimals = decimalsResult.status === 'success' ? Number(decimalsResult.result) : 18;
|
|
126
|
+
|
|
127
|
+
const info: TokenInfo = { address: addr, symbol, name, decimals };
|
|
128
|
+
result.set(addr, info);
|
|
129
|
+
|
|
130
|
+
// Write to DB cache (fire-and-forget)
|
|
131
|
+
prisma.tokenMetadata.upsert({
|
|
132
|
+
where: { tokenAddress_chain: { tokenAddress: addr, chain } },
|
|
133
|
+
create: { tokenAddress: addr, chain, symbol, name, decimals },
|
|
134
|
+
update: { symbol, name, decimals },
|
|
135
|
+
}).catch(() => {});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Fire-and-forget: DexScreener enrichment for icon
|
|
139
|
+
enrichFromDexScreener(uncached, chain).catch(() => {});
|
|
140
|
+
} catch {
|
|
141
|
+
// RPC failure — fill with defaults
|
|
142
|
+
for (const addr of uncached) {
|
|
143
|
+
if (!result.has(addr)) {
|
|
144
|
+
result.set(addr, { address: addr, symbol: 'UNKNOWN', name: 'Unknown Token', decimals: 18 });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Fire-and-forget DexScreener enrichment for token icons */
|
|
153
|
+
async function enrichFromDexScreener(addresses: string[], chain: string): Promise<void> {
|
|
154
|
+
for (const addr of addresses) {
|
|
155
|
+
try {
|
|
156
|
+
const res = await fetch(
|
|
157
|
+
`https://api.dexscreener.com/latest/dex/tokens/${addr}`,
|
|
158
|
+
{ signal: AbortSignal.timeout(5000) },
|
|
159
|
+
);
|
|
160
|
+
if (!res.ok) continue;
|
|
161
|
+
const data = await res.json();
|
|
162
|
+
const pairs: any[] = data.pairs || [];
|
|
163
|
+
if (pairs.length === 0) continue;
|
|
164
|
+
|
|
165
|
+
const info = pairs[0].baseToken;
|
|
166
|
+
if (!info) continue;
|
|
167
|
+
|
|
168
|
+
const icon = (pairs[0] as any).info?.imageUrl || null;
|
|
169
|
+
if (icon) {
|
|
170
|
+
await prisma.tokenMetadata.update({
|
|
171
|
+
where: { tokenAddress_chain: { tokenAddress: addr, chain } },
|
|
172
|
+
data: { icon },
|
|
173
|
+
}).catch(() => {});
|
|
174
|
+
}
|
|
175
|
+
} catch {
|
|
176
|
+
// Non-critical
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// --- Pool Resolution ---
|
|
182
|
+
|
|
183
|
+
const POOL_ABI = [
|
|
184
|
+
{ inputs: [], name: 'token0', outputs: [{ type: 'address' }], stateMutability: 'view', type: 'function' },
|
|
185
|
+
{ inputs: [], name: 'token1', outputs: [{ type: 'address' }], stateMutability: 'view', type: 'function' },
|
|
186
|
+
{ inputs: [], name: 'fee', outputs: [{ type: 'uint24' }], stateMutability: 'view', type: 'function' },
|
|
187
|
+
] as const;
|
|
188
|
+
|
|
189
|
+
export async function resolvePoolMetadata(
|
|
190
|
+
poolAddress: string,
|
|
191
|
+
chain: string,
|
|
192
|
+
): Promise<PoolInfo | null> {
|
|
193
|
+
const addr = poolAddress.toLowerCase();
|
|
194
|
+
|
|
195
|
+
// 1. Check DB cache
|
|
196
|
+
const cached = await prisma.poolMetadata.findUnique({
|
|
197
|
+
where: { poolAddress_chain: { poolAddress: addr, chain } },
|
|
198
|
+
});
|
|
199
|
+
if (cached) {
|
|
200
|
+
return {
|
|
201
|
+
address: addr,
|
|
202
|
+
token0: cached.token0 || '',
|
|
203
|
+
token1: cached.token1 || '',
|
|
204
|
+
fee: cached.fee ?? undefined,
|
|
205
|
+
dex: cached.dex ?? undefined,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// 2. Multicall for pool contract
|
|
210
|
+
try {
|
|
211
|
+
const rpcUrl = await getRpcUrl(chain);
|
|
212
|
+
const viemChain = VIEM_CHAINS[chain];
|
|
213
|
+
const client = createPublicClient({
|
|
214
|
+
chain: viemChain,
|
|
215
|
+
transport: http(rpcUrl),
|
|
216
|
+
batch: { multicall: true },
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const results = await client.multicall({
|
|
220
|
+
contracts: [
|
|
221
|
+
{ address: addr as Address, abi: POOL_ABI, functionName: 'token0' },
|
|
222
|
+
{ address: addr as Address, abi: POOL_ABI, functionName: 'token1' },
|
|
223
|
+
{ address: addr as Address, abi: POOL_ABI, functionName: 'fee' },
|
|
224
|
+
],
|
|
225
|
+
allowFailure: true,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
const token0 = results[0].status === 'success' ? (results[0].result as string).toLowerCase() : null;
|
|
229
|
+
const token1 = results[1].status === 'success' ? (results[1].result as string).toLowerCase() : null;
|
|
230
|
+
const fee = results[2].status === 'success' ? Number(results[2].result) : null;
|
|
231
|
+
|
|
232
|
+
if (!token0 || !token1) return null;
|
|
233
|
+
|
|
234
|
+
const dex = fee !== null ? 'uniswap_v3' : 'uniswap_v2';
|
|
235
|
+
const pool: PoolInfo = { address: addr, token0, token1, fee: fee ?? undefined, dex };
|
|
236
|
+
|
|
237
|
+
// Write to cache (immutable)
|
|
238
|
+
prisma.poolMetadata.create({
|
|
239
|
+
data: { poolAddress: addr, chain, token0, token1, fee, dex },
|
|
240
|
+
}).catch(() => {});
|
|
241
|
+
|
|
242
|
+
return pool;
|
|
243
|
+
} catch {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export async function resolveV4PoolId(
|
|
249
|
+
poolId: string,
|
|
250
|
+
chain: string,
|
|
251
|
+
): Promise<PoolInfo | null> {
|
|
252
|
+
// 1. Check DB cache (use poolId as poolAddress key)
|
|
253
|
+
const cached = await prisma.poolMetadata.findUnique({
|
|
254
|
+
where: { poolAddress_chain: { poolAddress: poolId, chain } },
|
|
255
|
+
});
|
|
256
|
+
if (cached) {
|
|
257
|
+
return {
|
|
258
|
+
address: poolId,
|
|
259
|
+
token0: cached.token0 || '',
|
|
260
|
+
token1: cached.token1 || '',
|
|
261
|
+
fee: cached.fee ?? undefined,
|
|
262
|
+
dex: 'uniswap_v4',
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// 2. Look for Initialize event on PoolManager
|
|
267
|
+
const contracts = KNOWN_CONTRACTS[chain];
|
|
268
|
+
if (!contracts?.v4PoolManager) return null;
|
|
269
|
+
|
|
270
|
+
try {
|
|
271
|
+
const rpcUrl = await getRpcUrl(chain);
|
|
272
|
+
const viemChain = VIEM_CHAINS[chain];
|
|
273
|
+
const client = createPublicClient({
|
|
274
|
+
chain: viemChain,
|
|
275
|
+
transport: http(rpcUrl),
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const logs = await client.getLogs({
|
|
279
|
+
address: contracts.v4PoolManager as Address,
|
|
280
|
+
event: {
|
|
281
|
+
type: 'event',
|
|
282
|
+
name: 'Initialize',
|
|
283
|
+
inputs: [
|
|
284
|
+
{ name: 'id', type: 'bytes32', indexed: true },
|
|
285
|
+
{ name: 'currency0', type: 'address', indexed: true },
|
|
286
|
+
{ name: 'currency1', type: 'address', indexed: true },
|
|
287
|
+
{ name: 'fee', type: 'uint24', indexed: false },
|
|
288
|
+
{ name: 'tickSpacing', type: 'int24', indexed: false },
|
|
289
|
+
{ name: 'hooks', type: 'address', indexed: false },
|
|
290
|
+
{ name: 'sqrtPriceX96', type: 'uint160', indexed: false },
|
|
291
|
+
{ name: 'tick', type: 'int24', indexed: false },
|
|
292
|
+
],
|
|
293
|
+
},
|
|
294
|
+
args: { id: poolId as `0x${string}` },
|
|
295
|
+
fromBlock: 0n,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
if (logs.length === 0) return null;
|
|
299
|
+
|
|
300
|
+
const initLog = logs[0];
|
|
301
|
+
const token0 = (initLog.args.currency0 as string).toLowerCase();
|
|
302
|
+
const token1 = (initLog.args.currency1 as string).toLowerCase();
|
|
303
|
+
const fee = Number(initLog.args.fee);
|
|
304
|
+
|
|
305
|
+
const pool: PoolInfo = { address: poolId, token0, token1, fee, dex: 'uniswap_v4' };
|
|
306
|
+
|
|
307
|
+
// Cache (immutable)
|
|
308
|
+
prisma.poolMetadata.create({
|
|
309
|
+
data: { poolAddress: poolId, chain, token0, token1, fee, dex: 'uniswap_v4' },
|
|
310
|
+
}).catch(() => {});
|
|
311
|
+
|
|
312
|
+
return pool;
|
|
313
|
+
} catch {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// --- Enrichment ---
|
|
319
|
+
|
|
320
|
+
function formatAmount(amount: bigint, decimals: number): string {
|
|
321
|
+
const divisor = 10n ** BigInt(decimals);
|
|
322
|
+
const whole = amount / divisor;
|
|
323
|
+
const remainder = amount % divisor;
|
|
324
|
+
|
|
325
|
+
if (remainder === 0n) return whole.toString();
|
|
326
|
+
|
|
327
|
+
const fracStr = remainder.toString().padStart(decimals, '0');
|
|
328
|
+
// Trim trailing zeros, keep up to 6 significant decimals
|
|
329
|
+
const trimmed = fracStr.replace(/0+$/, '').slice(0, 6);
|
|
330
|
+
return trimmed ? `${whole}.${trimmed}` : whole.toString();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
function shortenAddress(addr: string): string {
|
|
334
|
+
return `${addr.slice(0, 6)}...${addr.slice(-4)}`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
export async function enrichEvents(
|
|
338
|
+
events: DecodedEvent[],
|
|
339
|
+
address: string,
|
|
340
|
+
chain: string,
|
|
341
|
+
): Promise<EnrichedTransaction[]> {
|
|
342
|
+
const addr = address.toLowerCase();
|
|
343
|
+
const wethAddress = KNOWN_CONTRACTS[chain]?.weth?.toLowerCase();
|
|
344
|
+
|
|
345
|
+
// 1. Collect all unique token and pool addresses
|
|
346
|
+
const tokenAddresses = new Set<string>();
|
|
347
|
+
const poolAddresses: string[] = [];
|
|
348
|
+
const v4PoolIds: string[] = [];
|
|
349
|
+
|
|
350
|
+
for (const ev of events) {
|
|
351
|
+
if (ev.type === 'transfer' || ev.type === 'transfer_nft') {
|
|
352
|
+
tokenAddresses.add(ev.contractAddress);
|
|
353
|
+
} else if (ev.type === 'approval') {
|
|
354
|
+
tokenAddresses.add(ev.contractAddress);
|
|
355
|
+
} else if (ev.type === 'swap_v2') {
|
|
356
|
+
poolAddresses.push(ev.contractAddress);
|
|
357
|
+
} else if (ev.type === 'swap_v3') {
|
|
358
|
+
poolAddresses.push(ev.contractAddress);
|
|
359
|
+
} else if (ev.type === 'swap_v4') {
|
|
360
|
+
const poolId = ev.params.poolId as string;
|
|
361
|
+
if (poolId) v4PoolIds.push(poolId);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// 2. Batch-resolve metadata
|
|
366
|
+
const tokenMap = await resolveTokenMetadataBatch([...tokenAddresses], chain);
|
|
367
|
+
|
|
368
|
+
const poolMap = new Map<string, PoolInfo>();
|
|
369
|
+
for (const pa of poolAddresses) {
|
|
370
|
+
const info = await resolvePoolMetadata(pa, chain);
|
|
371
|
+
if (info) {
|
|
372
|
+
poolMap.set(pa, info);
|
|
373
|
+
// Also resolve pool tokens
|
|
374
|
+
if (info.token0) tokenAddresses.add(info.token0);
|
|
375
|
+
if (info.token1) tokenAddresses.add(info.token1);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
for (const pid of v4PoolIds) {
|
|
380
|
+
const info = await resolveV4PoolId(pid, chain);
|
|
381
|
+
if (info) {
|
|
382
|
+
poolMap.set(pid, info);
|
|
383
|
+
if (info.token0) tokenAddresses.add(info.token0);
|
|
384
|
+
if (info.token1) tokenAddresses.add(info.token1);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Resolve newly-discovered pool tokens
|
|
389
|
+
const additionalTokens = [...tokenAddresses].filter(a => !tokenMap.has(a));
|
|
390
|
+
if (additionalTokens.length > 0) {
|
|
391
|
+
const extra = await resolveTokenMetadataBatch(additionalTokens, chain);
|
|
392
|
+
for (const [k, v] of extra) tokenMap.set(k, v);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Helper to get symbol
|
|
396
|
+
const getSymbol = (addr: string): string => {
|
|
397
|
+
if (addr === wethAddress) return 'WETH';
|
|
398
|
+
return tokenMap.get(addr)?.symbol || shortenAddress(addr);
|
|
399
|
+
};
|
|
400
|
+
const getDecimals = (addr: string): number => {
|
|
401
|
+
return tokenMap.get(addr)?.decimals || 18;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// 3. Build enriched transactions
|
|
405
|
+
const result: EnrichedTransaction[] = [];
|
|
406
|
+
|
|
407
|
+
for (const ev of events) {
|
|
408
|
+
try {
|
|
409
|
+
switch (ev.type) {
|
|
410
|
+
case 'transfer': {
|
|
411
|
+
const from = ev.params.from as string;
|
|
412
|
+
const to = ev.params.to as string;
|
|
413
|
+
const amount = ev.params.amount as bigint;
|
|
414
|
+
const decimals = getDecimals(ev.contractAddress);
|
|
415
|
+
const symbol = getSymbol(ev.contractAddress);
|
|
416
|
+
const formatted = formatAmount(amount, decimals);
|
|
417
|
+
const isIncoming = to === addr;
|
|
418
|
+
const direction = isIncoming ? 'in' : 'out';
|
|
419
|
+
const counterparty = isIncoming ? from : to;
|
|
420
|
+
const summary = isIncoming
|
|
421
|
+
? `Received ${formatted} ${symbol} from ${shortenAddress(counterparty)}`
|
|
422
|
+
: `Sent ${formatted} ${symbol} to ${shortenAddress(counterparty)}`;
|
|
423
|
+
|
|
424
|
+
result.push({
|
|
425
|
+
type: 'transfer',
|
|
426
|
+
summary,
|
|
427
|
+
txHash: ev.txHash,
|
|
428
|
+
blockNumber: ev.blockNumber.toString(),
|
|
429
|
+
details: { from, to, amount: formatted, symbol, direction, tokenAddress: ev.contractAddress },
|
|
430
|
+
});
|
|
431
|
+
break;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
case 'transfer_nft': {
|
|
435
|
+
const from = ev.params.from as string;
|
|
436
|
+
const to = ev.params.to as string;
|
|
437
|
+
const tokenId = ev.params.tokenId as bigint;
|
|
438
|
+
const isIncoming = to === addr;
|
|
439
|
+
const counterparty = isIncoming ? from : to;
|
|
440
|
+
const summary = isIncoming
|
|
441
|
+
? `Received NFT #${tokenId} from ${shortenAddress(counterparty)}`
|
|
442
|
+
: `Sent NFT #${tokenId} to ${shortenAddress(counterparty)}`;
|
|
443
|
+
|
|
444
|
+
result.push({
|
|
445
|
+
type: 'transfer_nft',
|
|
446
|
+
summary,
|
|
447
|
+
txHash: ev.txHash,
|
|
448
|
+
blockNumber: ev.blockNumber.toString(),
|
|
449
|
+
details: { from, to, tokenId: tokenId.toString(), direction: isIncoming ? 'in' : 'out', contractAddress: ev.contractAddress },
|
|
450
|
+
});
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
case 'approval': {
|
|
455
|
+
const spender = ev.params.spender as string;
|
|
456
|
+
const symbol = getSymbol(ev.contractAddress);
|
|
457
|
+
const summary = `Approved ${shortenAddress(spender)} to spend ${symbol}`;
|
|
458
|
+
result.push({
|
|
459
|
+
type: 'approval',
|
|
460
|
+
summary,
|
|
461
|
+
txHash: ev.txHash,
|
|
462
|
+
blockNumber: ev.blockNumber.toString(),
|
|
463
|
+
details: { owner: ev.params.owner, spender, symbol, tokenAddress: ev.contractAddress },
|
|
464
|
+
});
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
case 'approval_nft': {
|
|
469
|
+
const spender = ev.params.spender as string;
|
|
470
|
+
const tokenId = ev.params.tokenId as bigint;
|
|
471
|
+
const summary = `Approved ${shortenAddress(spender)} for NFT #${tokenId}`;
|
|
472
|
+
result.push({
|
|
473
|
+
type: 'approval_nft',
|
|
474
|
+
summary,
|
|
475
|
+
txHash: ev.txHash,
|
|
476
|
+
blockNumber: ev.blockNumber.toString(),
|
|
477
|
+
details: { owner: ev.params.owner, spender, tokenId: tokenId.toString(), contractAddress: ev.contractAddress },
|
|
478
|
+
});
|
|
479
|
+
break;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
case 'swap_v2': {
|
|
483
|
+
const pool = poolMap.get(ev.contractAddress);
|
|
484
|
+
if (!pool) {
|
|
485
|
+
result.push({
|
|
486
|
+
type: 'swap_v2',
|
|
487
|
+
summary: `Swapped on ${shortenAddress(ev.contractAddress)}`,
|
|
488
|
+
txHash: ev.txHash,
|
|
489
|
+
blockNumber: ev.blockNumber.toString(),
|
|
490
|
+
protocol: 'uniswap_v2',
|
|
491
|
+
details: safeParams(ev.params),
|
|
492
|
+
});
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const a0In = ev.params.amount0In as bigint;
|
|
497
|
+
const a1In = ev.params.amount1In as bigint;
|
|
498
|
+
const a0Out = ev.params.amount0Out as bigint;
|
|
499
|
+
const a1Out = ev.params.amount1Out as bigint;
|
|
500
|
+
|
|
501
|
+
const tokenIn = a0In > 0n ? pool.token0 : pool.token1;
|
|
502
|
+
const tokenOut = a0Out > 0n ? pool.token0 : pool.token1;
|
|
503
|
+
const amountIn = a0In > 0n ? a0In : a1In;
|
|
504
|
+
const amountOut = a0Out > 0n ? a0Out : a1Out;
|
|
505
|
+
|
|
506
|
+
const symbolIn = getSymbol(tokenIn);
|
|
507
|
+
const symbolOut = getSymbol(tokenOut);
|
|
508
|
+
const formattedIn = formatAmount(amountIn, getDecimals(tokenIn));
|
|
509
|
+
const formattedOut = formatAmount(amountOut, getDecimals(tokenOut));
|
|
510
|
+
|
|
511
|
+
result.push({
|
|
512
|
+
type: 'swap',
|
|
513
|
+
summary: `Swapped ${formattedIn} ${symbolIn} for ${formattedOut} ${symbolOut}`,
|
|
514
|
+
txHash: ev.txHash,
|
|
515
|
+
blockNumber: ev.blockNumber.toString(),
|
|
516
|
+
protocol: 'uniswap_v2',
|
|
517
|
+
details: { tokenIn, tokenOut, amountIn: formattedIn, amountOut: formattedOut, symbolIn, symbolOut, pool: ev.contractAddress },
|
|
518
|
+
});
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
case 'swap_v3': {
|
|
523
|
+
const pool = poolMap.get(ev.contractAddress);
|
|
524
|
+
if (!pool) {
|
|
525
|
+
result.push({
|
|
526
|
+
type: 'swap_v3',
|
|
527
|
+
summary: `Swapped on ${shortenAddress(ev.contractAddress)}`,
|
|
528
|
+
txHash: ev.txHash,
|
|
529
|
+
blockNumber: ev.blockNumber.toString(),
|
|
530
|
+
protocol: 'uniswap_v3',
|
|
531
|
+
details: safeParams(ev.params),
|
|
532
|
+
});
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// V3 uses signed amounts: positive = sent to pool, negative = received from pool
|
|
537
|
+
const amount0 = ev.params.amount0 as bigint;
|
|
538
|
+
const amount1 = ev.params.amount1 as bigint;
|
|
539
|
+
|
|
540
|
+
const tokenIn = amount0 > 0n ? pool.token0 : pool.token1;
|
|
541
|
+
const tokenOut = amount0 > 0n ? pool.token1 : pool.token0;
|
|
542
|
+
const amountIn = amount0 > 0n ? amount0 : amount1;
|
|
543
|
+
const amountOut = amount0 > 0n ? -amount1 : -amount0;
|
|
544
|
+
|
|
545
|
+
const symbolIn = getSymbol(tokenIn);
|
|
546
|
+
const symbolOut = getSymbol(tokenOut);
|
|
547
|
+
const formattedIn = formatAmount(amountIn, getDecimals(tokenIn));
|
|
548
|
+
const formattedOut = formatAmount(amountOut, getDecimals(tokenOut));
|
|
549
|
+
|
|
550
|
+
result.push({
|
|
551
|
+
type: 'swap',
|
|
552
|
+
summary: `Swapped ${formattedIn} ${symbolIn} for ${formattedOut} ${symbolOut}`,
|
|
553
|
+
txHash: ev.txHash,
|
|
554
|
+
blockNumber: ev.blockNumber.toString(),
|
|
555
|
+
protocol: 'uniswap_v3',
|
|
556
|
+
details: { tokenIn, tokenOut, amountIn: formattedIn, amountOut: formattedOut, symbolIn, symbolOut, pool: ev.contractAddress },
|
|
557
|
+
});
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
case 'swap_v4': {
|
|
562
|
+
const poolId = ev.params.poolId as string;
|
|
563
|
+
const pool = poolMap.get(poolId);
|
|
564
|
+
if (!pool) {
|
|
565
|
+
// Serialize params — raw BigInt values (amount0, sqrtPriceX96, etc.) can't be JSON-serialized
|
|
566
|
+
const safeParams: Record<string, string> = {};
|
|
567
|
+
for (const [k, v] of Object.entries(ev.params)) {
|
|
568
|
+
safeParams[k] = typeof v === 'bigint' ? v.toString() : String(v);
|
|
569
|
+
}
|
|
570
|
+
result.push({
|
|
571
|
+
type: 'swap_v4',
|
|
572
|
+
summary: `Swapped on V4 pool`,
|
|
573
|
+
txHash: ev.txHash,
|
|
574
|
+
blockNumber: ev.blockNumber.toString(),
|
|
575
|
+
protocol: 'uniswap_v4',
|
|
576
|
+
details: safeParams,
|
|
577
|
+
});
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const amount0 = ev.params.amount0 as bigint;
|
|
582
|
+
const amount1 = ev.params.amount1 as bigint;
|
|
583
|
+
|
|
584
|
+
const tokenIn = amount0 > 0n ? pool.token0 : pool.token1;
|
|
585
|
+
const tokenOut = amount0 > 0n ? pool.token1 : pool.token0;
|
|
586
|
+
const amountIn = amount0 > 0n ? amount0 : amount1;
|
|
587
|
+
const amountOut = amount0 > 0n ? -amount1 : -amount0;
|
|
588
|
+
|
|
589
|
+
const symbolIn = getSymbol(tokenIn);
|
|
590
|
+
const symbolOut = getSymbol(tokenOut);
|
|
591
|
+
const formattedIn = formatAmount(amountIn, getDecimals(tokenIn));
|
|
592
|
+
const formattedOut = formatAmount(amountOut, getDecimals(tokenOut));
|
|
593
|
+
|
|
594
|
+
result.push({
|
|
595
|
+
type: 'swap',
|
|
596
|
+
summary: `Swapped ${formattedIn} ${symbolIn} for ${formattedOut} ${symbolOut}`,
|
|
597
|
+
txHash: ev.txHash,
|
|
598
|
+
blockNumber: ev.blockNumber.toString(),
|
|
599
|
+
protocol: 'uniswap_v4',
|
|
600
|
+
details: { tokenIn, tokenOut, amountIn: formattedIn, amountOut: formattedOut, symbolIn, symbolOut, poolId },
|
|
601
|
+
});
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
case 'weth_deposit': {
|
|
606
|
+
const wad = ev.params.wad as bigint;
|
|
607
|
+
const formatted = formatAmount(wad, 18);
|
|
608
|
+
result.push({
|
|
609
|
+
type: 'wrap',
|
|
610
|
+
summary: `Wrapped ${formatted} ETH`,
|
|
611
|
+
txHash: ev.txHash,
|
|
612
|
+
blockNumber: ev.blockNumber.toString(),
|
|
613
|
+
details: { amount: formatted, direction: 'wrap' },
|
|
614
|
+
});
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
case 'weth_withdrawal': {
|
|
619
|
+
const wad = ev.params.wad as bigint;
|
|
620
|
+
const formatted = formatAmount(wad, 18);
|
|
621
|
+
result.push({
|
|
622
|
+
type: 'unwrap',
|
|
623
|
+
summary: `Unwrapped ${formatted} WETH`,
|
|
624
|
+
txHash: ev.txHash,
|
|
625
|
+
blockNumber: ev.blockNumber.toString(),
|
|
626
|
+
details: { amount: formatted, direction: 'unwrap' },
|
|
627
|
+
});
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
default:
|
|
632
|
+
result.push({
|
|
633
|
+
type: ev.type,
|
|
634
|
+
summary: `Unknown event on ${shortenAddress(ev.contractAddress)}`,
|
|
635
|
+
txHash: ev.txHash,
|
|
636
|
+
blockNumber: ev.blockNumber.toString(),
|
|
637
|
+
details: safeParams(ev.params),
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
} catch {
|
|
641
|
+
result.push({
|
|
642
|
+
type: ev.type,
|
|
643
|
+
summary: `Event on ${shortenAddress(ev.contractAddress)}`,
|
|
644
|
+
txHash: ev.txHash,
|
|
645
|
+
blockNumber: ev.blockNumber.toString(),
|
|
646
|
+
details: safeParams(ev.params),
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
return result;
|
|
652
|
+
}
|