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,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized System Defaults
|
|
3
|
+
* ===========================
|
|
4
|
+
* Key-value store for configurable limits, permissions, TTLs, rate limits, etc.
|
|
5
|
+
* Backed by Prisma SystemDefault table with in-memory cache for fast reads.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const limit = await getDefault('limits.fund', 0.1);
|
|
9
|
+
* const perms = getDefaultSync('permissions.default', ['swap']);
|
|
10
|
+
* await setDefault('limits.fund', 0.05);
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { prisma } from './db';
|
|
14
|
+
|
|
15
|
+
// ─── Seed Defaults ──────────────────────────────────────────────────
|
|
16
|
+
// Canonical seed values — used for reset and as fallback when DB is empty.
|
|
17
|
+
|
|
18
|
+
export interface SeedDefault {
|
|
19
|
+
key: string;
|
|
20
|
+
value: unknown;
|
|
21
|
+
type: string;
|
|
22
|
+
label: string;
|
|
23
|
+
description?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const SEED_DEFAULTS: SeedDefault[] = [
|
|
27
|
+
// Permissions
|
|
28
|
+
{ key: 'permissions.default', value: ['wallet:create:hot', 'send:hot', 'swap', 'fund', 'action:create', 'secret:read', 'secret:write'], type: 'permissions', label: 'Default Agent Permissions', description: 'Permissions granted to new agent tokens by default' },
|
|
29
|
+
{ key: 'permissions.agent_tier', value: 'admin', type: 'permissions', label: 'Default Agent Permission Tier', description: 'Agent-chat permission level: admin (full access) or restricted (approval required)' },
|
|
30
|
+
|
|
31
|
+
// Financial limits
|
|
32
|
+
{ key: 'limits.fund', value: 0.1, type: 'financial', label: 'Default Fund Limit (ETH)', description: 'Default ETH limit for cold→hot transfers' },
|
|
33
|
+
{ key: 'limits.send', value: 0.1, type: 'financial', label: 'Default Send Limit (ETH)', description: 'Default ETH limit for hot wallet sends' },
|
|
34
|
+
{ key: 'limits.swap', value: 0.1, type: 'financial', label: 'Default Swap Limit (ETH)', description: 'Default ETH limit for token swaps' },
|
|
35
|
+
{ key: 'gas.evm_buffer', value: 0.001, type: 'financial', label: 'EVM Gas Buffer (ETH)', description: 'Reserved ETH buffer for max-send in UI' },
|
|
36
|
+
{ key: 'gas.sol_buffer', value: 0.000005, type: 'financial', label: 'Solana Gas Buffer (SOL)', description: 'Reserved SOL buffer for max-send in UI' },
|
|
37
|
+
|
|
38
|
+
// TTLs
|
|
39
|
+
{ key: 'ttl.agent', value: 3600, type: 'ttl', label: 'Agent Token TTL (seconds)', description: 'Default time-to-live for agent tokens' },
|
|
40
|
+
{ key: 'ttl.admin', value: 604800, type: 'ttl', label: 'Admin Token TTL (seconds)', description: 'Time-to-live for admin tokens (7 days)' },
|
|
41
|
+
{ key: 'ttl.app', value: 86400, type: 'ttl', label: 'App Token TTL (seconds)', description: 'Time-to-live for app tokens (24h)' },
|
|
42
|
+
{ key: 'ttl.action', value: 60, type: 'ttl', label: 'Action Token TTL (seconds)', description: 'Default time-to-live for action tokens' },
|
|
43
|
+
|
|
44
|
+
// Strategy runtime (cron-owned)
|
|
45
|
+
{ key: 'strategy.cron_enabled', value: true, type: 'strategy', label: 'Enable Cron-Owned Strategy Runtime', description: 'When true, strategy ticks and message hooks are owned by the cron process only' },
|
|
46
|
+
{ key: 'strategy.tick_interval', value: 1000, type: 'strategy', label: 'Strategy Scheduler Interval (ms)', description: 'Main cron loop interval for strategy ticks and queued messages' },
|
|
47
|
+
{ key: 'strategy.message_batch_size', value: 20, type: 'strategy', label: 'Strategy Message Batch Size', description: 'Maximum queued app messages processed per strategy loop' },
|
|
48
|
+
{ key: 'strategy.message_timeout_ms', value: 120000, type: 'strategy', label: 'App Message Timeout (ms)', description: 'How long API waits for cron-owned app message processing' },
|
|
49
|
+
{ key: 'strategy.health_stale_ms', value: 30000, type: 'strategy', label: 'Strategy Runtime Health Stale Threshold (ms)', description: 'How long a cron heartbeat can be stale before strategy runtime is considered unhealthy' },
|
|
50
|
+
|
|
51
|
+
// Rate limits (format: "max,windowMs")
|
|
52
|
+
{ key: 'rate.brute_force', value: '5,900000', type: 'rate_limit', label: 'Brute Force Limit', description: 'Max attempts per 15-minute window for auth endpoints' },
|
|
53
|
+
{ key: 'rate.auth_request', value: '10,60000', type: 'rate_limit', label: 'Auth Request Limit', description: 'Max auth requests per 1-minute window' },
|
|
54
|
+
{ key: 'rate.app_message', value: '10,60000', type: 'rate_limit', label: 'App Message Limit', description: 'Max messages per 1-minute window per app' },
|
|
55
|
+
{ key: 'rate.app_fetch', value: '60,60000', type: 'rate_limit', label: 'App Fetch Limit', description: 'Max fetch proxy requests per 1-minute window per app' },
|
|
56
|
+
{ key: 'rate.app_callback', value: '3,120000', type: 'rate_limit', label: 'App Callback Limit', description: 'Max auto-execute callbacks per 2-minute window per app' },
|
|
57
|
+
|
|
58
|
+
// Swap / slippage
|
|
59
|
+
{ key: 'swap.max_slippage', value: 50, type: 'swap', label: 'Max Slippage (%)', description: 'Maximum allowed slippage percentage' },
|
|
60
|
+
{ key: 'swap.min_slippage_admin', value: 0.5, type: 'swap', label: 'Min Slippage Admin (%)', description: 'Minimum slippage floor for admin tokens' },
|
|
61
|
+
{ key: 'swap.min_slippage_agent', value: 1.0, type: 'swap', label: 'Min Slippage Agent (%)', description: 'Minimum slippage floor for agent tokens' },
|
|
62
|
+
|
|
63
|
+
// AI provider & model
|
|
64
|
+
{ key: 'ai.provider', value: 'claude-cli', type: 'ai', label: 'AI provider mode', description: 'Which AI provider to use for hooks and ticks' },
|
|
65
|
+
|
|
66
|
+
// AI safety
|
|
67
|
+
{ key: 'ai.max_tool_calls', value: 10, type: 'ai_safety', label: 'Max Tool Calls', description: 'Maximum tool calls per hook invocation' },
|
|
68
|
+
{ key: 'ai.max_followup_depth', value: 3, type: 'ai_safety', label: 'Max Follow-up Depth', description: 'Maximum recursive intent follow-up depth' },
|
|
69
|
+
|
|
70
|
+
// Launch defaults
|
|
71
|
+
{ key: 'launch.initial_supply', value: '1000000000', type: 'launch', label: 'Initial Token Supply', description: 'Default initial supply for token launches' },
|
|
72
|
+
{ key: 'launch.sell_percent', value: 90, type: 'launch', label: 'Sell Percentage', description: 'Default percentage of supply to sell in auction' },
|
|
73
|
+
{ key: 'launch.epoch_length', value: 3600, type: 'launch', label: 'Epoch Length (seconds)', description: 'Default epoch length for dynamic auctions' },
|
|
74
|
+
|
|
75
|
+
// Protocol
|
|
76
|
+
{ key: 'protocol.fee_address', value: '0xa931533E0E0cCE34fc0FafB25ea2046d391eCAA5', type: 'protocol', label: 'Protocol Fee Address', description: 'Address that receives protocol fees from swaps and launches' },
|
|
77
|
+
|
|
78
|
+
// App constraints
|
|
79
|
+
{ key: 'app.max_file_size_mb', value: 5, type: 'app', label: 'Max App File Size (MB)', description: 'Maximum file size for installed apps' },
|
|
80
|
+
{ key: 'app.max_total_size_mb', value: 20, type: 'app', label: 'Max App Total Size (MB)', description: 'Maximum total size for installed apps' },
|
|
81
|
+
|
|
82
|
+
// Asset discovery
|
|
83
|
+
{ key: 'discovery.enabled', value: true, type: 'discovery', label: 'Enable Asset Discovery', description: 'Master toggle for incoming asset auto-discovery' },
|
|
84
|
+
{ key: 'discovery.scan_interval', value: 60000, type: 'discovery', label: 'Discovery Scan Interval (ms)', description: 'How often to scan for incoming transfers' },
|
|
85
|
+
{ key: 'discovery.max_blocks_per_tick', value: 2000, type: 'discovery', label: 'Max Blocks Per Scan', description: 'Maximum blocks to scan per tick per chain' },
|
|
86
|
+
{ key: 'discovery.min_value_usd', value: 0.5, type: 'discovery', label: 'Min Value USD', description: 'Minimum USD value to auto-track a discovered token' },
|
|
87
|
+
{ key: 'discovery.min_liquidity_usd', value: 1000, type: 'discovery', label: 'Min Liquidity USD', description: 'Minimum pool liquidity to auto-track a discovered token' },
|
|
88
|
+
{ key: 'discovery.safety_enabled', value: true, type: 'discovery', label: 'Enable Safety Check', description: 'Run honeypot/tax checks on discovered tokens' },
|
|
89
|
+
{ key: 'discovery.max_initial_lookback', value: 302400, type: 'discovery', label: 'Max Initial Lookback (blocks)', description: 'How far back to scan on first run (~7 days on Base)' },
|
|
90
|
+
|
|
91
|
+
// Trust / auto-approve defaults
|
|
92
|
+
{ key: 'trust.localAutoApprove', value: true, type: 'trust', label: 'Auto-Approve Local Socket Connections', description: 'Auto-approve auth requests from same-UID processes via Unix socket (0600 permission = same user = trusted)' },
|
|
93
|
+
{ key: 'trust.localProfile', value: 'dev', type: 'trust', label: 'Local Socket Agent Profile', description: 'Built-in profile used for local socket-issued agent tokens (strict/dev/admin)' },
|
|
94
|
+
{ key: 'trust.localProfileVersion', value: 'v1', type: 'trust', label: 'Local Socket Agent Profile Version', description: 'Version for local socket profile resolution' },
|
|
95
|
+
{ key: 'trust.localProfileOverrides', value: null, type: 'trust', label: 'Local Socket Agent Profile Overrides', description: 'Optional tighten-only overrides for the local socket profile' },
|
|
96
|
+
{ key: 'trust.localPermissions', value: ['extension:*'], type: 'trust', label: 'Auto-Approve Token Permissions', description: 'Permissions granted to auto-approved local tokens' },
|
|
97
|
+
{ key: 'trust.localLimits', value: { fund: 0, send: 0, swap: 0 }, type: 'trust', label: 'Auto-Approve Token Limits', description: 'Spending limits for auto-approved local tokens (0 = no financial operations)' },
|
|
98
|
+
{ key: 'trust.localTtl', value: 3600, type: 'trust', label: 'Auto-Approve Token TTL (seconds)', description: 'Time-to-live for auto-approved local tokens' },
|
|
99
|
+
|
|
100
|
+
// Credential vault defaults
|
|
101
|
+
{ key: 'defaults.credential.access.read', value: ['*'], type: 'credential', label: 'Default Credential Read Scopes', description: 'Default scopes for credential read access on new tokens' },
|
|
102
|
+
{ key: 'defaults.credential.access.write', value: ['*'], type: 'credential', label: 'Default Credential Write Scopes', description: 'Default scopes for credential write access on new tokens' },
|
|
103
|
+
{ key: 'defaults.credential.excludeFields.card', value: ['cvv'], type: 'credential', label: 'Card Excluded Fields', description: 'Fields excluded by default when reading card credentials' },
|
|
104
|
+
{ key: 'defaults.credential.excludeFields.login', value: ['password'], type: 'credential', label: 'Login Excluded Fields', description: 'Fields excluded by default when reading login credentials' },
|
|
105
|
+
{ key: 'defaults.credential.excludeFields.note', value: [], type: 'credential', label: 'Note Excluded Fields', description: 'Fields excluded by default when reading note credentials' },
|
|
106
|
+
|
|
107
|
+
// Balance sync
|
|
108
|
+
{ key: 'sync.active_interval', value: 15000, type: 'sync', label: 'Active Sync Interval (ms)', description: 'Interval for syncing active/cold wallets' },
|
|
109
|
+
{ key: 'sync.background_interval', value: 120000, type: 'sync', label: 'Background Sync Interval (ms)', description: 'Interval for syncing background wallets' },
|
|
110
|
+
{ key: 'sync.dormant_interval', value: 600000, type: 'sync', label: 'Dormant Sync Interval (ms)', description: 'Interval for syncing dormant wallets' },
|
|
111
|
+
{ key: 'sync.active_ttl', value: 60000, type: 'sync', label: 'Active Tier TTL (ms)', description: 'How long a wallet stays in active tier after focus' },
|
|
112
|
+
{ key: 'sync.enabled', value: true, type: 'sync', label: 'Enable Balance Sync', description: 'Master switch for the cron balance sync' },
|
|
113
|
+
{ key: 'sync.max_assets_per_call', value: 200, type: 'sync', label: 'Max Assets Per Multicall', description: 'Maximum ERC-20 assets per multicall batch' },
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
// ─── In-Memory Cache ────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
const cache = new Map<string, unknown>();
|
|
119
|
+
let cacheLoaded = false;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Invalidate one key or the entire defaults cache.
|
|
123
|
+
*/
|
|
124
|
+
export function invalidateCache(key?: string): void {
|
|
125
|
+
if (key) {
|
|
126
|
+
cache.delete(key);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
cache.clear();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ─── Change Listeners ───────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
type ChangeListener = (key: string, value: unknown) => void;
|
|
135
|
+
const listeners = new Map<string, Set<ChangeListener>>();
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Register a listener that fires when a specific key changes.
|
|
139
|
+
* Use '*' to listen to all changes.
|
|
140
|
+
*/
|
|
141
|
+
export function onDefaultChanged(key: string, callback: ChangeListener): () => void {
|
|
142
|
+
if (!listeners.has(key)) {
|
|
143
|
+
listeners.set(key, new Set());
|
|
144
|
+
}
|
|
145
|
+
listeners.get(key)!.add(callback);
|
|
146
|
+
return () => { listeners.get(key)?.delete(callback); };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function notifyListeners(key: string, value: unknown): void {
|
|
150
|
+
// Notify specific key listeners
|
|
151
|
+
listeners.get(key)?.forEach(cb => {
|
|
152
|
+
try { cb(key, value); } catch { /* listener errors don't propagate */ }
|
|
153
|
+
});
|
|
154
|
+
// Notify wildcard listeners
|
|
155
|
+
listeners.get('*')?.forEach(cb => {
|
|
156
|
+
try { cb(key, value); } catch { /* listener errors don't propagate */ }
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ─── Core API ───────────────────────────────────────────────────────
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Load all defaults from DB into cache. Call once at startup before app.listen().
|
|
164
|
+
*/
|
|
165
|
+
export async function preloadCache(): Promise<void> {
|
|
166
|
+
try {
|
|
167
|
+
const rows = await prisma.systemDefault.findMany();
|
|
168
|
+
for (const row of rows) {
|
|
169
|
+
cache.set(row.key, parseValue(row.value));
|
|
170
|
+
}
|
|
171
|
+
cacheLoaded = true;
|
|
172
|
+
} catch {
|
|
173
|
+
// DB may not have been migrated yet — seed defaults will be used as fallback
|
|
174
|
+
cacheLoaded = true;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get a default value (async). Reads: cache → DB → fallback.
|
|
180
|
+
*/
|
|
181
|
+
export async function getDefault<T>(key: string, fallback: T): Promise<T> {
|
|
182
|
+
// Cache hit
|
|
183
|
+
if (cache.has(key)) {
|
|
184
|
+
return cache.get(key) as T;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// DB lookup
|
|
188
|
+
try {
|
|
189
|
+
const row = await prisma.systemDefault.findUnique({ where: { key } });
|
|
190
|
+
if (row) {
|
|
191
|
+
const val = parseValue(row.value);
|
|
192
|
+
cache.set(key, val);
|
|
193
|
+
return val as T;
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
// DB error — fall through to seed/fallback
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Seed default
|
|
200
|
+
const seed = SEED_DEFAULTS.find(s => s.key === key);
|
|
201
|
+
if (seed !== undefined) {
|
|
202
|
+
cache.set(key, seed.value);
|
|
203
|
+
return seed.value as T;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return fallback;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get a default value synchronously from cache only.
|
|
211
|
+
* Requires preloadCache() to have been called at startup.
|
|
212
|
+
* Falls back to seed default → provided fallback.
|
|
213
|
+
*/
|
|
214
|
+
export function getDefaultSync<T>(key: string, fallback: T): T {
|
|
215
|
+
if (cache.has(key)) {
|
|
216
|
+
return cache.get(key) as T;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Seed default as fallback
|
|
220
|
+
const seed = SEED_DEFAULTS.find(s => s.key === key);
|
|
221
|
+
if (seed !== undefined) {
|
|
222
|
+
return seed.value as T;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return fallback;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Update a default value. Upserts to DB + updates cache + notifies listeners.
|
|
230
|
+
*/
|
|
231
|
+
export async function setDefault(key: string, value: unknown): Promise<void> {
|
|
232
|
+
const serialized = JSON.stringify(value);
|
|
233
|
+
|
|
234
|
+
// Look up metadata from seed or existing row
|
|
235
|
+
const seed = SEED_DEFAULTS.find(s => s.key === key);
|
|
236
|
+
const type = seed?.type || 'custom';
|
|
237
|
+
const label = seed?.label || key;
|
|
238
|
+
|
|
239
|
+
await prisma.systemDefault.upsert({
|
|
240
|
+
where: { key },
|
|
241
|
+
update: { value: serialized },
|
|
242
|
+
create: { key, value: serialized, type, label },
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
invalidateCache(key);
|
|
246
|
+
cache.set(key, value);
|
|
247
|
+
notifyListeners(key, value);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get all defaults grouped by type. Used by GET /defaults endpoint.
|
|
252
|
+
*/
|
|
253
|
+
export async function getAllDefaults(): Promise<Record<string, Array<{
|
|
254
|
+
key: string;
|
|
255
|
+
value: unknown;
|
|
256
|
+
type: string;
|
|
257
|
+
label: string;
|
|
258
|
+
description: string | null;
|
|
259
|
+
updatedAt: string;
|
|
260
|
+
}>>> {
|
|
261
|
+
const rows = await prisma.systemDefault.findMany({ orderBy: { key: 'asc' } });
|
|
262
|
+
const grouped: Record<string, Array<{
|
|
263
|
+
key: string;
|
|
264
|
+
value: unknown;
|
|
265
|
+
type: string;
|
|
266
|
+
label: string;
|
|
267
|
+
description: string | null;
|
|
268
|
+
updatedAt: string;
|
|
269
|
+
}>> = {};
|
|
270
|
+
|
|
271
|
+
for (const row of rows) {
|
|
272
|
+
if (!grouped[row.type]) {
|
|
273
|
+
grouped[row.type] = [];
|
|
274
|
+
}
|
|
275
|
+
grouped[row.type].push({
|
|
276
|
+
key: row.key,
|
|
277
|
+
value: parseValue(row.value),
|
|
278
|
+
type: row.type,
|
|
279
|
+
label: row.label,
|
|
280
|
+
description: row.description,
|
|
281
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return grouped;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Reset a default to its seed value. Pass '*' to reset all.
|
|
290
|
+
*/
|
|
291
|
+
export async function resetDefault(key: string): Promise<void> {
|
|
292
|
+
if (key === '*') {
|
|
293
|
+
// Reset all seed defaults
|
|
294
|
+
for (const seed of SEED_DEFAULTS) {
|
|
295
|
+
const serialized = JSON.stringify(seed.value);
|
|
296
|
+
await prisma.systemDefault.upsert({
|
|
297
|
+
where: { key: seed.key },
|
|
298
|
+
update: { value: serialized },
|
|
299
|
+
create: {
|
|
300
|
+
key: seed.key,
|
|
301
|
+
value: serialized,
|
|
302
|
+
type: seed.type,
|
|
303
|
+
label: seed.label,
|
|
304
|
+
description: seed.description,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
invalidateCache(seed.key);
|
|
308
|
+
cache.set(seed.key, seed.value);
|
|
309
|
+
notifyListeners(seed.key, seed.value);
|
|
310
|
+
}
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const seed = SEED_DEFAULTS.find(s => s.key === key);
|
|
315
|
+
if (!seed) {
|
|
316
|
+
throw new Error(`No seed default for key: ${key}`);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const serialized = JSON.stringify(seed.value);
|
|
320
|
+
await prisma.systemDefault.upsert({
|
|
321
|
+
where: { key },
|
|
322
|
+
update: { value: serialized },
|
|
323
|
+
create: {
|
|
324
|
+
key: seed.key,
|
|
325
|
+
value: serialized,
|
|
326
|
+
type: seed.type,
|
|
327
|
+
label: seed.label,
|
|
328
|
+
description: seed.description,
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
invalidateCache(key);
|
|
333
|
+
cache.set(key, seed.value);
|
|
334
|
+
notifyListeners(key, seed.value);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ─── Helpers ────────────────────────────────────────────────────────
|
|
338
|
+
|
|
339
|
+
/** Parse a stored JSON string back to its value */
|
|
340
|
+
function parseValue(raw: string): unknown {
|
|
341
|
+
try {
|
|
342
|
+
return JSON.parse(raw);
|
|
343
|
+
} catch {
|
|
344
|
+
return raw;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Parse a rate limit string "max,windowMs" into { max, windowMs }.
|
|
350
|
+
*/
|
|
351
|
+
export function parseRateLimit(value: unknown): { max: number; windowMs: number } {
|
|
352
|
+
if (typeof value === 'string') {
|
|
353
|
+
const [maxStr, windowStr] = value.split(',');
|
|
354
|
+
return { max: parseInt(maxStr, 10), windowMs: parseInt(windowStr, 10) };
|
|
355
|
+
}
|
|
356
|
+
return { max: 10, windowMs: 60000 };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// ─── Test Helpers ───────────────────────────────────────────────────
|
|
360
|
+
|
|
361
|
+
/** Reset cache state (for tests only) */
|
|
362
|
+
export function __resetCache(): void {
|
|
363
|
+
cache.clear();
|
|
364
|
+
listeners.clear();
|
|
365
|
+
cacheLoaded = false;
|
|
366
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import { DexAdapter, PoolInfo } from './types';
|
|
3
|
+
import { uniswapAdapter } from './uniswap';
|
|
4
|
+
import { relayAdapter } from './relay';
|
|
5
|
+
|
|
6
|
+
// Re-export types
|
|
7
|
+
export * from './types';
|
|
8
|
+
|
|
9
|
+
// Registry of available DEX adapters
|
|
10
|
+
const adapters: Record<string, DexAdapter> = {
|
|
11
|
+
relay: relayAdapter,
|
|
12
|
+
uniswap: uniswapAdapter,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Default DEX preference order for auto-detection
|
|
16
|
+
// Relay is primary (aggregator with MEV protection), Uniswap is fallback
|
|
17
|
+
const DEX_PRIORITY = ['relay', 'uniswap'] as const;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get a specific DEX adapter by name
|
|
21
|
+
*/
|
|
22
|
+
export function getDexAdapter(name: string): DexAdapter | null {
|
|
23
|
+
return adapters[name.toLowerCase()] || null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get all available DEX adapters
|
|
28
|
+
*/
|
|
29
|
+
export function getAllAdapters(): DexAdapter[] {
|
|
30
|
+
return Object.values(adapters);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get adapters that support a specific chain
|
|
35
|
+
*/
|
|
36
|
+
export function getAdaptersForChain(chainId: number): DexAdapter[] {
|
|
37
|
+
return Object.values(adapters).filter(a => a.supportsChain(chainId));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Detect the best DEX and pool for a token
|
|
42
|
+
* Returns the first DEX that has liquidity for this token
|
|
43
|
+
*/
|
|
44
|
+
export async function detectBestDex(
|
|
45
|
+
token: string,
|
|
46
|
+
provider: ethers.Provider,
|
|
47
|
+
chainId: number
|
|
48
|
+
): Promise<{ adapter: DexAdapter; pool: PoolInfo } | null> {
|
|
49
|
+
const supportedAdapters = getAdaptersForChain(chainId);
|
|
50
|
+
|
|
51
|
+
// Check in priority order
|
|
52
|
+
for (const dexName of DEX_PRIORITY) {
|
|
53
|
+
const adapter = adapters[dexName];
|
|
54
|
+
if (!adapter || !adapter.supportsChain(chainId)) continue;
|
|
55
|
+
|
|
56
|
+
const pool = await adapter.detectPool(token, provider);
|
|
57
|
+
if (pool) {
|
|
58
|
+
return { adapter, pool };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Check any remaining adapters not in priority list
|
|
63
|
+
for (const adapter of supportedAdapters) {
|
|
64
|
+
if (DEX_PRIORITY.includes(adapter.name as typeof DEX_PRIORITY[number])) continue;
|
|
65
|
+
|
|
66
|
+
const pool = await adapter.detectPool(token, provider);
|
|
67
|
+
if (pool) {
|
|
68
|
+
return { adapter, pool };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* List available DEX names
|
|
77
|
+
*/
|
|
78
|
+
export function listDexes(): string[] {
|
|
79
|
+
return Object.keys(adapters);
|
|
80
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import { DexAdapter, PoolInfo, SwapParams, SwapTxData } from './types';
|
|
3
|
+
import { getDefaultSync } from '../defaults';
|
|
4
|
+
|
|
5
|
+
const RELAY_API = 'https://api.relay.link';
|
|
6
|
+
|
|
7
|
+
// Native ETH address used by Relay
|
|
8
|
+
const NATIVE_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
9
|
+
|
|
10
|
+
// App fee: 1% (100 bps). Relay keeps 25%, we keep 75%.
|
|
11
|
+
const APP_FEE_BPS = '100';
|
|
12
|
+
|
|
13
|
+
// Supported EVM chain IDs
|
|
14
|
+
const SUPPORTED_CHAINS = new Set([
|
|
15
|
+
1, // Ethereum
|
|
16
|
+
10, // Optimism
|
|
17
|
+
137, // Polygon
|
|
18
|
+
8453, // Base
|
|
19
|
+
42161, // Arbitrum
|
|
20
|
+
324, // zkSync Era
|
|
21
|
+
43114, // Avalanche
|
|
22
|
+
56, // BNB Chain
|
|
23
|
+
250, // Fantom
|
|
24
|
+
59144, // Linea
|
|
25
|
+
534352, // Scroll
|
|
26
|
+
7777777, // Zora
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
// Relay quote request
|
|
30
|
+
interface RelayQuoteRequest {
|
|
31
|
+
user: string;
|
|
32
|
+
originChainId: number;
|
|
33
|
+
destinationChainId: number;
|
|
34
|
+
originCurrency: string;
|
|
35
|
+
destinationCurrency: string;
|
|
36
|
+
amount: string;
|
|
37
|
+
tradeType: 'EXACT_INPUT';
|
|
38
|
+
slippageTolerance?: string;
|
|
39
|
+
source?: string;
|
|
40
|
+
appFees?: { recipient: string; fee: string }[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Relay quote response types
|
|
44
|
+
interface RelayTxData {
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
data: string;
|
|
48
|
+
value: string;
|
|
49
|
+
chainId: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface RelayStepItem {
|
|
53
|
+
status: string;
|
|
54
|
+
data: RelayTxData;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface RelayStep {
|
|
58
|
+
id: string;
|
|
59
|
+
kind: string;
|
|
60
|
+
items: RelayStepItem[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface RelayQuoteResponse {
|
|
64
|
+
steps: RelayStep[];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Relay price response types
|
|
68
|
+
export interface RelayPriceResponse {
|
|
69
|
+
fees: {
|
|
70
|
+
gas: { amount: string; amountUsd: string; currency: { symbol: string } };
|
|
71
|
+
relayer?: { amount: string; amountUsd: string };
|
|
72
|
+
app?: { amount: string; amountUsd: string };
|
|
73
|
+
};
|
|
74
|
+
details: {
|
|
75
|
+
sender: { amount: string; amountFormatted: string; amountUsd: string; currency: { symbol: string; decimals: number; address: string } };
|
|
76
|
+
recipient: { amount: string; amountFormatted: string; amountUsd: string; currency: { symbol: string; decimals: number; address: string } };
|
|
77
|
+
rate: string;
|
|
78
|
+
slippageTolerance: { origin: { percent: string } };
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get a lightweight price/quote from Relay.
|
|
84
|
+
* Uses /price endpoint (no calldata, lighter than /quote/v2).
|
|
85
|
+
* Returns amounts, fees, exchange rate, and impact.
|
|
86
|
+
*/
|
|
87
|
+
export async function getRelayPrice(params: {
|
|
88
|
+
user: string;
|
|
89
|
+
originChainId: number;
|
|
90
|
+
destinationChainId?: number;
|
|
91
|
+
originCurrency: string;
|
|
92
|
+
destinationCurrency: string;
|
|
93
|
+
amount: string;
|
|
94
|
+
slippageBps?: number;
|
|
95
|
+
}): Promise<RelayPriceResponse> {
|
|
96
|
+
const body: RelayQuoteRequest = {
|
|
97
|
+
user: params.user,
|
|
98
|
+
originChainId: params.originChainId,
|
|
99
|
+
destinationChainId: params.destinationChainId ?? params.originChainId,
|
|
100
|
+
originCurrency: params.originCurrency,
|
|
101
|
+
destinationCurrency: params.destinationCurrency,
|
|
102
|
+
amount: params.amount,
|
|
103
|
+
tradeType: 'EXACT_INPUT',
|
|
104
|
+
source: 'aurawallet',
|
|
105
|
+
appFees: [{ recipient: getDefaultSync('protocol.fee_address', '0xa931533E0E0cCE34fc0FafB25ea2046d391eCAA5'), fee: APP_FEE_BPS }],
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
if (params.slippageBps !== undefined) {
|
|
109
|
+
body.slippageTolerance = params.slippageBps.toString();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const response = await fetch(`${RELAY_API}/price`, {
|
|
113
|
+
method: 'POST',
|
|
114
|
+
headers: { 'Content-Type': 'application/json' },
|
|
115
|
+
body: JSON.stringify(body),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
const text = await response.text();
|
|
120
|
+
throw new Error(`Relay price failed: ${response.status} ${text}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return response.json();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get a swap quote from Relay.
|
|
128
|
+
* Returns the raw transaction data for the first incomplete transaction step.
|
|
129
|
+
*/
|
|
130
|
+
async function getRelayQuote(params: {
|
|
131
|
+
user: string;
|
|
132
|
+
originChainId: number;
|
|
133
|
+
destinationChainId?: number;
|
|
134
|
+
originCurrency: string;
|
|
135
|
+
destinationCurrency: string;
|
|
136
|
+
amount: string;
|
|
137
|
+
slippageBps?: number;
|
|
138
|
+
}): Promise<RelayTxData> {
|
|
139
|
+
const body: RelayQuoteRequest = {
|
|
140
|
+
user: params.user,
|
|
141
|
+
originChainId: params.originChainId,
|
|
142
|
+
destinationChainId: params.destinationChainId ?? params.originChainId,
|
|
143
|
+
originCurrency: params.originCurrency,
|
|
144
|
+
destinationCurrency: params.destinationCurrency,
|
|
145
|
+
amount: params.amount,
|
|
146
|
+
tradeType: 'EXACT_INPUT',
|
|
147
|
+
source: 'aurawallet',
|
|
148
|
+
appFees: [{ recipient: getDefaultSync('protocol.fee_address', '0xa931533E0E0cCE34fc0FafB25ea2046d391eCAA5'), fee: APP_FEE_BPS }],
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
if (params.slippageBps !== undefined) {
|
|
152
|
+
body.slippageTolerance = params.slippageBps.toString();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const response = await fetch(`${RELAY_API}/quote/v2`, {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
headers: { 'Content-Type': 'application/json' },
|
|
158
|
+
body: JSON.stringify(body),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (!response.ok) {
|
|
162
|
+
const text = await response.text();
|
|
163
|
+
throw new Error(`Relay quote failed: ${response.status} ${text}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const data: RelayQuoteResponse = await response.json();
|
|
167
|
+
|
|
168
|
+
// Find the first incomplete transaction step
|
|
169
|
+
for (const step of data.steps) {
|
|
170
|
+
if (step.kind !== 'transaction') continue;
|
|
171
|
+
for (const item of step.items) {
|
|
172
|
+
if (item.status === 'incomplete' && item.data) {
|
|
173
|
+
return item.data;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
throw new Error('Relay quote returned no executable transaction steps');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Relay adapter for same-chain swaps via Relay aggregator API.
|
|
183
|
+
* Supports all major EVM chains. No API key required.
|
|
184
|
+
*/
|
|
185
|
+
export const relayAdapter: DexAdapter = {
|
|
186
|
+
name: 'relay',
|
|
187
|
+
|
|
188
|
+
supportsChain(chainId: number): boolean {
|
|
189
|
+
return SUPPORTED_CHAINS.has(chainId);
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
async detectPool(
|
|
193
|
+
_token: string,
|
|
194
|
+
_provider: ethers.Provider
|
|
195
|
+
): Promise<PoolInfo | null> {
|
|
196
|
+
// Relay is an aggregator -- it finds routes internally.
|
|
197
|
+
// Return a synthetic PoolInfo to indicate Relay can handle this token.
|
|
198
|
+
return { version: 'relay', poolAddress: 'relay' };
|
|
199
|
+
},
|
|
200
|
+
|
|
201
|
+
async buildSwapTx(params: SwapParams): Promise<SwapTxData> {
|
|
202
|
+
const { token, direction, amount, from, chainId, destinationChainId } = params;
|
|
203
|
+
|
|
204
|
+
// Determine origin/destination currencies
|
|
205
|
+
// buy: ETH -> token, sell: token -> ETH
|
|
206
|
+
const originCurrency = direction === 'buy' ? NATIVE_ADDRESS : token;
|
|
207
|
+
const destinationCurrency = direction === 'buy' ? token : NATIVE_ADDRESS;
|
|
208
|
+
|
|
209
|
+
// Amount is already in wei (EVM) or lamports (Solana)
|
|
210
|
+
const amountWei = amount;
|
|
211
|
+
|
|
212
|
+
const txData = await getRelayQuote({
|
|
213
|
+
user: from,
|
|
214
|
+
originChainId: chainId,
|
|
215
|
+
destinationChainId,
|
|
216
|
+
originCurrency,
|
|
217
|
+
destinationCurrency,
|
|
218
|
+
amount: amountWei,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
to: txData.to,
|
|
223
|
+
data: txData.data,
|
|
224
|
+
value: txData.value || '0',
|
|
225
|
+
};
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
getRouterAddress(): string {
|
|
229
|
+
// Relay uses different contract addresses per chain/tx.
|
|
230
|
+
// Return a descriptive placeholder; the actual `to` comes from buildSwapTx.
|
|
231
|
+
return 'relay';
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
export default relayAdapter;
|