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,353 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { ethers } from 'ethers';
|
|
3
|
+
import { getHotWallet, signWithHotWallet, tokenCanAccessWallet } from '../lib/hot';
|
|
4
|
+
import { getTempWallet, signWithTempWallet } from '../lib/temp';
|
|
5
|
+
import { isUnlocked } from '../lib/cold';
|
|
6
|
+
import { getRpcUrl } from '../lib/config';
|
|
7
|
+
import { logger } from '../lib/logger';
|
|
8
|
+
import {
|
|
9
|
+
getDexAdapter,
|
|
10
|
+
detectBestDex,
|
|
11
|
+
listDexes,
|
|
12
|
+
PoolKey
|
|
13
|
+
} from '../lib/dex';
|
|
14
|
+
import { getV4PoolKey, getKnownV4Hooks, detectV4PoolFromEvents } from '../lib/dex/uniswap';
|
|
15
|
+
import { isAdmin } from '../lib/permissions';
|
|
16
|
+
import { hasAnyPermission } from '../lib/permissions';
|
|
17
|
+
import { reserveSpend, releaseSpend, getRemainingByType } from '../lib/sessions';
|
|
18
|
+
import { getDefault } from '../lib/defaults';
|
|
19
|
+
import { getNativeAddress } from '../lib/address';
|
|
20
|
+
import { recordTransaction, autoTrackToken } from '../lib/transactions';
|
|
21
|
+
import { AuthInfo } from '../middleware/auth';
|
|
22
|
+
import { ChainConfig } from '../lib/config';
|
|
23
|
+
|
|
24
|
+
export async function handleEvmSwap(
|
|
25
|
+
req: Request,
|
|
26
|
+
res: Response,
|
|
27
|
+
auth: AuthInfo,
|
|
28
|
+
targetChain: string,
|
|
29
|
+
chainConfig: ChainConfig,
|
|
30
|
+
destinationChainId?: number,
|
|
31
|
+
targetChainOut?: string
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
const {
|
|
34
|
+
from,
|
|
35
|
+
token,
|
|
36
|
+
direction,
|
|
37
|
+
amount,
|
|
38
|
+
minOut,
|
|
39
|
+
slippage,
|
|
40
|
+
dex: requestedDex,
|
|
41
|
+
version: requestedVersion,
|
|
42
|
+
poolFee,
|
|
43
|
+
poolKey,
|
|
44
|
+
hook: requestedHook,
|
|
45
|
+
description: userDescription
|
|
46
|
+
} = req.body;
|
|
47
|
+
|
|
48
|
+
const provider = new ethers.JsonRpcProvider(await getRpcUrl(targetChain));
|
|
49
|
+
|
|
50
|
+
// Determine wallet type and verify ownership
|
|
51
|
+
const hotWallet = await getHotWallet(from);
|
|
52
|
+
const tempWallet = getTempWallet(from);
|
|
53
|
+
|
|
54
|
+
if (!hotWallet && !tempWallet) {
|
|
55
|
+
res.status(404).json({ error: 'Wallet not found' });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (hotWallet) {
|
|
60
|
+
// Check permission
|
|
61
|
+
if (!isAdmin(auth) && !hasAnyPermission(auth.token.permissions, ['swap'])) {
|
|
62
|
+
logger.permissionDenied('swap', auth.token.agentId, '/swap');
|
|
63
|
+
res.status(403).json({ error: 'Token does not have swap permission' });
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Verify token can access this wallet
|
|
68
|
+
const canAccess = await tokenCanAccessWallet(auth.tokenHash, auth.token.walletAccess, from);
|
|
69
|
+
if (!isAdmin(auth) && !canAccess) {
|
|
70
|
+
logger.permissionDenied('wallet_access', auth.token.agentId, '/swap');
|
|
71
|
+
res.status(403).json({ error: 'Token does not have access to this wallet' });
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!isUnlocked()) {
|
|
76
|
+
logger.authFailed('Cold wallet locked', '/swap');
|
|
77
|
+
res.status(401).json({ error: 'Cold wallet must be unlocked to send from hot wallet' });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
} else if (tempWallet) {
|
|
81
|
+
// Check permission
|
|
82
|
+
if (!isAdmin(auth) && !hasAnyPermission(auth.token.permissions, ['swap'])) {
|
|
83
|
+
logger.permissionDenied('swap', auth.token.agentId, '/swap');
|
|
84
|
+
res.status(403).json({ error: 'Token does not have swap permission' });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Reserve spending atomically (prevents TOCTOU race between concurrent requests)
|
|
90
|
+
const currency = getNativeAddress(targetChain);
|
|
91
|
+
const evmSwapAmount = parseFloat(ethers.formatEther(BigInt(amount)));
|
|
92
|
+
const needsEvmLimit = !isAdmin(auth) && evmSwapAmount > 0;
|
|
93
|
+
if (needsEvmLimit) {
|
|
94
|
+
const reserve = reserveSpend(auth.tokenHash, auth.token, 'swap', evmSwapAmount, currency);
|
|
95
|
+
if (!reserve.ok) {
|
|
96
|
+
const remaining = getRemainingByType(auth.tokenHash, auth.token, 'swap', currency);
|
|
97
|
+
logger.limitExceeded(auth.token.agentId, 'swap', evmSwapAmount, remaining);
|
|
98
|
+
res.status(403).json({ error: 'Amount exceeds remaining swap limit', remaining: reserve.remaining, requested: evmSwapAmount });
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Helper to roll back reserved spend on early exit or error
|
|
104
|
+
const evmRollback = () => {
|
|
105
|
+
if (needsEvmLimit) releaseSpend(auth.tokenHash, 'swap', evmSwapAmount, currency);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// Get DEX adapter
|
|
109
|
+
let adapter;
|
|
110
|
+
let detectedPool;
|
|
111
|
+
let version = requestedVersion;
|
|
112
|
+
let detectedFee = poolFee;
|
|
113
|
+
let detectedPoolKey = poolKey;
|
|
114
|
+
|
|
115
|
+
if (requestedDex) {
|
|
116
|
+
// Use specified DEX
|
|
117
|
+
adapter = getDexAdapter(requestedDex);
|
|
118
|
+
if (!adapter) {
|
|
119
|
+
evmRollback();
|
|
120
|
+
res.status(400).json({
|
|
121
|
+
error: `Unknown DEX: ${requestedDex}. Available: ${listDexes().join(', ')}`
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!adapter.supportsChain(chainConfig.chainId)) {
|
|
127
|
+
evmRollback();
|
|
128
|
+
res.status(400).json({ error: `${requestedDex} not supported on ${targetChain}` });
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Detect pool if version not specified
|
|
133
|
+
if (!version) {
|
|
134
|
+
detectedPool = await adapter.detectPool(token, provider);
|
|
135
|
+
if (!detectedPool) {
|
|
136
|
+
evmRollback();
|
|
137
|
+
res.status(400).json({ error: `No ${requestedDex} pool found for this token` });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
version = detectedPool.version;
|
|
141
|
+
detectedFee = detectedPool.fee;
|
|
142
|
+
detectedPoolKey = detectedPool.poolKey;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
// Auto-detect best DEX
|
|
146
|
+
const result = await detectBestDex(token, provider, chainConfig.chainId);
|
|
147
|
+
if (!result) {
|
|
148
|
+
evmRollback();
|
|
149
|
+
res.status(400).json({ error: 'No liquidity pool found for this token' });
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
adapter = result.adapter;
|
|
153
|
+
detectedPool = result.pool;
|
|
154
|
+
// Only use detected version if user didn't specify one
|
|
155
|
+
if (!version) {
|
|
156
|
+
version = detectedPool.version;
|
|
157
|
+
}
|
|
158
|
+
if (!detectedFee) {
|
|
159
|
+
detectedFee = detectedPool.fee;
|
|
160
|
+
}
|
|
161
|
+
if (!detectedPoolKey) {
|
|
162
|
+
detectedPoolKey = detectedPool.poolKey;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// V4 requires poolKey (Uniswap-specific, skip for aggregators like Relay)
|
|
167
|
+
if (adapter.name === 'uniswap' && version === 'v4' && !detectedPoolKey) {
|
|
168
|
+
// 1. Try specified hook first
|
|
169
|
+
if (requestedHook) {
|
|
170
|
+
detectedPoolKey = getV4PoolKey(token, requestedHook);
|
|
171
|
+
if (!detectedPoolKey) {
|
|
172
|
+
evmRollback();
|
|
173
|
+
res.status(400).json({
|
|
174
|
+
error: `Unknown hook: ${requestedHook}. Known hooks: ${getKnownV4Hooks().join(', ')}, none`
|
|
175
|
+
});
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// 2. Try known hooks (clanker, zora, etc.)
|
|
181
|
+
if (!detectedPoolKey) {
|
|
182
|
+
for (const hookName of getKnownV4Hooks()) {
|
|
183
|
+
detectedPoolKey = getV4PoolKey(token, hookName);
|
|
184
|
+
if (detectedPoolKey) break;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 3. Try no-hook pools with common fee/tickSpacing combos
|
|
189
|
+
if (!detectedPoolKey) {
|
|
190
|
+
detectedPoolKey = getV4PoolKey(token, 'none');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 4. Event lookup fallback (slow, requires RPC)
|
|
194
|
+
if (!detectedPoolKey) {
|
|
195
|
+
detectedPoolKey = await detectV4PoolFromEvents(token, provider);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!detectedPoolKey) {
|
|
199
|
+
evmRollback();
|
|
200
|
+
res.status(400).json({
|
|
201
|
+
error: `V4 pool not found. Provide poolKey or specify a known hook. Known hooks: ${getKnownV4Hooks().join(', ')}, none`
|
|
202
|
+
});
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Enforce slippage floors
|
|
208
|
+
const minSlippage = isAdmin(auth)
|
|
209
|
+
? await getDefault<number>('swap.min_slippage_admin', 0.5)
|
|
210
|
+
: await getDefault<number>('swap.min_slippage_agent', 1.0);
|
|
211
|
+
let effectiveSlippage = slippage;
|
|
212
|
+
if (effectiveSlippage !== undefined && effectiveSlippage !== null) {
|
|
213
|
+
if (effectiveSlippage < minSlippage) {
|
|
214
|
+
effectiveSlippage = minSlippage;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Calculate finalMinOut — amount is already in wei, use BigInt directly
|
|
219
|
+
let finalMinOut: string;
|
|
220
|
+
if (effectiveSlippage !== undefined && effectiveSlippage !== null) {
|
|
221
|
+
const amountBig = BigInt(amount);
|
|
222
|
+
const slippageBps = BigInt(Math.floor(effectiveSlippage * 100)); // basis points
|
|
223
|
+
const floorMinOut = amountBig - (amountBig * slippageBps / 10000n);
|
|
224
|
+
|
|
225
|
+
if (minOut && minOut !== '0') {
|
|
226
|
+
// Caller provided explicit minOut - enforce it's not below the slippage floor
|
|
227
|
+
const explicitMinOut = BigInt(minOut);
|
|
228
|
+
if (explicitMinOut < floorMinOut) {
|
|
229
|
+
evmRollback();
|
|
230
|
+
res.status(400).json({
|
|
231
|
+
error: `minOut too low: ${minOut} is below the ${effectiveSlippage}% slippage floor (min: ${floorMinOut.toString()})`
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
finalMinOut = minOut;
|
|
236
|
+
} else {
|
|
237
|
+
finalMinOut = floorMinOut.toString();
|
|
238
|
+
}
|
|
239
|
+
} else if (minOut && minOut !== '0') {
|
|
240
|
+
// No slippage param but explicit minOut - validate against the floor slippage
|
|
241
|
+
const amountBig = BigInt(amount);
|
|
242
|
+
const floorBps = BigInt(Math.floor(minSlippage * 100));
|
|
243
|
+
const floorMinOut = amountBig - (amountBig * floorBps / 10000n);
|
|
244
|
+
const explicitMinOut = BigInt(minOut);
|
|
245
|
+
if (explicitMinOut < floorMinOut) {
|
|
246
|
+
evmRollback();
|
|
247
|
+
res.status(400).json({
|
|
248
|
+
error: `minOut too low: ${minOut} is below the ${minSlippage}% slippage floor (min: ${floorMinOut.toString()})`
|
|
249
|
+
});
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
finalMinOut = minOut;
|
|
253
|
+
} else {
|
|
254
|
+
evmRollback();
|
|
255
|
+
res.status(400).json({ error: 'slippage is required (percentage, e.g. 1.0 for 1%)' });
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Build the swap transaction
|
|
260
|
+
const swapTxData = await adapter.buildSwapTx({
|
|
261
|
+
token,
|
|
262
|
+
direction,
|
|
263
|
+
amount,
|
|
264
|
+
minOut: finalMinOut,
|
|
265
|
+
from,
|
|
266
|
+
chainId: chainConfig.chainId,
|
|
267
|
+
destinationChainId,
|
|
268
|
+
version,
|
|
269
|
+
fee: detectedFee,
|
|
270
|
+
poolKey: detectedPoolKey
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Build transaction object
|
|
274
|
+
const tx: ethers.TransactionRequest = {
|
|
275
|
+
from,
|
|
276
|
+
to: swapTxData.to,
|
|
277
|
+
data: swapTxData.data,
|
|
278
|
+
value: BigInt(swapTxData.value)
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// Sign and send
|
|
282
|
+
let txHash: string;
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
if (hotWallet) {
|
|
286
|
+
const result = await signWithHotWallet(from, tx, provider);
|
|
287
|
+
txHash = result.hash;
|
|
288
|
+
} else if (tempWallet) {
|
|
289
|
+
txHash = await signWithTempWallet(from, tx, provider);
|
|
290
|
+
} else {
|
|
291
|
+
evmRollback();
|
|
292
|
+
res.status(404).json({ error: 'Wallet not found' });
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
} catch (err) {
|
|
296
|
+
evmRollback();
|
|
297
|
+
throw err;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Log the swap
|
|
301
|
+
const versionStr = version ? version.toUpperCase() : '';
|
|
302
|
+
const description = userDescription || (direction === 'buy'
|
|
303
|
+
? `Bought ${token} with ${amount} ETH via ${adapter.name} ${versionStr}`
|
|
304
|
+
: `Sold ${amount} tokens of ${token} for ETH via ${adapter.name} ${versionStr}`);
|
|
305
|
+
|
|
306
|
+
await recordTransaction({
|
|
307
|
+
walletAddress: from,
|
|
308
|
+
txHash,
|
|
309
|
+
type: 'swap',
|
|
310
|
+
amount: direction === 'buy' ? amount : undefined,
|
|
311
|
+
tokenAddress: token,
|
|
312
|
+
tokenAmount: direction === 'sell' ? amount : undefined,
|
|
313
|
+
from,
|
|
314
|
+
to: swapTxData.to || adapter.getRouterAddress(),
|
|
315
|
+
description,
|
|
316
|
+
chain: targetChain,
|
|
317
|
+
logTitle: `Swap ${direction === 'buy' ? 'Buy' : 'Sell'}`,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Auto-track the swapped token (save pool info for price lookup)
|
|
321
|
+
await autoTrackToken({
|
|
322
|
+
walletAddress: from,
|
|
323
|
+
tokenAddress: token,
|
|
324
|
+
chain: targetChain,
|
|
325
|
+
poolAddress: detectedPool?.poolAddress,
|
|
326
|
+
poolVersion: detectedPool?.version,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Log swap event
|
|
330
|
+
const agentId = !isAdmin(auth) ? auth.token.agentId : undefined;
|
|
331
|
+
const fromToken = direction === 'buy' ? 'ETH' : token.slice(0, 10);
|
|
332
|
+
const toToken = direction === 'buy' ? token.slice(0, 10) : 'ETH';
|
|
333
|
+
logger.swap(from, fromToken, toToken, amount, txHash, agentId);
|
|
334
|
+
|
|
335
|
+
const remaining = isAdmin(auth)
|
|
336
|
+
? Infinity
|
|
337
|
+
: getRemainingByType(auth.tokenHash, auth.token, 'swap', currency);
|
|
338
|
+
|
|
339
|
+
res.json({
|
|
340
|
+
success: true,
|
|
341
|
+
hash: txHash,
|
|
342
|
+
from,
|
|
343
|
+
token,
|
|
344
|
+
direction,
|
|
345
|
+
amountIn: amount,
|
|
346
|
+
dex: adapter.name,
|
|
347
|
+
version,
|
|
348
|
+
chain: targetChain,
|
|
349
|
+
...(targetChainOut && { chainOut: targetChainOut }),
|
|
350
|
+
router: adapter.getRouterAddress(),
|
|
351
|
+
remaining
|
|
352
|
+
});
|
|
353
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { PublicKey, VersionedTransaction } from '@solana/web3.js';
|
|
3
|
+
import { getHotWallet, tokenCanAccessWallet } from '../lib/hot';
|
|
4
|
+
import { hasTempWallet, getTempSolanaKeypair } from '../lib/temp';
|
|
5
|
+
import { isUnlocked } from '../lib/cold';
|
|
6
|
+
import { logger } from '../lib/logger';
|
|
7
|
+
import { isAdmin } from '../lib/permissions';
|
|
8
|
+
import { hasAnyPermission } from '../lib/permissions';
|
|
9
|
+
import { reserveSpend, releaseSpend, getRemainingByType } from '../lib/sessions';
|
|
10
|
+
import { getDefaultSync } from '../lib/defaults';
|
|
11
|
+
import { getNativeAddress, getNativeCurrency, NATIVE_ADDRESSES } from '../lib/address';
|
|
12
|
+
import { getSolanaConnection } from '../lib/solana/connection';
|
|
13
|
+
import { getSolanaKeypair } from '../lib/solana/wallet';
|
|
14
|
+
import { recordTransaction, autoTrackToken } from '../lib/transactions';
|
|
15
|
+
import { executeJupiterSwap } from '../lib/solana/jupiter';
|
|
16
|
+
import { AuthInfo } from '../middleware/auth';
|
|
17
|
+
import { ChainConfig } from '../lib/config';
|
|
18
|
+
|
|
19
|
+
export async function handleSolanaSwap(
|
|
20
|
+
req: Request,
|
|
21
|
+
res: Response,
|
|
22
|
+
auth: AuthInfo,
|
|
23
|
+
targetChain: string,
|
|
24
|
+
_chainConfig: ChainConfig
|
|
25
|
+
): Promise<void> {
|
|
26
|
+
const { from, token, direction, amount, slippage, chainOut, description: userDescription } = req.body;
|
|
27
|
+
|
|
28
|
+
if (chainOut) {
|
|
29
|
+
res.status(400).json({ error: 'Cross-chain swaps are not supported on Solana' });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const currency = getNativeAddress(targetChain);
|
|
34
|
+
const nativeCurrency = getNativeCurrency(targetChain);
|
|
35
|
+
|
|
36
|
+
// Determine wallet
|
|
37
|
+
const hotWallet = await getHotWallet(from);
|
|
38
|
+
const isTempWallet = hasTempWallet(from);
|
|
39
|
+
|
|
40
|
+
if (!hotWallet && !isTempWallet) {
|
|
41
|
+
res.status(404).json({ error: 'Wallet not found' });
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Permission checks
|
|
46
|
+
if (!isAdmin(auth) && !hasAnyPermission(auth.token.permissions, ['swap'])) {
|
|
47
|
+
logger.permissionDenied('swap', auth.token.agentId, '/swap');
|
|
48
|
+
res.status(403).json({ error: 'Token does not have swap permission' });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (hotWallet) {
|
|
53
|
+
const canAccess = await tokenCanAccessWallet(auth.tokenHash, auth.token.walletAccess, from, targetChain);
|
|
54
|
+
if (!isAdmin(auth) && !canAccess) {
|
|
55
|
+
logger.permissionDenied('wallet_access', auth.token.agentId, '/swap');
|
|
56
|
+
res.status(403).json({ error: 'Token does not have access to this wallet' });
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!isUnlocked()) {
|
|
60
|
+
logger.authFailed('Cold wallet locked', '/swap');
|
|
61
|
+
res.status(401).json({ error: 'Cold wallet must be unlocked to send from hot wallet' });
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Reserve spending atomically (prevents TOCTOU race between concurrent requests)
|
|
67
|
+
const swapAmountSol = Number(BigInt(amount)) / 1e9;
|
|
68
|
+
const needsSolLimit = !isAdmin(auth) && swapAmountSol > 0;
|
|
69
|
+
if (needsSolLimit) {
|
|
70
|
+
const reserve = reserveSpend(auth.tokenHash, auth.token, 'swap', swapAmountSol, currency);
|
|
71
|
+
if (!reserve.ok) {
|
|
72
|
+
logger.limitExceeded(auth.token.agentId, 'swap', swapAmountSol, reserve.remaining);
|
|
73
|
+
res.status(403).json({ error: 'Amount exceeds remaining swap limit', remaining: reserve.remaining, requested: swapAmountSol });
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Jupiter swap
|
|
79
|
+
const connection = await getSolanaConnection(targetChain);
|
|
80
|
+
const inputMint = direction === 'buy' ? NATIVE_ADDRESSES.SOL : token;
|
|
81
|
+
const outputMint = direction === 'buy' ? token : NATIVE_ADDRESSES.SOL;
|
|
82
|
+
|
|
83
|
+
// Amount is already in lamports (buy) or raw token amount (sell)
|
|
84
|
+
const amountRaw = amount;
|
|
85
|
+
|
|
86
|
+
// Calculate slippage in bps
|
|
87
|
+
const effectiveSlippage = slippage ?? getDefaultSync<number>('swap.min_slippage_agent', 1.0);
|
|
88
|
+
const slippageBps = Math.round(effectiveSlippage * 100);
|
|
89
|
+
|
|
90
|
+
let userPubkey: PublicKey;
|
|
91
|
+
try {
|
|
92
|
+
userPubkey = new PublicKey(from);
|
|
93
|
+
} catch {
|
|
94
|
+
if (needsSolLimit) releaseSpend(auth.tokenHash, 'swap', swapAmountSol, currency);
|
|
95
|
+
res.status(400).json({ error: 'Invalid Solana address' });
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Get signer
|
|
100
|
+
let signerKeypair;
|
|
101
|
+
if (hotWallet) {
|
|
102
|
+
signerKeypair = await getSolanaKeypair(from);
|
|
103
|
+
} else {
|
|
104
|
+
signerKeypair = getTempSolanaKeypair(from);
|
|
105
|
+
if (!signerKeypair) {
|
|
106
|
+
if (needsSolLimit) releaseSpend(auth.tokenHash, 'swap', swapAmountSol, currency);
|
|
107
|
+
res.status(404).json({ error: 'Temp Solana wallet not found' });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let signature: string;
|
|
113
|
+
try {
|
|
114
|
+
const result = await executeJupiterSwap(
|
|
115
|
+
connection,
|
|
116
|
+
inputMint,
|
|
117
|
+
outputMint,
|
|
118
|
+
amountRaw,
|
|
119
|
+
slippageBps,
|
|
120
|
+
userPubkey,
|
|
121
|
+
async (tx: VersionedTransaction) => {
|
|
122
|
+
tx.sign([signerKeypair!]);
|
|
123
|
+
return tx;
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
signature = result.signature;
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (needsSolLimit) releaseSpend(auth.tokenHash, 'swap', swapAmountSol, currency);
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Log
|
|
133
|
+
const description = userDescription || (direction === 'buy'
|
|
134
|
+
? `Bought ${token} with ${amount} ${nativeCurrency} via Jupiter`
|
|
135
|
+
: `Sold ${amount} tokens of ${token} for ${nativeCurrency} via Jupiter`);
|
|
136
|
+
|
|
137
|
+
await recordTransaction({
|
|
138
|
+
walletAddress: from,
|
|
139
|
+
txHash: signature,
|
|
140
|
+
type: 'swap',
|
|
141
|
+
amount: direction === 'buy' ? amount : undefined,
|
|
142
|
+
tokenAddress: token,
|
|
143
|
+
tokenAmount: direction === 'sell' ? amount : undefined,
|
|
144
|
+
from,
|
|
145
|
+
to: 'jupiter',
|
|
146
|
+
description,
|
|
147
|
+
chain: targetChain,
|
|
148
|
+
logTitle: `Swap ${direction === 'buy' ? 'Buy' : 'Sell'}`,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
await autoTrackToken({
|
|
152
|
+
walletAddress: from,
|
|
153
|
+
tokenAddress: token,
|
|
154
|
+
chain: targetChain,
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
const agentId = !isAdmin(auth) ? auth.token.agentId : undefined;
|
|
158
|
+
const fromToken = direction === 'buy' ? nativeCurrency : token.slice(0, 10);
|
|
159
|
+
const toToken = direction === 'buy' ? token.slice(0, 10) : nativeCurrency;
|
|
160
|
+
logger.swap(from, fromToken, toToken, amount, signature, agentId);
|
|
161
|
+
|
|
162
|
+
const remaining = isAdmin(auth)
|
|
163
|
+
? Infinity
|
|
164
|
+
: getRemainingByType(auth.tokenHash, auth.token, 'swap', currency);
|
|
165
|
+
|
|
166
|
+
res.json({
|
|
167
|
+
success: true,
|
|
168
|
+
hash: signature,
|
|
169
|
+
from,
|
|
170
|
+
token,
|
|
171
|
+
direction,
|
|
172
|
+
amountIn: amount,
|
|
173
|
+
dex: 'jupiter',
|
|
174
|
+
chain: targetChain,
|
|
175
|
+
remaining
|
|
176
|
+
});
|
|
177
|
+
}
|