feat: add token search in modal when confirming address
This commit is contained in:
@@ -225,6 +225,7 @@ def chat(
|
|||||||
success=result.get("success", False),
|
success=result.get("success", False),
|
||||||
strategy_needs_confirmation=result.get("strategy_needs_confirmation", False),
|
strategy_needs_confirmation=result.get("strategy_needs_confirmation", False),
|
||||||
strategy_data=result.get("strategy_data") if result.get("strategy_needs_confirmation") else None,
|
strategy_data=result.get("strategy_data") if result.get("strategy_needs_confirmation") else None,
|
||||||
|
token_search_results=result.get("token_search_results"),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ class BotChatResponse(BaseModel):
|
|||||||
success: bool = False
|
success: bool = False
|
||||||
strategy_needs_confirmation: Optional[bool] = False
|
strategy_needs_confirmation: Optional[bool] = False
|
||||||
strategy_data: Optional[dict] = None
|
strategy_data: Optional[dict] = None
|
||||||
|
token_search_results: Optional[List[dict]] = None
|
||||||
|
|
||||||
|
|
||||||
class SignalResponse(BaseModel):
|
class SignalResponse(BaseModel):
|
||||||
|
|||||||
@@ -165,11 +165,42 @@ class ConversationalAgent:
|
|||||||
|
|
||||||
# Check if token_address is missing in strategy_update
|
# Check if token_address is missing in strategy_update
|
||||||
strategy_needs_confirmation = False
|
strategy_needs_confirmation = False
|
||||||
|
token_search_results = None
|
||||||
|
|
||||||
if strategy_update:
|
if strategy_update:
|
||||||
|
# Extract token name from conditions
|
||||||
|
token_name = None
|
||||||
for cond in strategy_update.get("conditions", []):
|
for cond in strategy_update.get("conditions", []):
|
||||||
if not cond.get("token_address"):
|
if not cond.get("token_address") and cond.get("token"):
|
||||||
|
token_name = cond.get("token")
|
||||||
strategy_needs_confirmation = True
|
strategy_needs_confirmation = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Search for token if name is found
|
||||||
|
if strategy_needs_confirmation and token_name:
|
||||||
|
try:
|
||||||
|
from ..ave.client import AveCloudClient
|
||||||
|
from ...core.config import get_settings
|
||||||
|
settings = get_settings()
|
||||||
|
ave_client = AveCloudClient(
|
||||||
|
api_key=settings.AVE_API_KEY,
|
||||||
|
plan=settings.AVE_API_PLAN
|
||||||
|
)
|
||||||
|
# Run async search in sync context
|
||||||
|
import asyncio
|
||||||
|
tokens = asyncio.run(ave_client.get_tokens(query=token_name, chain="bsc", limit=5))
|
||||||
|
if tokens:
|
||||||
|
token_search_results = [
|
||||||
|
{
|
||||||
|
"symbol": t.get("symbol", ""),
|
||||||
|
"name": t.get("name", ""),
|
||||||
|
"address": t.get("id") or t.get("contract_address", ""),
|
||||||
|
"chain": t.get("chain", "bsc")
|
||||||
|
}
|
||||||
|
for t in tokens
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Token search error: {e}")
|
||||||
|
|
||||||
# Only update strategy if token_address is provided
|
# Only update strategy if token_address is provided
|
||||||
if strategy_update and strategy_needs_confirmation:
|
if strategy_update and strategy_needs_confirmation:
|
||||||
@@ -181,6 +212,7 @@ class ConversationalAgent:
|
|||||||
"strategy_updated": False,
|
"strategy_updated": False,
|
||||||
"strategy_needs_confirmation": True,
|
"strategy_needs_confirmation": True,
|
||||||
"strategy_data": strategy_update,
|
"strategy_data": strategy_update,
|
||||||
|
"token_search_results": token_search_results,
|
||||||
"success": True
|
"success": True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -132,4 +132,12 @@ export interface BotChatResponse {
|
|||||||
success: boolean;
|
success: boolean;
|
||||||
strategy_needs_confirmation?: boolean;
|
strategy_needs_confirmation?: boolean;
|
||||||
strategy_data?: StrategyConfig | null;
|
strategy_data?: StrategyConfig | null;
|
||||||
|
token_search_results?: TokenSearchResult[] | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TokenSearchResult {
|
||||||
|
symbol: string;
|
||||||
|
name: string;
|
||||||
|
address: string;
|
||||||
|
chain: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import { isAuthenticated, isLoading, chatStore, addMessage, setMessages, clearChat, currentBotStore, setCurrentBot } from '$lib/stores';
|
import { isAuthenticated, isLoading, chatStore, addMessage, setMessages, clearChat, currentBotStore, setCurrentBot } from '$lib/stores';
|
||||||
import { api } from '$lib/api';
|
import { api } from '$lib/api';
|
||||||
import { ChatInterface, StrategyPreview } from '$lib/components';
|
import { ChatInterface, StrategyPreview } from '$lib/components';
|
||||||
|
import type { TokenSearchResult } from '$lib/api';
|
||||||
|
|
||||||
let botId = $derived($page.params.id);
|
let botId = $derived($page.params.id);
|
||||||
let isSending = $state(false);
|
let isSending = $state(false);
|
||||||
@@ -15,6 +16,7 @@
|
|||||||
let pendingStrategyData = $state<any>(null);
|
let pendingStrategyData = $state<any>(null);
|
||||||
let tokenAddressInput = $state('');
|
let tokenAddressInput = $state('');
|
||||||
let confirmingMessage = $state('');
|
let confirmingMessage = $state('');
|
||||||
|
let tokenSearchResults = $state<TokenSearchResult[]>([]);
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
if (!$isAuthenticated && !$isLoading) {
|
if (!$isAuthenticated && !$isLoading) {
|
||||||
@@ -67,6 +69,7 @@
|
|||||||
pendingStrategyData = response.strategy_data;
|
pendingStrategyData = response.strategy_data;
|
||||||
confirmingMessage = response.response;
|
confirmingMessage = response.response;
|
||||||
tokenAddressInput = '';
|
tokenAddressInput = '';
|
||||||
|
tokenSearchResults = response.token_search_results || [];
|
||||||
showTokenConfirm = true;
|
showTokenConfirm = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,12 +137,18 @@
|
|||||||
showTokenConfirm = false;
|
showTokenConfirm = false;
|
||||||
pendingStrategyData = null;
|
pendingStrategyData = null;
|
||||||
tokenAddressInput = '';
|
tokenAddressInput = '';
|
||||||
|
tokenSearchResults = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectTokenResult(result: TokenSearchResult) {
|
||||||
|
tokenAddressInput = result.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelTokenConfirm() {
|
function cancelTokenConfirm() {
|
||||||
showTokenConfirm = false;
|
showTokenConfirm = false;
|
||||||
pendingStrategyData = null;
|
pendingStrategyData = null;
|
||||||
tokenAddressInput = '';
|
tokenAddressInput = '';
|
||||||
|
tokenSearchResults = [];
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -151,9 +160,23 @@
|
|||||||
{#if showTokenConfirm}
|
{#if showTokenConfirm}
|
||||||
<div class="modal-overlay" onclick={cancelTokenConfirm}>
|
<div class="modal-overlay" onclick={cancelTokenConfirm}>
|
||||||
<div class="modal-content" onclick={(e) => e.stopPropagation()}>
|
<div class="modal-content" onclick={(e) => e.stopPropagation()}>
|
||||||
<h3>Confirm Token Address</h3>
|
<h3>Select Token Address</h3>
|
||||||
<p class="modal-message">{confirmingMessage}</p>
|
<p class="modal-message">{confirmingMessage}</p>
|
||||||
<p class="modal-hint">Please enter the BSC contract address for the token:</p>
|
|
||||||
|
{#if tokenSearchResults.length > 0}
|
||||||
|
<div class="token-results">
|
||||||
|
<p class="modal-hint">Select a token:</p>
|
||||||
|
{#each tokenSearchResults as result}
|
||||||
|
<button class="token-result" onclick={() => selectTokenResult(result)}>
|
||||||
|
<span class="token-symbol">{result.symbol}</span>
|
||||||
|
<span class="token-name">{result.name}</span>
|
||||||
|
<span class="token-address">{result.address.slice(0, 10)}...{result.address.slice(-8)}</span>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
<p class="modal-divider">or enter manually:</p>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<input type="text" class="token-input" bind:value={tokenAddressInput} placeholder="0x..."/>
|
<input type="text" class="token-input" bind:value={tokenAddressInput} placeholder="0x..."/>
|
||||||
<div class="modal-actions">
|
<div class="modal-actions">
|
||||||
<button class="btn btn-secondary" onclick={cancelTokenConfirm}>Cancel</button>
|
<button class="btn btn-secondary" onclick={cancelTokenConfirm}>Cancel</button>
|
||||||
@@ -363,4 +386,61 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Token Results */
|
||||||
|
.token-results {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-result {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
color: #fff;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-result:hover {
|
||||||
|
background: rgba(102, 126, 234, 0.2);
|
||||||
|
border-color: rgba(102, 126, 234, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-result:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-symbol {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #667eea;
|
||||||
|
min-width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-name {
|
||||||
|
flex: 1;
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token-address {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #666;
|
||||||
|
font-family: 'Monaco', 'Menlo', monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-divider {
|
||||||
|
text-align: center;
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user