Fix swap btc + fix bot stoping
This commit is contained in:
@@ -24,7 +24,7 @@ public class SwapTokensRequest
|
|||||||
/// The amount to swap
|
/// The amount to swap
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
[Range(0.000001, double.MaxValue, ErrorMessage = "Amount must be greater than 0")]
|
[Range(0.0000000000001, double.MaxValue, ErrorMessage = "Amount must be greater than 0")]
|
||||||
public double Amount { get; set; }
|
public double Amount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ public class TradingBot : Bot, ITradingBot
|
|||||||
{
|
{
|
||||||
// Check broker balance before running
|
// Check broker balance before running
|
||||||
var balance = await ExchangeService.GetBalance(Account, false);
|
var balance = await ExchangeService.GetBalance(Account, false);
|
||||||
if (balance < Constants.GMX.Config.MinimumPositionAmount)
|
if (balance < Constants.GMX.Config.MinimumPositionAmount && Positions.All(p => p.IsFinished()))
|
||||||
{
|
{
|
||||||
await LogWarning(
|
await LogWarning(
|
||||||
$"Balance on broker is below {Constants.GMX.Config.MinimumPositionAmount} USD (actual: {balance}). Stopping bot {Identifier} and saving backup.");
|
$"Balance on broker is below {Constants.GMX.Config.MinimumPositionAmount} USD (actual: {balance}). Stopping bot {Identifier} and saving backup.");
|
||||||
|
|||||||
@@ -24,21 +24,30 @@ interface SwapFormInput {
|
|||||||
allowedSlippage: number
|
allowedSlippage: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ValidationErrorResponse {
|
||||||
|
type: string
|
||||||
|
title: string
|
||||||
|
status: number
|
||||||
|
errors: Record<string, string[]>
|
||||||
|
traceId: string
|
||||||
|
}
|
||||||
|
|
||||||
const SwapModal: React.FC<SwapModalProps> = ({
|
const SwapModal: React.FC<SwapModalProps> = ({
|
||||||
isOpen,
|
isOpen,
|
||||||
onClose,
|
onClose,
|
||||||
account,
|
account,
|
||||||
fromTicker,
|
fromTicker,
|
||||||
availableAmount,
|
availableAmount,
|
||||||
}) => {
|
}) => {
|
||||||
const [isLoading, setIsLoading] = useState(false)
|
const [isLoading, setIsLoading] = useState(false)
|
||||||
const { error, setError, handleApiErrorWithToast } = useApiError()
|
const [validationErrors, setValidationErrors] = useState<Record<string, string[]>>({})
|
||||||
const { apiUrl } = useApiUrlStore()
|
const {error, setError, handleApiErrorWithToast} = useApiError()
|
||||||
|
const {apiUrl} = useApiUrlStore()
|
||||||
const client = new AccountClient({}, apiUrl)
|
const client = new AccountClient({}, apiUrl)
|
||||||
const [selectedToTicker, setSelectedToTicker] = useState<Ticker>(Ticker.USDC)
|
const [selectedToTicker, setSelectedToTicker] = useState<Ticker>(Ticker.USDC)
|
||||||
const [selectedOrderType, setSelectedOrderType] = useState<string>('market')
|
const [selectedOrderType, setSelectedOrderType] = useState<string>('market')
|
||||||
|
|
||||||
const { register, handleSubmit, watch, setValue } = useForm<SwapFormInput>({
|
const {register, handleSubmit, watch, setValue} = useForm<SwapFormInput>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
fromTicker: fromTicker,
|
fromTicker: fromTicker,
|
||||||
toTicker: Ticker.USDC,
|
toTicker: Ticker.USDC,
|
||||||
@@ -62,6 +71,7 @@ const SwapModal: React.FC<SwapModalProps> = ({
|
|||||||
const t = new Toast(`Swapping ${form.amount} ${form.fromTicker} to ${form.toTicker} on ${account.name}`)
|
const t = new Toast(`Swapping ${form.amount} ${form.fromTicker} to ${form.toTicker} on ${account.name}`)
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
|
setValidationErrors({})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await client.account_SwapGmxTokens(
|
const result = await client.account_SwapGmxTokens(
|
||||||
@@ -85,16 +95,31 @@ const SwapModal: React.FC<SwapModalProps> = ({
|
|||||||
setError(errorMessage)
|
setError(errorMessage)
|
||||||
t.update('error', `Swap failed: ${errorMessage}`)
|
t.update('error', `Swap failed: ${errorMessage}`)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err: any) {
|
||||||
|
// Handle validation errors from API
|
||||||
|
if (err.response?.data && typeof err.response.data === 'object') {
|
||||||
|
const errorData = err.response.data as ValidationErrorResponse
|
||||||
|
console.log(errorData)
|
||||||
|
if (errorData.errors && typeof errorData.errors === 'object') {
|
||||||
|
setValidationErrors(errorData.errors)
|
||||||
|
const errorMessages = Object.values(errorData.errors).flat()
|
||||||
|
const errorMessage = errorMessages.join(', ')
|
||||||
|
setError(errorMessage)
|
||||||
|
t.update('error', `Validation failed: ${errorMessage}`)
|
||||||
|
} else {
|
||||||
handleApiErrorWithToast(err, t)
|
handleApiErrorWithToast(err, t)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleApiErrorWithToast(err, t)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleFormSubmit = (e: React.FormEvent) => {
|
const handleFormSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
handleSubmit(onSubmit)(e)
|
await handleSubmit(onSubmit)(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const modalContent = (
|
const modalContent = (
|
||||||
@@ -120,6 +145,18 @@ const SwapModal: React.FC<SwapModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form onSubmit={handleFormSubmit}>
|
<form onSubmit={handleFormSubmit}>
|
||||||
|
{Object.keys(validationErrors).length > 0 && (
|
||||||
|
<div className="alert alert-error mb-4">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold">Validation Errors:</h4>
|
||||||
|
{Object.entries(validationErrors).map(([field, errors]) => (
|
||||||
|
<div key={field} className="mt-1">
|
||||||
|
<strong>{field}:</strong> {errors.join(', ')}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="space-y-4 mb-4">
|
<div className="space-y-4 mb-4">
|
||||||
<FormInput label="To Ticker" htmlFor="toTicker">
|
<FormInput label="To Ticker" htmlFor="toTicker">
|
||||||
<select
|
<select
|
||||||
@@ -147,20 +184,27 @@ const SwapModal: React.FC<SwapModalProps> = ({
|
|||||||
type="number"
|
type="number"
|
||||||
step="any"
|
step="any"
|
||||||
placeholder="Enter amount to swap"
|
placeholder="Enter amount to swap"
|
||||||
className="input input-bordered w-full mb-2"
|
className={`input input-bordered w-full mb-2 ${validationErrors.Amount ? 'input-error' : ''}`}
|
||||||
{...register('amount', {
|
{...register('amount', {
|
||||||
valueAsNumber: true,
|
valueAsNumber: true,
|
||||||
min: 0.0001,
|
min: 0.00000000000001,
|
||||||
max: availableAmount,
|
max: availableAmount,
|
||||||
required: true
|
required: true
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
{validationErrors.Amount && (
|
||||||
|
<div className="text-error text-xs mt-1">
|
||||||
|
{validationErrors.Amount.map((error, index) => (
|
||||||
|
<div key={index}>{error}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
max={availableAmount}
|
max={availableAmount}
|
||||||
step={availableAmount / 100}
|
step={0.00000000000001}
|
||||||
className="range range-primary w-full"
|
className="range range-primary w-full"
|
||||||
value={watchedAmount || 0}
|
value={watchedAmount || 0}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
@@ -201,7 +245,7 @@ const SwapModal: React.FC<SwapModalProps> = ({
|
|||||||
type="number"
|
type="number"
|
||||||
step="0.1"
|
step="0.1"
|
||||||
placeholder="0.5"
|
placeholder="0.5"
|
||||||
className="input input-bordered w-full"
|
className={`input input-bordered w-full ${validationErrors.AllowedSlippage ? 'input-error' : ''}`}
|
||||||
{...register('allowedSlippage', {
|
{...register('allowedSlippage', {
|
||||||
valueAsNumber: true,
|
valueAsNumber: true,
|
||||||
min: 0.1,
|
min: 0.1,
|
||||||
@@ -209,6 +253,13 @@ const SwapModal: React.FC<SwapModalProps> = ({
|
|||||||
value: 0.5
|
value: 0.5
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
{validationErrors.AllowedSlippage && (
|
||||||
|
<div className="text-error text-xs mt-1">
|
||||||
|
{validationErrors.AllowedSlippage.map((error, index) => (
|
||||||
|
<div key={index}>{error}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</FormInput>
|
</FormInput>
|
||||||
|
|
||||||
{selectedOrderType === 'limit' && (
|
{selectedOrderType === 'limit' && (
|
||||||
@@ -217,9 +268,16 @@ const SwapModal: React.FC<SwapModalProps> = ({
|
|||||||
type="number"
|
type="number"
|
||||||
step="any"
|
step="any"
|
||||||
placeholder="Enter trigger ratio"
|
placeholder="Enter trigger ratio"
|
||||||
className="input input-bordered w-full"
|
className={`input input-bordered w-full ${validationErrors.TriggerRatio ? 'input-error' : ''}`}
|
||||||
{...register('triggerRatio', { valueAsNumber: true })}
|
{...register('triggerRatio', {valueAsNumber: true})}
|
||||||
/>
|
/>
|
||||||
|
{validationErrors.TriggerRatio && (
|
||||||
|
<div className="text-error text-xs mt-1">
|
||||||
|
{validationErrors.TriggerRatio.map((error, index) => (
|
||||||
|
<div key={index}>{error}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</FormInput>
|
</FormInput>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user