auramaxx 0.0.1
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 +77 -0
- package/apps/desktop-electron/main.js +428 -0
- package/bin/auramaxx.js +1063 -0
- package/docs/ADAPTERS.md +466 -0
- package/docs/AGENT_SETUP.md +159 -0
- package/docs/API.md +127 -0
- package/docs/APPS.md +199 -0
- package/docs/ARCHITECTURE.md +235 -0
- package/docs/AUTH.md +318 -0
- package/docs/BEST-PRACTICES.md +82 -0
- package/docs/CLI.md +141 -0
- package/docs/DESKTOP_ELECTRON.md +26 -0
- package/docs/DEVELOPING-APPS.md +453 -0
- package/docs/MCP.md +122 -0
- package/docs/PACKAGING_POLICY.md +19 -0
- package/docs/PERMISSION.md +137 -0
- package/docs/PROTOCOL.md +142 -0
- package/docs/README.md +50 -0
- package/docs/SKILLS.md +132 -0
- package/docs/TROUBLESHOOTING.md +376 -0
- package/docs/WORKSPACE.md +673 -0
- package/docs/agent-auth.md +14 -0
- package/docs/api/authentication.md +79 -0
- package/docs/api/secrets/api-keys.md +28 -0
- package/docs/api/secrets/credentials.md +80 -0
- package/docs/api/secrets/sharing.md +48 -0
- package/docs/api/system.md +41 -0
- package/docs/api/wallets/apps-strategies.md +66 -0
- package/docs/api/wallets/core.md +46 -0
- package/docs/api/wallets/data-portfolio.md +42 -0
- package/docs/aura-file.md +48 -0
- package/docs/core-concepts/FEATURES.md +114 -0
- package/docs/credentials.md +120 -0
- package/docs/external/HOW_TO_AURAMAXX/GETTING_SECRETS.md +33 -0
- package/docs/external/HOW_TO_AURAMAXX/README.md +45 -0
- package/docs/external/getting-started.md +10 -0
- package/docs/external/overview.md +19 -0
- package/docs/external/persona-paths.md +7 -0
- package/docs/external/share-secret.md +76 -0
- package/docs/external/why-aura.md +7 -0
- package/docs/security.md +227 -0
- package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -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 +28 -0
- package/package.json +167 -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/20260222090000_update_admin_ttl_default/migration.sql +10 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +447 -0
- package/public/logo.webp +0 -0
- package/scripts/add-app.js +245 -0
- package/server/abi/SwapHelper.json +438 -0
- package/server/cli/approval.ts +447 -0
- package/server/cli/commands/actions.ts +474 -0
- package/server/cli/commands/api.ts +220 -0
- package/server/cli/commands/apikey.ts +277 -0
- package/server/cli/commands/app.ts +204 -0
- package/server/cli/commands/auth.ts +464 -0
- package/server/cli/commands/cron.ts +24 -0
- package/server/cli/commands/diary.ts +274 -0
- package/server/cli/commands/doctor.ts +1247 -0
- package/server/cli/commands/env.ts +476 -0
- package/server/cli/commands/experimental.ts +69 -0
- package/server/cli/commands/init.ts +798 -0
- package/server/cli/commands/lock.ts +157 -0
- package/server/cli/commands/mcp.ts +285 -0
- package/server/cli/commands/quickhack.ts +86 -0
- package/server/cli/commands/release-check.ts +231 -0
- package/server/cli/commands/restore.ts +314 -0
- package/server/cli/commands/service.ts +320 -0
- package/server/cli/commands/shell-hook.ts +512 -0
- package/server/cli/commands/skill.ts +216 -0
- package/server/cli/commands/start.ts +139 -0
- package/server/cli/commands/status.ts +59 -0
- package/server/cli/commands/stop.ts +36 -0
- package/server/cli/commands/token.ts +180 -0
- package/server/cli/commands/unlock.ts +50 -0
- package/server/cli/commands/vault.ts +1323 -0
- package/server/cli/commands/wallet.ts +209 -0
- package/server/cli/index.ts +280 -0
- package/server/cli/lib/approval-poll.ts +94 -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 +280 -0
- package/server/cli/lib/dotenv-migrate.ts +116 -0
- package/server/cli/lib/dotenv-parser.ts +146 -0
- package/server/cli/lib/escalation.ts +57 -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/lock-unlock-helper.ts +71 -0
- package/server/cli/lib/process.ts +162 -0
- package/server/cli/lib/prompt.ts +294 -0
- package/server/cli/lib/theme.ts +240 -0
- package/server/cli/socket.ts +579 -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 +420 -0
- package/server/lib/adapters/factory.ts +119 -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 +419 -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 +258 -0
- package/server/lib/app-installer.ts +505 -0
- package/server/lib/app-tokens.ts +247 -0
- package/server/lib/approval-link.ts +27 -0
- package/server/lib/auth.ts +314 -0
- package/server/lib/auto-execute.ts +160 -0
- package/server/lib/batch.ts +242 -0
- package/server/lib/cold.ts +1048 -0
- package/server/lib/config.ts +408 -0
- package/server/lib/credential-access-audit.ts +85 -0
- package/server/lib/credential-access-policy.ts +111 -0
- package/server/lib/credential-health.ts +343 -0
- package/server/lib/credential-import.ts +608 -0
- package/server/lib/credential-scope.ts +102 -0
- package/server/lib/credential-shares.ts +190 -0
- package/server/lib/credential-transport.ts +533 -0
- package/server/lib/credential-vault.ts +77 -0
- package/server/lib/credentials.ts +422 -0
- package/server/lib/crypto.ts +8 -0
- package/server/lib/db.ts +58 -0
- package/server/lib/defaults.ts +386 -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/diary.ts +34 -0
- package/server/lib/dont-ask-again-policy.ts +41 -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 +114 -0
- package/server/lib/error.ts +20 -0
- package/server/lib/events.ts +217 -0
- package/server/lib/feature-flags.ts +93 -0
- package/server/lib/hot.ts +357 -0
- package/server/lib/human-action-summary.ts +80 -0
- package/server/lib/key-fingerprint.ts +28 -0
- package/server/lib/logger.ts +340 -0
- package/server/lib/network.ts +137 -0
- package/server/lib/notifications.ts +230 -0
- package/server/lib/oauth2-refresh.ts +241 -0
- package/server/lib/oursecret.ts +71 -0
- package/server/lib/passkey-credential.ts +360 -0
- package/server/lib/passkey.ts +68 -0
- package/server/lib/permissions.ts +299 -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 +297 -0
- package/server/lib/resolve-action.ts +328 -0
- package/server/lib/resolve.ts +36 -0
- package/server/lib/secret-gist-share.ts +296 -0
- package/server/lib/sessions.ts +634 -0
- package/server/lib/socket-path.ts +56 -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 +159 -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 +237 -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 +84 -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/update-check.ts +35 -0
- package/server/lib/verified-summary.ts +414 -0
- package/server/lib/view-registry.ts +80 -0
- package/server/mcp/profile-policy.ts +30 -0
- package/server/mcp/server.ts +1589 -0
- package/server/mcp/tools.ts +276 -0
- package/server/middleware/auth.ts +119 -0
- package/server/middleware/requestLogger.ts +84 -0
- package/server/routes/actions.ts +539 -0
- package/server/routes/adapters.ts +711 -0
- package/server/routes/addressbook.ts +113 -0
- package/server/routes/ai.ts +34 -0
- package/server/routes/apikeys.ts +343 -0
- package/server/routes/apps.ts +601 -0
- package/server/routes/auth.ts +406 -0
- package/server/routes/backup.ts +404 -0
- package/server/routes/batch.ts +270 -0
- package/server/routes/bookmarks.ts +162 -0
- package/server/routes/credential-shares.ts +380 -0
- package/server/routes/credential-vaults.ts +159 -0
- package/server/routes/credentials.ts +1782 -0
- package/server/routes/dashboard.ts +97 -0
- package/server/routes/defaults.ts +124 -0
- package/server/routes/flags.ts +11 -0
- package/server/routes/fund.ts +225 -0
- package/server/routes/heartbeat.ts +375 -0
- package/server/routes/import.ts +364 -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 +366 -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 +352 -0
- package/server/routes/swap-solana.ts +176 -0
- package/server/routes/swap.ts +356 -0
- package/server/routes/token.ts +247 -0
- package/server/routes/unlock.ts +467 -0
- package/server/routes/views.ts +41 -0
- package/server/routes/wallet-assets.ts +361 -0
- package/server/routes/wallet-transactions.ts +515 -0
- package/server/routes/wallet.ts +709 -0
- package/server/types.ts +146 -0
- package/shared/credential-field-schema.ts +248 -0
- package/skills/auramaxx/HEARTBEAT.md +78 -0
- package/skills/auramaxx/SKILL.md +745 -0
- package/skills/auramaxx/docs/AGENT_SETUP.md +155 -0
- package/skills/auramaxx/docs/API.md +127 -0
- package/skills/auramaxx/docs/AUTH.md +318 -0
- package/skills/auramaxx/docs/CLI.md +130 -0
- package/skills/auramaxx/docs/MCP.md +122 -0
- package/skills/auramaxx/docs/TROUBLESHOOTING.md +357 -0
- package/skills/auramaxx/docs/WORKSPACE.md +673 -0
- package/skills/auramaxx/docs/security.md +227 -0
- package/skills/task-lifecycle/SKILL.md +378 -0
- package/src/app/api/[...doc]/page.tsx +36 -0
- package/src/app/api/agent-requests/route.ts +30 -0
- package/src/app/api/apps/install/route.ts +132 -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/docs/plain/route.ts +74 -0
- package/src/app/api/events/route.ts +92 -0
- package/src/app/api/page.tsx +290 -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 +40 -0
- package/src/app/api/workspace/config/route.ts +121 -0
- package/src/app/api/workspace/import/route.ts +127 -0
- package/src/app/api/workspace/route.ts +116 -0
- package/src/app/app-legacy-do-not-use/page.tsx +2245 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/approve/[actionId]/page.tsx +409 -0
- package/src/app/docs/DocsPageContent.tsx +269 -0
- package/src/app/docs/[...doc]/page.tsx +41 -0
- package/src/app/docs/page.tsx +38 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +819 -0
- package/src/app/health/page.tsx +5 -0
- package/src/app/hello/page.tsx +102 -0
- package/src/app/icon.png +0 -0
- package/src/app/layout.tsx +39 -0
- package/src/app/page.tsx +1964 -0
- package/src/app/privacy/page.tsx +63 -0
- package/src/app/providers.tsx +87 -0
- package/src/app/share/[token]/page.tsx +295 -0
- package/src/app/terms/page.tsx +80 -0
- package/src/components/ChainSelector.tsx +44 -0
- package/src/components/HumanActionBar.tsx +697 -0
- package/src/components/NotificationDrawer.tsx +387 -0
- package/src/components/PasskeyEnrollmentPrompt.tsx +235 -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 +88 -0
- package/src/components/design-system/ChainIndicator.tsx +65 -0
- package/src/components/design-system/ChainSelector.tsx +147 -0
- package/src/components/design-system/ConfirmationModal.tsx +107 -0
- package/src/components/design-system/ConfirmationPopover.tsx +81 -0
- package/src/components/design-system/DownloadButton.tsx +149 -0
- package/src/components/design-system/Drawer.tsx +133 -0
- package/src/components/design-system/FilterDropdown.tsx +183 -0
- package/src/components/design-system/ItemPicker.tsx +157 -0
- package/src/components/design-system/Modal.tsx +296 -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 +65 -0
- package/src/components/design-system/TyvekCollapsibleSection.tsx +55 -0
- package/src/components/design-system/index.ts +14 -0
- package/src/components/docs/ClientSideMarkdown.tsx +51 -0
- package/src/components/docs/DocsSearchBar.tsx +118 -0
- package/src/components/docs/DocsThemeToggle.tsx +38 -0
- package/src/components/docs/PersistentDocGroup.tsx +91 -0
- package/src/components/docs/ShareUrlButton.tsx +33 -0
- package/src/components/docs/SidebarScrollMemory.tsx +56 -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/CreateViewModal.tsx +88 -0
- package/src/components/layout/LeftRail.tsx +114 -0
- package/src/components/layout/TabBar.tsx +284 -0
- package/src/components/layout/WalletSidebar.tsx +1030 -0
- package/src/components/layout/index.ts +6 -0
- package/src/components/marketing/AuraMaxxSpecOverlay.tsx +653 -0
- package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
- package/src/components/vault/ApiKeysConsole.tsx +1272 -0
- package/src/components/vault/AuditConsole.tsx +600 -0
- package/src/components/vault/CredentialDetail.tsx +625 -0
- package/src/components/vault/CredentialEmpty.tsx +55 -0
- package/src/components/vault/CredentialField.tsx +583 -0
- package/src/components/vault/CredentialForm.tsx +1484 -0
- package/src/components/vault/CredentialList.tsx +265 -0
- package/src/components/vault/CredentialRow.tsx +130 -0
- package/src/components/vault/CredentialShareModal.tsx +273 -0
- package/src/components/vault/CredentialVault.tsx +1662 -0
- package/src/components/vault/CredentialWalletWidget.tsx +103 -0
- package/src/components/vault/DocsConsole.tsx +113 -0
- package/src/components/vault/ImportCredentialsModal.tsx +578 -0
- package/src/components/vault/LargeTypeModal.tsx +88 -0
- package/src/components/vault/PasswordGenerator.tsx +232 -0
- package/src/components/vault/TOTPDisplay.tsx +108 -0
- package/src/components/vault/TotpSetupPanel.tsx +198 -0
- package/src/components/vault/VaultSidebar.tsx +881 -0
- package/src/components/vault/credentialFormName.ts +91 -0
- package/src/components/vault/hooks/useVaultKeyboardShortcuts.ts +69 -0
- package/src/components/vault/types.ts +56 -0
- package/src/context/AuthContext.tsx +365 -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 +4 -0
- package/src/hooks/useAgentActions.ts +552 -0
- package/src/hooks/useBalance.ts +103 -0
- package/src/hooks/useBalances.ts +129 -0
- package/src/hooks/useTheme.ts +156 -0
- package/src/instrumentation.ts +12 -0
- package/src/lib/api-docs.ts +154 -0
- package/src/lib/api.ts +474 -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/credential-field-schema.ts +11 -0
- package/src/lib/crypto.ts +112 -0
- package/src/lib/db.ts +21 -0
- package/src/lib/docs.ts +544 -0
- package/src/lib/events.ts +363 -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/totp-import.ts +57 -0
- package/src/lib/vault-crypto.ts +129 -0
- package/src/lib/view-registry.ts +57 -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 +170 -0
- package/tailwind.config.ts +99 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,665 @@
|
|
|
1
|
+
import { Router, Request, Response } from 'express';
|
|
2
|
+
import { createPublicClient, createWalletClient, http, parseEther } from 'viem';
|
|
3
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
4
|
+
import { base, mainnet, ink, unichain } from 'viem/chains';
|
|
5
|
+
import {
|
|
6
|
+
DopplerSDK,
|
|
7
|
+
MulticurveBuilder,
|
|
8
|
+
StaticAuctionBuilder,
|
|
9
|
+
DynamicAuctionBuilder,
|
|
10
|
+
FEE_TIERS,
|
|
11
|
+
DAY_SECONDS,
|
|
12
|
+
getAddresses,
|
|
13
|
+
isSupportedChainId,
|
|
14
|
+
} from '@whetstone-research/doppler-sdk';
|
|
15
|
+
import { getHotWallet, exportHotWallet, tokenCanAccessWallet } from '../lib/hot';
|
|
16
|
+
import { getTempWallet, hasTempWallet } from '../lib/temp';
|
|
17
|
+
import { isUnlocked } from '../lib/cold';
|
|
18
|
+
import { loadConfig, getRpcUrl, resolveChain } from '../lib/config';
|
|
19
|
+
import { prisma } from '../lib/db';
|
|
20
|
+
import { logger, logEvent } from '../lib/logger';
|
|
21
|
+
import { requireWalletAuth } from '../middleware/auth';
|
|
22
|
+
import { hasAnyPermission, isAdmin, buildPermissionDenied } from '../lib/permissions';
|
|
23
|
+
import { getDefault, getDefaultSync } from '../lib/defaults';
|
|
24
|
+
import { getErrorMessage, HttpError } from '../lib/error';
|
|
25
|
+
import { recordTransaction, autoTrackToken } from '../lib/transactions';
|
|
26
|
+
|
|
27
|
+
const router = Router();
|
|
28
|
+
|
|
29
|
+
interface LaunchRequest {
|
|
30
|
+
from: string;
|
|
31
|
+
name: string;
|
|
32
|
+
symbol: string;
|
|
33
|
+
tokenURI?: string;
|
|
34
|
+
type?: 'static' | 'dynamic' | 'multicurve';
|
|
35
|
+
|
|
36
|
+
// Token metadata (builds tokenURI automatically if tokenURI not provided)
|
|
37
|
+
imageUrl?: string; // Public URL of the token image (e.g. telegra.ph)
|
|
38
|
+
metadata?: Record<string, string>; // Extra metadata fields (description, website, twitter, etc.)
|
|
39
|
+
|
|
40
|
+
// Supply config
|
|
41
|
+
initialSupply?: string; // Default: '1000000000' (1B)
|
|
42
|
+
numTokensToSell?: string; // Default: 90% of initialSupply
|
|
43
|
+
|
|
44
|
+
// Multicurve preset (simplest option)
|
|
45
|
+
preset?: 'low' | 'medium' | 'high';
|
|
46
|
+
|
|
47
|
+
// Pool config (advanced)
|
|
48
|
+
fee?: number;
|
|
49
|
+
tickSpacing?: number;
|
|
50
|
+
|
|
51
|
+
// Static auction ticks
|
|
52
|
+
startTick?: number;
|
|
53
|
+
endTick?: number;
|
|
54
|
+
|
|
55
|
+
// Dynamic auction config
|
|
56
|
+
duration?: number; // seconds
|
|
57
|
+
epochLength?: number; // seconds
|
|
58
|
+
minProceeds?: string; // ETH
|
|
59
|
+
maxProceeds?: string; // ETH
|
|
60
|
+
|
|
61
|
+
// Migration after auction
|
|
62
|
+
migration?: 'uniswapV2' | 'uniswapV3' | 'uniswapV4' | 'noOp';
|
|
63
|
+
|
|
64
|
+
// Vesting
|
|
65
|
+
vestingDuration?: number; // seconds
|
|
66
|
+
|
|
67
|
+
// Scheduling (multicurve only)
|
|
68
|
+
startTime?: number; // Unix timestamp
|
|
69
|
+
|
|
70
|
+
// Governance
|
|
71
|
+
governance?: 'default' | 'noOp';
|
|
72
|
+
|
|
73
|
+
// Beneficiaries (fee recipients for the launched pool)
|
|
74
|
+
beneficiaries?: { address: string; shares: string }[];
|
|
75
|
+
|
|
76
|
+
chain?: string;
|
|
77
|
+
description?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Map chain names to viem chain objects
|
|
81
|
+
const VIEM_CHAINS: Record<string, typeof base> = {
|
|
82
|
+
base,
|
|
83
|
+
ethereum: mainnet,
|
|
84
|
+
ink,
|
|
85
|
+
unichain,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// POST /launch - Launch a token via Doppler protocol
|
|
89
|
+
router.post('/', requireWalletAuth, async (req: Request, res: Response) => {
|
|
90
|
+
try {
|
|
91
|
+
const {
|
|
92
|
+
from,
|
|
93
|
+
name,
|
|
94
|
+
symbol,
|
|
95
|
+
tokenURI: explicitTokenURI,
|
|
96
|
+
imageUrl,
|
|
97
|
+
metadata: extraMetadata,
|
|
98
|
+
type: auctionType = 'multicurve',
|
|
99
|
+
initialSupply: initialSupplyStr,
|
|
100
|
+
numTokensToSell: numTokensToSellStr,
|
|
101
|
+
preset,
|
|
102
|
+
fee,
|
|
103
|
+
tickSpacing,
|
|
104
|
+
startTick,
|
|
105
|
+
endTick,
|
|
106
|
+
duration,
|
|
107
|
+
epochLength,
|
|
108
|
+
minProceeds: minProceedsStr,
|
|
109
|
+
maxProceeds: maxProceedsStr,
|
|
110
|
+
migration = 'uniswapV2',
|
|
111
|
+
vestingDuration,
|
|
112
|
+
startTime,
|
|
113
|
+
governance = 'default',
|
|
114
|
+
beneficiaries,
|
|
115
|
+
chain,
|
|
116
|
+
description: userDescription,
|
|
117
|
+
} = req.body as LaunchRequest;
|
|
118
|
+
|
|
119
|
+
const auth = req.auth!;
|
|
120
|
+
|
|
121
|
+
// Validate required fields
|
|
122
|
+
if (!from || typeof from !== 'string') {
|
|
123
|
+
res.status(400).json({ error: 'from address is required' });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!name || typeof name !== 'string') {
|
|
128
|
+
res.status(400).json({ error: 'name is required' });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!symbol || typeof symbol !== 'string') {
|
|
133
|
+
res.status(400).json({ error: 'symbol is required' });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (!['static', 'dynamic', 'multicurve'].includes(auctionType)) {
|
|
138
|
+
res.status(400).json({ error: 'type must be "static", "dynamic", or "multicurve"' });
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Get chain config
|
|
143
|
+
const { targetChain, chainConfig } = resolveChain(chain);
|
|
144
|
+
|
|
145
|
+
// Doppler only supports EVM chains
|
|
146
|
+
if (!isSupportedChainId(chainConfig.chainId)) {
|
|
147
|
+
res.status(400).json({
|
|
148
|
+
error: `Doppler does not support chain ${targetChain} (chainId: ${chainConfig.chainId})`
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Determine wallet type and verify ownership
|
|
154
|
+
const hotWallet = await getHotWallet(from);
|
|
155
|
+
const tempWallet = getTempWallet(from);
|
|
156
|
+
|
|
157
|
+
if (!hotWallet && !tempWallet) {
|
|
158
|
+
res.status(404).json({ error: 'Wallet not found' });
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Permission checks
|
|
163
|
+
if (!isAdmin(auth) && !hasAnyPermission(auth.token.permissions, ['launch'])) {
|
|
164
|
+
logger.permissionDenied('launch', auth.token.agentId, '/launch');
|
|
165
|
+
res.status(403).json(buildPermissionDenied('Token does not have launch permission', ['launch'], auth.token.permissions));
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (hotWallet) {
|
|
170
|
+
const canAccess = await tokenCanAccessWallet(auth.tokenHash, auth.token.walletAccess, from);
|
|
171
|
+
if (!isAdmin(auth) && !canAccess) {
|
|
172
|
+
logger.permissionDenied('wallet_access', auth.token.agentId, '/launch');
|
|
173
|
+
res.status(403).json(buildPermissionDenied('Token does not have access to this wallet', ['wallet:access'], auth.token.permissions));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!isUnlocked()) {
|
|
178
|
+
logger.authFailed('Cold wallet locked', '/launch');
|
|
179
|
+
res.status(401).json({ error: 'Cold wallet must be unlocked to launch from hot wallet' });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
} else if (tempWallet) {
|
|
183
|
+
if (!isAdmin(auth) && !hasAnyPermission(auth.token.permissions, ['launch'])) {
|
|
184
|
+
logger.permissionDenied('launch', auth.token.agentId, '/launch');
|
|
185
|
+
res.status(403).json(buildPermissionDenied('Token does not have launch permission', ['launch'], auth.token.permissions));
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Get the wallet's private key for viem
|
|
191
|
+
let privateKey: `0x${string}`;
|
|
192
|
+
|
|
193
|
+
if (hotWallet) {
|
|
194
|
+
const exported = await exportHotWallet(from);
|
|
195
|
+
privateKey = exported.privateKey as `0x${string}`;
|
|
196
|
+
if (!privateKey.startsWith('0x')) {
|
|
197
|
+
privateKey = `0x${privateKey}` as `0x${string}`;
|
|
198
|
+
}
|
|
199
|
+
} else if (tempWallet) {
|
|
200
|
+
privateKey = tempWallet.privateKey as `0x${string}`;
|
|
201
|
+
if (!privateKey.startsWith('0x')) {
|
|
202
|
+
privateKey = `0x${privateKey}` as `0x${string}`;
|
|
203
|
+
}
|
|
204
|
+
} else {
|
|
205
|
+
res.status(404).json({ error: 'Wallet not found' });
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Set up viem clients
|
|
210
|
+
const rpcUrl = await getRpcUrl(targetChain);
|
|
211
|
+
const viemChain = VIEM_CHAINS[targetChain];
|
|
212
|
+
|
|
213
|
+
if (!viemChain) {
|
|
214
|
+
res.status(400).json({ error: `No viem chain config for ${targetChain}` });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const account = privateKeyToAccount(privateKey);
|
|
219
|
+
|
|
220
|
+
const publicClient = createPublicClient({
|
|
221
|
+
chain: viemChain,
|
|
222
|
+
transport: http(rpcUrl),
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const walletClient = createWalletClient({
|
|
226
|
+
chain: viemChain,
|
|
227
|
+
transport: http(rpcUrl),
|
|
228
|
+
account,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Initialize Doppler SDK
|
|
232
|
+
const sdk = new DopplerSDK({
|
|
233
|
+
publicClient,
|
|
234
|
+
walletClient,
|
|
235
|
+
chainId: chainConfig.chainId,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const addresses = getAddresses(chainConfig.chainId);
|
|
239
|
+
const defaultSupply = await getDefault<string>('launch.initial_supply', '1000000000');
|
|
240
|
+
const defaultSellPercent = await getDefault<number>('launch.sell_percent', 90);
|
|
241
|
+
const supplyStr = initialSupplyStr || String(defaultSupply);
|
|
242
|
+
const initialSupply = parseEther(supplyStr);
|
|
243
|
+
const numTokensToSell = numTokensToSellStr
|
|
244
|
+
? parseEther(numTokensToSellStr)
|
|
245
|
+
: (initialSupply * BigInt(defaultSellPercent)) / 100n;
|
|
246
|
+
|
|
247
|
+
// Build tokenURI: explicit tokenURI wins, otherwise build from imageUrl/metadata
|
|
248
|
+
let tokenURI = explicitTokenURI || '';
|
|
249
|
+
if (!tokenURI && (imageUrl || extraMetadata)) {
|
|
250
|
+
const metadataJson: Record<string, unknown> = {
|
|
251
|
+
name,
|
|
252
|
+
symbol,
|
|
253
|
+
...(imageUrl && { image: imageUrl }),
|
|
254
|
+
...extraMetadata,
|
|
255
|
+
};
|
|
256
|
+
const encoded = Buffer.from(JSON.stringify(metadataJson)).toString('base64');
|
|
257
|
+
tokenURI = `data:application/json;base64,${encoded}`;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const tokenConfig = {
|
|
261
|
+
name,
|
|
262
|
+
symbol,
|
|
263
|
+
tokenURI,
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const saleConfig = {
|
|
267
|
+
initialSupply,
|
|
268
|
+
numTokensToSell,
|
|
269
|
+
numeraire: addresses.weth,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// Protocol fee address (integrator for all launches)
|
|
273
|
+
const protocolFeeAddress = getDefaultSync('protocol.fee_address', '0xa931533E0E0cCE34fc0FafB25ea2046d391eCAA5') as `0x${string}`;
|
|
274
|
+
|
|
275
|
+
// Convert beneficiaries from API format to SDK format
|
|
276
|
+
const sdkBeneficiaries = beneficiaries?.map(b => ({
|
|
277
|
+
beneficiary: b.address as `0x${string}`,
|
|
278
|
+
shares: parseEther(b.shares),
|
|
279
|
+
}));
|
|
280
|
+
|
|
281
|
+
let result: {
|
|
282
|
+
tokenAddress?: string;
|
|
283
|
+
poolAddress?: string;
|
|
284
|
+
hookAddress?: string;
|
|
285
|
+
poolId?: string;
|
|
286
|
+
hash?: string;
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
if (auctionType === 'multicurve') {
|
|
290
|
+
const builder = new MulticurveBuilder(chainConfig.chainId)
|
|
291
|
+
.tokenConfig(tokenConfig)
|
|
292
|
+
.saleConfig(saleConfig);
|
|
293
|
+
|
|
294
|
+
// Use preset or manual config
|
|
295
|
+
if (preset) {
|
|
296
|
+
builder.withMarketCapPresets({
|
|
297
|
+
fee: fee ?? FEE_TIERS.LOW,
|
|
298
|
+
presets: [preset],
|
|
299
|
+
...(sdkBeneficiaries && { beneficiaries: sdkBeneficiaries }),
|
|
300
|
+
});
|
|
301
|
+
} else {
|
|
302
|
+
builder.poolConfig({
|
|
303
|
+
fee: fee ?? 0,
|
|
304
|
+
tickSpacing: tickSpacing ?? 8,
|
|
305
|
+
curves: [
|
|
306
|
+
{ tickLower: 0, tickUpper: 240000, numPositions: 10, shares: parseEther('0.5') },
|
|
307
|
+
{ tickLower: 16000, tickUpper: 240000, numPositions: 10, shares: parseEther('0.5') },
|
|
308
|
+
],
|
|
309
|
+
...(sdkBeneficiaries && { beneficiaries: sdkBeneficiaries }),
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (vestingDuration) {
|
|
314
|
+
builder.withVesting({ duration: BigInt(vestingDuration) });
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (startTime) {
|
|
318
|
+
builder.withSchedule({ startTime });
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
builder.withIntegrator(protocolFeeAddress);
|
|
322
|
+
builder.withGovernance({ type: governance === 'noOp' ? 'noOp' : 'default' });
|
|
323
|
+
builder.withMigration({ type: migration as 'uniswapV2' | 'uniswapV3' | 'uniswapV4' | 'noOp' });
|
|
324
|
+
builder.withUserAddress(from as `0x${string}`);
|
|
325
|
+
|
|
326
|
+
const params = builder.build();
|
|
327
|
+
const createResult = await sdk.factory.createMulticurve(params);
|
|
328
|
+
|
|
329
|
+
result = {
|
|
330
|
+
tokenAddress: createResult.tokenAddress,
|
|
331
|
+
poolId: createResult.poolId,
|
|
332
|
+
hash: createResult.transactionHash,
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
} else if (auctionType === 'static') {
|
|
336
|
+
const builder = new StaticAuctionBuilder(chainConfig.chainId)
|
|
337
|
+
.tokenConfig(tokenConfig)
|
|
338
|
+
.saleConfig(saleConfig);
|
|
339
|
+
|
|
340
|
+
if (startTick !== undefined && endTick !== undefined) {
|
|
341
|
+
builder.poolByTicks({
|
|
342
|
+
startTick,
|
|
343
|
+
endTick,
|
|
344
|
+
fee: fee ?? 10000,
|
|
345
|
+
});
|
|
346
|
+
} else {
|
|
347
|
+
builder.poolByTicks({ fee: fee ?? 10000 });
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (vestingDuration) {
|
|
351
|
+
builder.withVesting({ duration: BigInt(vestingDuration) });
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (sdkBeneficiaries) {
|
|
355
|
+
builder.withBeneficiaries(sdkBeneficiaries);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
builder.withIntegrator(protocolFeeAddress);
|
|
359
|
+
builder.withGovernance({ type: governance === 'noOp' ? 'noOp' : 'default' });
|
|
360
|
+
builder.withMigration({ type: migration as 'uniswapV2' | 'uniswapV3' | 'uniswapV4' | 'noOp' });
|
|
361
|
+
builder.withUserAddress(from as `0x${string}`);
|
|
362
|
+
|
|
363
|
+
const params = builder.build();
|
|
364
|
+
const createResult = await sdk.factory.createStaticAuction(params);
|
|
365
|
+
|
|
366
|
+
result = {
|
|
367
|
+
tokenAddress: createResult.tokenAddress,
|
|
368
|
+
poolAddress: createResult.poolAddress,
|
|
369
|
+
hash: createResult.transactionHash,
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
} else if (auctionType === 'dynamic') {
|
|
373
|
+
if (beneficiaries?.length) {
|
|
374
|
+
logEvent({ category: 'system', action: 'launch_warning', description: 'Beneficiaries are not supported for dynamic auctions — ignoring' });
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const builder = new DynamicAuctionBuilder(chainConfig.chainId)
|
|
378
|
+
.tokenConfig(tokenConfig)
|
|
379
|
+
.saleConfig(saleConfig)
|
|
380
|
+
.poolConfig({
|
|
381
|
+
fee: fee ?? 3000,
|
|
382
|
+
tickSpacing: tickSpacing ?? 60,
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
const defaultEpochLength = await getDefault<number>('launch.epoch_length', 3600);
|
|
386
|
+
builder.auctionByTicks({
|
|
387
|
+
duration: duration ?? 7 * DAY_SECONDS,
|
|
388
|
+
epochLength: epochLength ?? defaultEpochLength,
|
|
389
|
+
startTick: startTick ?? -92103,
|
|
390
|
+
endTick: endTick ?? -69080,
|
|
391
|
+
minProceeds: minProceedsStr ? parseEther(minProceedsStr) : parseEther('0.1'),
|
|
392
|
+
maxProceeds: maxProceedsStr ? parseEther(maxProceedsStr) : parseEther('100'),
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
if (vestingDuration) {
|
|
396
|
+
builder.withVesting({ duration: BigInt(vestingDuration) });
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
builder.withIntegrator(protocolFeeAddress);
|
|
400
|
+
builder.withGovernance({ type: governance === 'noOp' ? 'noOp' : 'default' });
|
|
401
|
+
builder.withMigration({ type: migration as 'uniswapV2' | 'uniswapV3' | 'uniswapV4' | 'noOp' });
|
|
402
|
+
builder.withUserAddress(from as `0x${string}`);
|
|
403
|
+
|
|
404
|
+
const params = builder.build();
|
|
405
|
+
const createResult = await sdk.factory.createDynamicAuction(params);
|
|
406
|
+
|
|
407
|
+
result = {
|
|
408
|
+
tokenAddress: createResult.tokenAddress,
|
|
409
|
+
hookAddress: createResult.hookAddress,
|
|
410
|
+
poolId: createResult.poolId,
|
|
411
|
+
hash: createResult.transactionHash,
|
|
412
|
+
};
|
|
413
|
+
} else {
|
|
414
|
+
res.status(400).json({ error: `Unknown auction type: ${auctionType}` });
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Log the launch
|
|
419
|
+
const txHash = result.hash || '';
|
|
420
|
+
const description = userDescription || `Launched ${symbol} (${name}) via Doppler ${auctionType} auction`;
|
|
421
|
+
|
|
422
|
+
await recordTransaction({
|
|
423
|
+
walletAddress: from,
|
|
424
|
+
txHash,
|
|
425
|
+
type: 'launch',
|
|
426
|
+
tokenAddress: result.tokenAddress || undefined,
|
|
427
|
+
from,
|
|
428
|
+
to: result.poolAddress || result.hookAddress || addresses.airlock,
|
|
429
|
+
description,
|
|
430
|
+
chain: targetChain,
|
|
431
|
+
logTitle: `Token Launch: ${symbol}`,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
// Auto-track the launched token
|
|
435
|
+
if (result.tokenAddress) {
|
|
436
|
+
await autoTrackToken({
|
|
437
|
+
walletAddress: from,
|
|
438
|
+
tokenAddress: result.tokenAddress,
|
|
439
|
+
chain: targetChain,
|
|
440
|
+
symbol,
|
|
441
|
+
name,
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Log event
|
|
446
|
+
const agentId = !isAdmin(auth) ? auth.token.agentId : undefined;
|
|
447
|
+
logger.swap(from, 'ETH', symbol, '0', txHash, agentId); // Reuse swap logger for now
|
|
448
|
+
|
|
449
|
+
res.json({
|
|
450
|
+
success: true,
|
|
451
|
+
hash: txHash,
|
|
452
|
+
from,
|
|
453
|
+
tokenAddress: result.tokenAddress,
|
|
454
|
+
...(result.poolAddress && { poolAddress: result.poolAddress }),
|
|
455
|
+
...(result.hookAddress && { hookAddress: result.hookAddress }),
|
|
456
|
+
...(result.poolId && { poolId: result.poolId }),
|
|
457
|
+
type: auctionType,
|
|
458
|
+
name,
|
|
459
|
+
symbol,
|
|
460
|
+
chain: targetChain,
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
} catch (error) {
|
|
464
|
+
if (error instanceof HttpError) { res.status(error.status).json({ error: error.message }); return; }
|
|
465
|
+
res.status(500).json({ error: getErrorMessage(error) });
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// Helper: set up viem clients + Doppler SDK for fee collection
|
|
470
|
+
async function createSdkForWallet(from: string, targetChain: string) {
|
|
471
|
+
const config = loadConfig();
|
|
472
|
+
const chainConfig = config.chains[targetChain];
|
|
473
|
+
if (!chainConfig) throw new Error(`Unknown chain: ${targetChain}`);
|
|
474
|
+
if (!isSupportedChainId(chainConfig.chainId)) {
|
|
475
|
+
throw new Error(`Doppler does not support chain ${targetChain}`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const hotWallet = await getHotWallet(from);
|
|
479
|
+
const tempWallet = getTempWallet(from);
|
|
480
|
+
if (!hotWallet && !tempWallet) throw new Error('Wallet not found');
|
|
481
|
+
|
|
482
|
+
let privateKey: `0x${string}`;
|
|
483
|
+
if (hotWallet) {
|
|
484
|
+
const exported = await exportHotWallet(from);
|
|
485
|
+
privateKey = exported.privateKey as `0x${string}`;
|
|
486
|
+
if (!privateKey.startsWith('0x')) privateKey = `0x${privateKey}` as `0x${string}`;
|
|
487
|
+
} else {
|
|
488
|
+
privateKey = tempWallet!.privateKey as `0x${string}`;
|
|
489
|
+
if (!privateKey.startsWith('0x')) privateKey = `0x${privateKey}` as `0x${string}`;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const rpcUrl = await getRpcUrl(targetChain);
|
|
493
|
+
const viemChain = VIEM_CHAINS[targetChain];
|
|
494
|
+
if (!viemChain) throw new Error(`No viem chain config for ${targetChain}`);
|
|
495
|
+
|
|
496
|
+
const account = privateKeyToAccount(privateKey);
|
|
497
|
+
const publicClient = createPublicClient({ chain: viemChain, transport: http(rpcUrl) });
|
|
498
|
+
const walletClient = createWalletClient({ chain: viemChain, transport: http(rpcUrl), account });
|
|
499
|
+
|
|
500
|
+
const sdk = new DopplerSDK({ publicClient, walletClient, chainId: chainConfig.chainId });
|
|
501
|
+
return { sdk, hotWallet, tempWallet };
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// POST /launch/collect-fees - Collect fees from ALL launched tokens
|
|
505
|
+
// No launch permission required — collectFees is permissionless on-chain,
|
|
506
|
+
// fees always go to configured beneficiaries. Caller only pays gas.
|
|
507
|
+
router.post('/collect-fees', requireWalletAuth, async (req: Request, res: Response) => {
|
|
508
|
+
try {
|
|
509
|
+
const { from, chain } = req.body as { from: string; chain?: string };
|
|
510
|
+
const auth = req.auth!;
|
|
511
|
+
|
|
512
|
+
if (!from || typeof from !== 'string') {
|
|
513
|
+
res.status(400).json({ error: 'from address is required (wallet to pay gas)' });
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const hotWallet = await getHotWallet(from);
|
|
518
|
+
const tempWallet = getTempWallet(from);
|
|
519
|
+
if (!hotWallet && !tempWallet) {
|
|
520
|
+
res.status(404).json({ error: 'Wallet not found' });
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (hotWallet) {
|
|
525
|
+
const canAccess = await tokenCanAccessWallet(auth.tokenHash, auth.token.walletAccess, from);
|
|
526
|
+
if (!isAdmin(auth) && !canAccess) {
|
|
527
|
+
logger.permissionDenied('wallet_access', auth.token.agentId, '/launch/collect-fees');
|
|
528
|
+
res.status(403).json(buildPermissionDenied('Token does not have access to this wallet', ['wallet:access'], auth.token.permissions));
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const config = loadConfig();
|
|
534
|
+
const targetChain = chain || config.defaultChain;
|
|
535
|
+
|
|
536
|
+
// Find all launched tokens on this chain
|
|
537
|
+
const launches = await prisma.transaction.findMany({
|
|
538
|
+
where: {
|
|
539
|
+
type: 'launch',
|
|
540
|
+
chain: targetChain,
|
|
541
|
+
tokenAddress: { not: null },
|
|
542
|
+
},
|
|
543
|
+
select: { tokenAddress: true },
|
|
544
|
+
distinct: ['tokenAddress'],
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
if (launches.length === 0) {
|
|
548
|
+
res.json({ success: true, message: 'No launched tokens found', results: [] });
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const { sdk } = await createSdkForWallet(from, targetChain);
|
|
553
|
+
|
|
554
|
+
const results: Array<{
|
|
555
|
+
tokenAddress: string;
|
|
556
|
+
success: boolean;
|
|
557
|
+
fees0?: string;
|
|
558
|
+
fees1?: string;
|
|
559
|
+
transactionHash?: string;
|
|
560
|
+
error?: string;
|
|
561
|
+
}> = [];
|
|
562
|
+
|
|
563
|
+
for (const launch of launches) {
|
|
564
|
+
const tokenAddr = launch.tokenAddress!;
|
|
565
|
+
try {
|
|
566
|
+
const pool = await sdk.getMulticurvePool(tokenAddr as `0x${string}`);
|
|
567
|
+
const { fees0, fees1, transactionHash } = await pool.collectFees();
|
|
568
|
+
results.push({
|
|
569
|
+
tokenAddress: tokenAddr,
|
|
570
|
+
success: true,
|
|
571
|
+
fees0: fees0.toString(),
|
|
572
|
+
fees1: fees1.toString(),
|
|
573
|
+
transactionHash,
|
|
574
|
+
});
|
|
575
|
+
} catch (err) {
|
|
576
|
+
results.push({
|
|
577
|
+
tokenAddress: tokenAddr,
|
|
578
|
+
success: false,
|
|
579
|
+
error: getErrorMessage(err),
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
const collected = results.filter(r => r.success).length;
|
|
585
|
+
const failed = results.filter(r => !r.success).length;
|
|
586
|
+
|
|
587
|
+
res.json({
|
|
588
|
+
success: true,
|
|
589
|
+
chain: targetChain,
|
|
590
|
+
total: launches.length,
|
|
591
|
+
collected,
|
|
592
|
+
failed,
|
|
593
|
+
results,
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
} catch (error) {
|
|
597
|
+
if (error instanceof HttpError) { res.status(error.status).json({ error: error.message }); return; }
|
|
598
|
+
res.status(500).json({ error: getErrorMessage(error) });
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// POST /launch/:tokenAddress/collect-fees - Collect fees from a specific launched token
|
|
603
|
+
// No launch permission required — collectFees is permissionless on-chain,
|
|
604
|
+
// fees always go to configured beneficiaries. Caller only pays gas.
|
|
605
|
+
router.post('/:tokenAddress/collect-fees', requireWalletAuth, async (req: Request<{ tokenAddress: string }>, res: Response) => {
|
|
606
|
+
try {
|
|
607
|
+
const { tokenAddress } = req.params;
|
|
608
|
+
const { from, chain } = req.body as { from: string; chain?: string };
|
|
609
|
+
const auth = req.auth!;
|
|
610
|
+
|
|
611
|
+
if (!from || typeof from !== 'string') {
|
|
612
|
+
res.status(400).json({ error: 'from address is required (wallet to pay gas)' });
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const hotWallet = await getHotWallet(from);
|
|
617
|
+
const tempWallet = getTempWallet(from);
|
|
618
|
+
if (!hotWallet && !tempWallet) {
|
|
619
|
+
res.status(404).json({ error: 'Wallet not found' });
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (hotWallet) {
|
|
624
|
+
const canAccess = await tokenCanAccessWallet(auth.tokenHash, auth.token.walletAccess, from);
|
|
625
|
+
if (!isAdmin(auth) && !canAccess) {
|
|
626
|
+
logger.permissionDenied('wallet_access', auth.token.agentId, `/launch/${tokenAddress}/collect-fees`);
|
|
627
|
+
res.status(403).json(buildPermissionDenied('Token does not have access to this wallet', ['wallet:access'], auth.token.permissions));
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const config = loadConfig();
|
|
633
|
+
const targetChain = chain || config.defaultChain;
|
|
634
|
+
|
|
635
|
+
const { sdk } = await createSdkForWallet(from, targetChain);
|
|
636
|
+
|
|
637
|
+
const pool = await sdk.getMulticurvePool(tokenAddress as `0x${string}`);
|
|
638
|
+
const { fees0, fees1, transactionHash } = await pool.collectFees();
|
|
639
|
+
|
|
640
|
+
// Log the fee collection
|
|
641
|
+
await prisma.log.create({
|
|
642
|
+
data: {
|
|
643
|
+
walletAddress: from,
|
|
644
|
+
title: `Fee Collection: ${tokenAddress}`,
|
|
645
|
+
description: `Collected fees from launched token ${tokenAddress} (fees0: ${fees0}, fees1: ${fees1})`,
|
|
646
|
+
txHash: transactionHash,
|
|
647
|
+
}
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
res.json({
|
|
651
|
+
success: true,
|
|
652
|
+
tokenAddress,
|
|
653
|
+
fees0: fees0.toString(),
|
|
654
|
+
fees1: fees1.toString(),
|
|
655
|
+
transactionHash,
|
|
656
|
+
chain: targetChain,
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (error instanceof HttpError) { res.status(error.status).json({ error: error.message }); return; }
|
|
661
|
+
res.status(500).json({ error: getErrorMessage(error) });
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
export default router;
|