Price reminder and init approval

* Start price reminder grain

* Add config and init grain at startup

* Save init wallet when already init
This commit is contained in:
Oda
2025-09-13 02:29:14 +07:00
committed by GitHub
parent da50b30344
commit 56b4f14eb3
69 changed files with 2373 additions and 701 deletions

View File

@@ -331,6 +331,41 @@ export class AccountClient extends AuthorizedApiBase {
}
return Promise.resolve<SwapInfos>(null as any);
}
account_GetExchangeApprovalStatus(): Promise<ExchangeApprovalStatus[]> {
let url_ = this.baseUrl + "/Account/exchange-approval-status";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "GET",
headers: {
"Accept": "application/json"
}
};
return this.transformOptions(options_).then(transformedOptions_ => {
return this.http.fetch(url_, transformedOptions_);
}).then((_response: Response) => {
return this.processAccount_GetExchangeApprovalStatus(_response);
});
}
protected processAccount_GetExchangeApprovalStatus(response: Response): Promise<ExchangeApprovalStatus[]> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ExchangeApprovalStatus[];
return result200;
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<ExchangeApprovalStatus[]>(null as any);
}
}
export class BacktestClient extends AuthorizedApiBase {
@@ -3535,6 +3570,7 @@ export interface Account {
secret?: string | null;
user?: User | null;
balances?: Balance[] | null;
isGmxInitialized?: boolean;
isPrivyWallet?: boolean;
}
@@ -3735,6 +3771,11 @@ export interface SendTokenRequest {
chainId?: number | null;
}
export interface ExchangeApprovalStatus {
exchange?: TradingExchanges;
isApproved?: boolean;
}
export interface Backtest {
id: string;
finalPnl: number;
@@ -3984,7 +4025,7 @@ export enum Confidence {
export interface Candle {
exchange: TradingExchanges;
ticker: string;
ticker: Ticker;
openTime: Date;
date: Date;
open: number;
@@ -4073,7 +4114,7 @@ export interface TradingBotConfigRequest {
botTradingBalance: number;
name: string;
flipPosition: boolean;
cooldownPeriod?: number;
cooldownPeriod?: number | null;
maxLossStreak?: number;
scenario?: ScenarioRequest | null;
scenarioName?: string | null;
@@ -4625,6 +4666,8 @@ export interface PrivyInitAddressResponse {
orderVaultHash?: string | null;
exchangeRouterHash?: string | null;
error?: string | null;
address?: string | null;
isAlreadyInitialized?: boolean;
}
export interface LoginRequest {

View File

@@ -18,6 +18,7 @@ export interface Account {
secret?: string | null;
user?: User | null;
balances?: Balance[] | null;
isGmxInitialized?: boolean;
isPrivyWallet?: boolean;
}
@@ -218,6 +219,11 @@ export interface SendTokenRequest {
chainId?: number | null;
}
export interface ExchangeApprovalStatus {
exchange?: TradingExchanges;
isApproved?: boolean;
}
export interface Backtest {
id: string;
finalPnl: number;
@@ -467,7 +473,7 @@ export enum Confidence {
export interface Candle {
exchange: TradingExchanges;
ticker: string;
ticker: Ticker;
openTime: Date;
date: Date;
open: number;
@@ -556,7 +562,7 @@ export interface TradingBotConfigRequest {
botTradingBalance: number;
name: string;
flipPosition: boolean;
cooldownPeriod?: number;
cooldownPeriod?: number | null;
maxLossStreak?: number;
scenario?: ScenarioRequest | null;
scenarioName?: string | null;
@@ -1108,6 +1114,8 @@ export interface PrivyInitAddressResponse {
orderVaultHash?: string | null;
exchangeRouterHash?: string | null;
error?: string | null;
address?: string | null;
isAlreadyInitialized?: boolean;
}
export interface LoginRequest {

View File

@@ -2,12 +2,12 @@ import React, {useState} from 'react'
import {Card, FormInput, GridTile} from '../../components/mollecules'
import useApiUrlStore from '../../app/store/apiStore'
import {
type AgentBalanceHistory,
BotStatus,
DataClient,
type Position,
TradeDirection,
type UserStrategyDetailsViewModel
type AgentBalanceHistory,
BotStatus,
DataClient,
type Position,
TradeDirection,
type UserStrategyDetailsViewModel
} from '../../generated/ManagingApi'
interface AgentData {
@@ -140,7 +140,7 @@ function AgentSearch({ index }: { index: number }) {
totalWins,
totalLosses,
avgWinRate,
activeStrategies: agentData.strategies.filter(s => s.state === 'RUNNING').length,
activeStrategies: agentData.strategies.filter(s => s.state === BotStatus.Running).length,
totalStrategies: agentData.strategies.length
}
}

View File

@@ -1,10 +1,12 @@
import React, {useState} from 'react'
import {FiRefreshCw, FiSend} from 'react-icons/fi'
import {useQuery} from '@tanstack/react-query'
import {SelectColumnFilter, Table} from '../../../components/mollecules'
import type {IAccountRowDetail} from '../../../global/type.tsx'
import type {Account, Balance} from '../../../generated/ManagingApi'
import {Ticker} from '../../../generated/ManagingApi'
import {AccountClient, Ticker} from '../../../generated/ManagingApi'
import useApiUrlStore from '../../../app/store/apiStore'
import SwapModal from './SwapModal'
import SendTokenModal from './SendTokenModal'
@@ -17,6 +19,21 @@ const AccountRowDetails: React.FC<IAccountRowDetailProps> = ({
showTotal,
account,
}) => {
const { apiUrl } = useApiUrlStore()
const accountClient = new AccountClient({}, apiUrl)
// Fetch exchange approval status using TanStack Query
const { data: exchangeApprovalStatus, isLoading: isLoadingApprovalStatus, error: approvalStatusError, refetch: refetchApprovalStatus } = useQuery({
queryKey: ['exchangeApprovalStatus'],
queryFn: async () => {
return await accountClient.account_GetExchangeApprovalStatus()
},
staleTime: 60000, // Consider data fresh for 1 minute
gcTime: 5 * 60 * 1000, // Keep in cache for 5 minutes
retry: 2, // Retry failed requests up to 2 times
enabled: !!apiUrl, // Only run query when apiUrl is available
})
const [swapModalState, setSwapModalState] = useState<{
isOpen: boolean
fromTicker: Ticker | null
@@ -164,6 +181,44 @@ const AccountRowDetails: React.FC<IAccountRowDetailProps> = ({
showTotal={showTotal}
showPagination={false}
/>
{/* Exchange Approval Status */}
<div className="mt-4">
<div className="flex items-center justify-between mb-2">
<h4 className="text-sm font-medium text-gray-700">Exchange Approval Status</h4>
<button
className={`btn btn-xs btn-ghost ${isLoadingApprovalStatus ? 'loading' : ''}`}
onClick={() => refetchApprovalStatus()}
disabled={isLoadingApprovalStatus}
title="Refresh approval status"
>
{!isLoadingApprovalStatus && <FiRefreshCw className="h-3 w-3" />}
{!isLoadingApprovalStatus && <span className="ml-1">Refresh</span>}
</button>
</div>
{isLoadingApprovalStatus ? (
<div className="text-sm text-gray-500">Loading approval status...</div>
) : approvalStatusError ? (
<div className="text-sm text-red-500">Error loading approval status</div>
) : exchangeApprovalStatus && exchangeApprovalStatus.length > 0 ? (
<div className="flex flex-wrap gap-2">
{exchangeApprovalStatus.map((status) => (
<div
key={status.exchange}
className={`badge ${
status.isApproved
? 'badge-success'
: 'badge-outline'
}`}
>
{status.exchange}: {status.isApproved ? 'Approved' : 'Not Approved'}
</div>
))}
</div>
) : (
<div className="text-sm text-gray-500">No exchange data available</div>
)}
</div>
{swapModalState.isOpen && swapModalState.fromTicker && (
<SwapModal