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,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction History — Public API
|
|
3
|
+
* ==================================
|
|
4
|
+
* Main entry point for fetching and decoding on-chain events.
|
|
5
|
+
* Reusable by route handlers and future cron jobs (FEAT-011 Phase 5).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createPublicClient, http, numberToHex } from 'viem';
|
|
9
|
+
import { base, mainnet } from 'viem/chains';
|
|
10
|
+
import { getRpcUrl } from '../config';
|
|
11
|
+
import { decodeLogs, type RawLog, type DecodedEvent } from './decoder';
|
|
12
|
+
import { enrichEvents, type EnrichedTransaction } from './enricher';
|
|
13
|
+
import { ALL_TOPIC0S, EVENT_SIGNATURES, KNOWN_CONTRACTS } from './signatures';
|
|
14
|
+
|
|
15
|
+
// Re-export everything for consumers
|
|
16
|
+
export { decodeLogs, type DecodedEvent, type RawLog } from './decoder';
|
|
17
|
+
export { enrichEvents, resolveTokenMetadataBatch, resolvePoolMetadata, resolveV4PoolId, type EnrichedTransaction, type TokenInfo, type PoolInfo } from './enricher';
|
|
18
|
+
export { EVENT_SIGNATURES, KNOWN_CONTRACTS, TOPIC_TO_EVENT, ALL_TOPIC0S } from './signatures';
|
|
19
|
+
|
|
20
|
+
// Map chain names to viem chain objects
|
|
21
|
+
const VIEM_CHAINS: Record<string, typeof base> = {
|
|
22
|
+
base,
|
|
23
|
+
ethereum: mainnet,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Chunk size for getLogs — fits Alchemy free tier (max 10 blocks inclusive per call)
|
|
27
|
+
const CHUNK_SIZE = 9n;
|
|
28
|
+
// Default scan range: ~2000 blocks (~1 hour on Base at 2s/block)
|
|
29
|
+
const DEFAULT_SCAN_RANGE = 2000n;
|
|
30
|
+
// Max chunks to scan before giving up (2000 / 10 = 200 chunks × 3 calls = 600 RPC calls max)
|
|
31
|
+
const MAX_CHUNKS = 200;
|
|
32
|
+
|
|
33
|
+
export interface FetchEventsOptions {
|
|
34
|
+
address: string;
|
|
35
|
+
chain?: string;
|
|
36
|
+
fromBlock?: bigint;
|
|
37
|
+
toBlock?: bigint;
|
|
38
|
+
limit?: number;
|
|
39
|
+
/** Filter by event type names (e.g. ['transfer', 'swap_v2']) */
|
|
40
|
+
types?: string[];
|
|
41
|
+
/** When set, query logs emitted BY this token contract (address field) instead of topic-based wallet matching */
|
|
42
|
+
tokenAddress?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface FetchEventsResult {
|
|
46
|
+
transactions: EnrichedTransaction[];
|
|
47
|
+
blockRange: { from: string; to: string };
|
|
48
|
+
total: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Pad a 20-byte address to 32-byte topic */
|
|
52
|
+
function padAddress(addr: string): `0x${string}` {
|
|
53
|
+
return ('0x' + addr.toLowerCase().replace('0x', '').padStart(64, '0')) as `0x${string}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Raw eth_getLogs call — bypasses viem's getLogs which silently ignores the `topics` parameter.
|
|
58
|
+
* viem's getLogs only supports ABI-based event/events params for topic filtering;
|
|
59
|
+
* raw topics arrays are discarded. This function calls eth_getLogs directly via client.request()
|
|
60
|
+
* so we have full control over topic filtering.
|
|
61
|
+
*/
|
|
62
|
+
async function rawGetLogs(
|
|
63
|
+
client: ReturnType<typeof createPublicClient>,
|
|
64
|
+
params: {
|
|
65
|
+
address?: string;
|
|
66
|
+
topics?: (string | string[] | null)[];
|
|
67
|
+
fromBlock: bigint;
|
|
68
|
+
toBlock: bigint;
|
|
69
|
+
},
|
|
70
|
+
): Promise<RawLog[]> {
|
|
71
|
+
const rpcParams: Record<string, unknown> = {
|
|
72
|
+
fromBlock: numberToHex(params.fromBlock),
|
|
73
|
+
toBlock: numberToHex(params.toBlock),
|
|
74
|
+
};
|
|
75
|
+
if (params.address) rpcParams.address = params.address;
|
|
76
|
+
if (params.topics) rpcParams.topics = params.topics;
|
|
77
|
+
|
|
78
|
+
const logs: any[] = await (client as any).request({
|
|
79
|
+
method: 'eth_getLogs',
|
|
80
|
+
params: [rpcParams],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return logs.map(log => ({
|
|
84
|
+
address: log.address,
|
|
85
|
+
topics: log.topics as string[],
|
|
86
|
+
data: log.data,
|
|
87
|
+
transactionHash: log.transactionHash,
|
|
88
|
+
logIndex: typeof log.logIndex === 'number' ? log.logIndex : parseInt(log.logIndex, 16),
|
|
89
|
+
blockNumber: typeof log.blockNumber === 'bigint' ? log.blockNumber : BigInt(log.blockNumber),
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Map type names to topic0 hashes for filtering */
|
|
94
|
+
function getTopicsForTypes(types?: string[]): `0x${string}`[] {
|
|
95
|
+
if (!types || types.length === 0) return ALL_TOPIC0S as unknown as `0x${string}`[];
|
|
96
|
+
|
|
97
|
+
const topicMap: Record<string, string[]> = {
|
|
98
|
+
transfer: [EVENT_SIGNATURES.TRANSFER],
|
|
99
|
+
approval: [EVENT_SIGNATURES.APPROVAL],
|
|
100
|
+
swap: [EVENT_SIGNATURES.SWAP_V2, EVENT_SIGNATURES.SWAP_V3, EVENT_SIGNATURES.SWAP_V4],
|
|
101
|
+
swap_v2: [EVENT_SIGNATURES.SWAP_V2],
|
|
102
|
+
swap_v3: [EVENT_SIGNATURES.SWAP_V3],
|
|
103
|
+
swap_v4: [EVENT_SIGNATURES.SWAP_V4],
|
|
104
|
+
wrap: [EVENT_SIGNATURES.WETH_DEPOSIT, EVENT_SIGNATURES.WETH_WITHDRAWAL],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const topics = new Set<string>();
|
|
108
|
+
for (const t of types) {
|
|
109
|
+
const mapped = topicMap[t.toLowerCase()];
|
|
110
|
+
if (mapped) mapped.forEach(h => topics.add(h));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// V4 Swap events don't contain the wallet address in topics, so they're
|
|
114
|
+
// discovered via correlation with orphan Transfer events. When swap types
|
|
115
|
+
// are requested, also fetch Transfers so V4 correlation has material.
|
|
116
|
+
const hasSwapType = types.some(t => {
|
|
117
|
+
const lower = t.toLowerCase();
|
|
118
|
+
return lower === 'swap' || lower === 'swap_v4';
|
|
119
|
+
});
|
|
120
|
+
if (hasSwapType) {
|
|
121
|
+
topics.add(EVENT_SIGNATURES.TRANSFER);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return topics.size > 0
|
|
125
|
+
? ([...topics] as `0x${string}`[])
|
|
126
|
+
: (ALL_TOPIC0S as unknown as `0x${string}`[]);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Fetch logs for a single chunk (3 parallel calls: sender, recipient, WETH) */
|
|
130
|
+
async function fetchLogsForChunk(
|
|
131
|
+
client: ReturnType<typeof createPublicClient>,
|
|
132
|
+
topicFilter: `0x${string}`[],
|
|
133
|
+
paddedAddr: `0x${string}`,
|
|
134
|
+
chunkFrom: bigint,
|
|
135
|
+
chunkTo: bigint,
|
|
136
|
+
): Promise<RawLog[]> {
|
|
137
|
+
const wethTopics = [
|
|
138
|
+
EVENT_SIGNATURES.WETH_DEPOSIT,
|
|
139
|
+
EVENT_SIGNATURES.WETH_WITHDRAWAL,
|
|
140
|
+
] as `0x${string}`[];
|
|
141
|
+
|
|
142
|
+
const [logs1, logs2, logsWeth] = await Promise.all([
|
|
143
|
+
rawGetLogs(client, { topics: [topicFilter, paddedAddr], fromBlock: chunkFrom, toBlock: chunkTo }),
|
|
144
|
+
rawGetLogs(client, { topics: [topicFilter, null, paddedAddr], fromBlock: chunkFrom, toBlock: chunkTo }),
|
|
145
|
+
rawGetLogs(client, { topics: [wethTopics, paddedAddr], fromBlock: chunkFrom, toBlock: chunkTo }),
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
const seen = new Set<string>();
|
|
149
|
+
const result: RawLog[] = [];
|
|
150
|
+
|
|
151
|
+
for (const logSet of [logs1, logs2, logsWeth]) {
|
|
152
|
+
for (const log of logSet) {
|
|
153
|
+
const key = `${log.transactionHash}:${log.logIndex}`;
|
|
154
|
+
if (seen.has(key)) continue;
|
|
155
|
+
seen.add(key);
|
|
156
|
+
result.push(log);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Fetch logs emitted BY a specific token contract (uses viem's address filter instead of topic positions) */
|
|
164
|
+
async function fetchLogsForToken(
|
|
165
|
+
client: ReturnType<typeof createPublicClient>,
|
|
166
|
+
topicFilter: `0x${string}`[],
|
|
167
|
+
tokenAddress: string,
|
|
168
|
+
chunkFrom: bigint,
|
|
169
|
+
chunkTo: bigint,
|
|
170
|
+
): Promise<RawLog[]> {
|
|
171
|
+
return rawGetLogs(client, {
|
|
172
|
+
address: tokenAddress.toLowerCase(),
|
|
173
|
+
topics: [topicFilter],
|
|
174
|
+
fromBlock: chunkFrom,
|
|
175
|
+
toBlock: chunkTo,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Post-processing: find V4 Swap events for wallet transactions.
|
|
181
|
+
* V4 Swap topics don't contain the wallet address (topic1 = poolId, topic2 = Universal Router),
|
|
182
|
+
* so we correlate via shared txHash with the wallet's Transfer events.
|
|
183
|
+
*/
|
|
184
|
+
async function fetchV4SwapsForTransactions(
|
|
185
|
+
client: ReturnType<typeof createPublicClient>,
|
|
186
|
+
decoded: DecodedEvent[],
|
|
187
|
+
address: string,
|
|
188
|
+
chain: string,
|
|
189
|
+
): Promise<RawLog[]> {
|
|
190
|
+
const v4PoolManager = KNOWN_CONTRACTS[chain]?.v4PoolManager;
|
|
191
|
+
if (!v4PoolManager) return [];
|
|
192
|
+
|
|
193
|
+
const addr = address.toLowerCase();
|
|
194
|
+
|
|
195
|
+
// Collect txHashes that already have a swap event
|
|
196
|
+
const txHashesWithSwap = new Set<string>();
|
|
197
|
+
for (const ev of decoded) {
|
|
198
|
+
if (ev.type === 'swap_v2' || ev.type === 'swap_v3' || ev.type === 'swap_v4') {
|
|
199
|
+
txHashesWithSwap.add(ev.txHash);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Find "orphan" transfers: Transfer events involving the wallet that have no swap in the same tx
|
|
204
|
+
const orphanBlocks = new Map<string, bigint>(); // txHash -> blockNumber
|
|
205
|
+
for (const ev of decoded) {
|
|
206
|
+
if (ev.type !== 'transfer') continue;
|
|
207
|
+
if (txHashesWithSwap.has(ev.txHash)) continue;
|
|
208
|
+
const from = (ev.params.from as string).toLowerCase();
|
|
209
|
+
const to = (ev.params.to as string).toLowerCase();
|
|
210
|
+
if (from === addr || to === addr) {
|
|
211
|
+
orphanBlocks.set(ev.txHash, ev.blockNumber);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (orphanBlocks.size === 0) return [];
|
|
216
|
+
|
|
217
|
+
// Get unique block numbers to query
|
|
218
|
+
const uniqueBlocks = [...new Set(orphanBlocks.values())];
|
|
219
|
+
const orphanTxHashes = new Set(orphanBlocks.keys());
|
|
220
|
+
|
|
221
|
+
// Query PoolManager for V4 Swap events at those blocks (batched 5 at a time)
|
|
222
|
+
const v4SwapTopic = EVENT_SIGNATURES.SWAP_V4 as `0x${string}`;
|
|
223
|
+
const allV4Logs: RawLog[] = [];
|
|
224
|
+
|
|
225
|
+
for (let i = 0; i < uniqueBlocks.length; i += 5) {
|
|
226
|
+
const batch = uniqueBlocks.slice(i, i + 5);
|
|
227
|
+
const results = await Promise.all(
|
|
228
|
+
batch.map(blockNum =>
|
|
229
|
+
rawGetLogs(client, {
|
|
230
|
+
address: v4PoolManager,
|
|
231
|
+
topics: [v4SwapTopic],
|
|
232
|
+
fromBlock: blockNum,
|
|
233
|
+
toBlock: blockNum,
|
|
234
|
+
}).catch(() => []),
|
|
235
|
+
),
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
for (const logs of results) {
|
|
239
|
+
for (const log of logs) {
|
|
240
|
+
// Only include V4 Swap logs that share a txHash with an orphan transfer
|
|
241
|
+
if (orphanTxHashes.has(log.transactionHash)) {
|
|
242
|
+
allV4Logs.push({
|
|
243
|
+
address: log.address,
|
|
244
|
+
topics: log.topics as string[],
|
|
245
|
+
data: log.data,
|
|
246
|
+
transactionHash: log.transactionHash,
|
|
247
|
+
logIndex: log.logIndex,
|
|
248
|
+
blockNumber: log.blockNumber,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return allV4Logs;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Fetch, decode, and enrich on-chain events for an address.
|
|
260
|
+
* Scans backwards in CHUNK_SIZE-block chunks to stay within RPC limits.
|
|
261
|
+
*/
|
|
262
|
+
export async function fetchAndDecodeEvents(opts: FetchEventsOptions): Promise<FetchEventsResult> {
|
|
263
|
+
const {
|
|
264
|
+
address,
|
|
265
|
+
chain = 'base',
|
|
266
|
+
limit = 20,
|
|
267
|
+
types,
|
|
268
|
+
tokenAddress,
|
|
269
|
+
} = opts;
|
|
270
|
+
|
|
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 latestBlock = await client.getBlockNumber();
|
|
279
|
+
const toBlock = opts.toBlock ?? latestBlock;
|
|
280
|
+
const fromBlock = opts.fromBlock ?? (toBlock > DEFAULT_SCAN_RANGE ? toBlock - DEFAULT_SCAN_RANGE : 0n);
|
|
281
|
+
|
|
282
|
+
const paddedAddr = padAddress(address);
|
|
283
|
+
const topicFilter = getTopicsForTypes(types);
|
|
284
|
+
|
|
285
|
+
// Scan backwards in chunks, collecting logs until we have enough
|
|
286
|
+
const allLogs: RawLog[] = [];
|
|
287
|
+
const seenGlobal = new Set<string>();
|
|
288
|
+
let chunksScanned = 0;
|
|
289
|
+
let chunkEnd = toBlock;
|
|
290
|
+
|
|
291
|
+
while (chunkEnd > fromBlock && chunksScanned < MAX_CHUNKS) {
|
|
292
|
+
const chunkStart = chunkEnd - CHUNK_SIZE > fromBlock ? chunkEnd - CHUNK_SIZE : fromBlock;
|
|
293
|
+
|
|
294
|
+
// Token mode: query logs emitted BY the token contract
|
|
295
|
+
// Wallet mode: query logs with wallet address in topic positions
|
|
296
|
+
const chunkLogs = tokenAddress
|
|
297
|
+
? await fetchLogsForToken(client, topicFilter, tokenAddress, chunkStart, chunkEnd)
|
|
298
|
+
: await fetchLogsForChunk(client, topicFilter, paddedAddr, chunkStart, chunkEnd);
|
|
299
|
+
|
|
300
|
+
for (const log of chunkLogs) {
|
|
301
|
+
const key = `${log.transactionHash}:${log.logIndex}`;
|
|
302
|
+
if (!seenGlobal.has(key)) {
|
|
303
|
+
seenGlobal.add(key);
|
|
304
|
+
allLogs.push(log);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
chunksScanned++;
|
|
309
|
+
chunkEnd = chunkStart > 0n ? chunkStart - 1n : 0n;
|
|
310
|
+
|
|
311
|
+
// Early exit once we have enough logs
|
|
312
|
+
if (allLogs.length >= limit) break;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Sort by block desc, then logIndex desc
|
|
316
|
+
allLogs.sort((a, b) => {
|
|
317
|
+
const blockCmp = Number(b.blockNumber - a.blockNumber);
|
|
318
|
+
if (blockCmp !== 0) return blockCmp;
|
|
319
|
+
return b.logIndex - a.logIndex;
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const total = allLogs.length;
|
|
323
|
+
const limited = allLogs.slice(0, limit);
|
|
324
|
+
|
|
325
|
+
// Decode
|
|
326
|
+
let decoded = decodeLogs(limited);
|
|
327
|
+
|
|
328
|
+
// V4 swap correlation: for wallet-level queries (not token queries),
|
|
329
|
+
// find V4 Swap events that share a txHash with orphan transfers
|
|
330
|
+
if (!tokenAddress && decoded.length > 0) {
|
|
331
|
+
const v4Logs = await fetchV4SwapsForTransactions(client, decoded, address, chain);
|
|
332
|
+
if (v4Logs.length > 0) {
|
|
333
|
+
const v4Decoded = decodeLogs(v4Logs);
|
|
334
|
+
decoded = [...decoded, ...v4Decoded];
|
|
335
|
+
// Re-sort after merging
|
|
336
|
+
decoded.sort((a, b) => {
|
|
337
|
+
const blockCmp = Number(b.blockNumber - a.blockNumber);
|
|
338
|
+
if (blockCmp !== 0) return blockCmp;
|
|
339
|
+
return b.logIndex - a.logIndex;
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Enrich
|
|
345
|
+
const transactions = await enrichEvents(decoded, address, chain);
|
|
346
|
+
|
|
347
|
+
// Fetch block timestamps (batch unique blocks, max 10 at a time)
|
|
348
|
+
const allBlockNumbers = decoded.map(d => d.blockNumber);
|
|
349
|
+
const uniqueBlocks = [...new Set(allBlockNumbers)];
|
|
350
|
+
const timestampMap = new Map<string, number>();
|
|
351
|
+
|
|
352
|
+
for (let i = 0; i < uniqueBlocks.length; i += 10) {
|
|
353
|
+
const batch = uniqueBlocks.slice(i, i + 10);
|
|
354
|
+
const blocks = await Promise.all(
|
|
355
|
+
batch.map(bn => client.getBlock({ blockNumber: bn }).catch(() => null)),
|
|
356
|
+
);
|
|
357
|
+
for (const block of blocks) {
|
|
358
|
+
if (block) {
|
|
359
|
+
timestampMap.set(block.number.toString(), Number(block.timestamp));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Attach timestamps
|
|
365
|
+
for (const tx of transactions) {
|
|
366
|
+
const ts = timestampMap.get(tx.blockNumber);
|
|
367
|
+
if (ts) tx.timestamp = ts;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Post-filter by requested types (the topic filter may include extra event types
|
|
371
|
+
// for V4 correlation — e.g. Transfer events fetched alongside swap types)
|
|
372
|
+
let filtered = transactions;
|
|
373
|
+
if (types && types.length > 0) {
|
|
374
|
+
const requested = new Set(types.map(t => t.toLowerCase()));
|
|
375
|
+
filtered = transactions.filter(tx => {
|
|
376
|
+
const t = tx.type;
|
|
377
|
+
if (requested.has(t)) return true;
|
|
378
|
+
// 'swap' matches all enriched swap variants (swap, swap_v2, swap_v3, swap_v4)
|
|
379
|
+
if (requested.has('swap') && (t === 'swap' || t === 'swap_v2' || t === 'swap_v3' || t === 'swap_v4')) return true;
|
|
380
|
+
// 'wrap' matches both wrap and unwrap
|
|
381
|
+
if (requested.has('wrap') && (t === 'wrap' || t === 'unwrap')) return true;
|
|
382
|
+
return false;
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
return {
|
|
387
|
+
transactions: filtered,
|
|
388
|
+
blockRange: { from: fromBlock.toString(), to: toBlock.toString() },
|
|
389
|
+
total,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Signature Constants
|
|
3
|
+
* =========================
|
|
4
|
+
* Pre-computed keccak256 topic0 hashes for common EVM events.
|
|
5
|
+
* Used by the log decoder and on-chain transaction history fetcher.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// keccak256 of event signatures → topic0 hashes
|
|
9
|
+
export const EVENT_SIGNATURES = {
|
|
10
|
+
// ERC-20 / ERC-721
|
|
11
|
+
TRANSFER: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', // Transfer(address,address,uint256)
|
|
12
|
+
APPROVAL: '0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925', // Approval(address,address,uint256)
|
|
13
|
+
|
|
14
|
+
// Uniswap V2
|
|
15
|
+
SWAP_V2: '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822', // Swap(address,uint256,uint256,uint256,uint256,address)
|
|
16
|
+
|
|
17
|
+
// Uniswap V3
|
|
18
|
+
SWAP_V3: '0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67', // Swap(address,address,int256,int256,uint160,uint128,int24)
|
|
19
|
+
|
|
20
|
+
// Uniswap V4
|
|
21
|
+
SWAP_V4: '0x40e9cecb9f5f1f1c5b9c97dec2917b7ee92e57ba5563708daca94dd84ad7112f', // Swap(bytes32,address,int128,int128,uint160,uint128,int24,uint24)
|
|
22
|
+
|
|
23
|
+
// Uniswap V4 Initialize
|
|
24
|
+
INITIALIZE_V4: '0x803151a295203f64f7e2ca2db584660e99eaf67eca6f05af1bf0707e7d38f2cf', // Initialize(bytes32,address,address,uint24,int24,address,uint160,int24)
|
|
25
|
+
|
|
26
|
+
// WETH
|
|
27
|
+
WETH_DEPOSIT: '0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c', // Deposit(address,uint256)
|
|
28
|
+
WETH_WITHDRAWAL: '0x7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65', // Withdrawal(address,uint256)
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
// All topic0 values for getLogs queries
|
|
32
|
+
export const ALL_TOPIC0S = Object.values(EVENT_SIGNATURES);
|
|
33
|
+
|
|
34
|
+
// Known contract addresses per chain
|
|
35
|
+
export const KNOWN_CONTRACTS: Record<string, {
|
|
36
|
+
weth: string;
|
|
37
|
+
v2Factory?: string;
|
|
38
|
+
v3Factory?: string;
|
|
39
|
+
v4PoolManager?: string;
|
|
40
|
+
}> = {
|
|
41
|
+
base: {
|
|
42
|
+
weth: '0x4200000000000000000000000000000000000006',
|
|
43
|
+
v2Factory: '0x8909Dc15e40173Ff4699343b6eB8132c65e18eC6',
|
|
44
|
+
v3Factory: '0x33128a8fC17869897dcE68Ed026d694621f6FDfD',
|
|
45
|
+
v4PoolManager: '0x498581fF718922c3f8e6A244956aF099B2652b2b',
|
|
46
|
+
},
|
|
47
|
+
ethereum: {
|
|
48
|
+
weth: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
|
|
49
|
+
v2Factory: '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f',
|
|
50
|
+
v3Factory: '0x1F98431c8aD98523631AE4a59f267346ea31F984',
|
|
51
|
+
v4PoolManager: '0x000000000004444c5dc75cB358380D2e3dE08A90',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// Reverse lookup: topic0 → event key name
|
|
56
|
+
export const TOPIC_TO_EVENT: Record<string, keyof typeof EVENT_SIGNATURES> = {};
|
|
57
|
+
for (const [key, hash] of Object.entries(EVENT_SIGNATURES)) {
|
|
58
|
+
TOPIC_TO_EVENT[hash] = key as keyof typeof EVENT_SIGNATURES;
|
|
59
|
+
}
|