diff --git a/src/core/intent-request-controller.mjs b/src/core/intent-request-controller.mjs index 4d29265..2658b25 100644 --- a/src/core/intent-request-controller.mjs +++ b/src/core/intent-request-controller.mjs @@ -50,11 +50,13 @@ export function createIntentRequestController({ ); const slippageBps = Number(body.slippage_bps ?? requestPair.slippageBps ?? config.intentRequestDefaultSlippageBps ?? 200); const minDeadlineMs = Number(body.min_deadline_ms || requestPair.minDeadlineMs || config.intentRequestMinDeadlineMs || 60_000); - const maxAmountUnits = parseDecimalToUnits( - String(requestPair.requestMaxNotional || config.intentRequestMaxAmountEure || 5), - sourceAsset.decimals, - { field: 'intent_request_max_notional' }, - ); + const maxAmountUnits = requestPair.requestMaxNotional == null + ? null + : parseDecimalToUnits( + String(requestPair.requestMaxNotional), + sourceAsset.decimals, + { field: 'intent_request_max_notional' }, + ); let sourceAmountUnits = '0'; let expectedDestinationAmountUnits = '0'; @@ -80,22 +82,25 @@ export function createIntentRequestController({ ); } sourceAmountUnits = parseDecimalToUnits(amountEure, sourceAsset.decimals, { field: 'amount_eure' }); - if (BigInt(sourceAmountUnits) > BigInt(maxAmountUnits)) { + if (maxAmountUnits != null && BigInt(sourceAmountUnits) > BigInt(maxAmountUnits)) { blockedBeforeQuote = true; throw codedError( 'amount_exceeds_request_limit', - `Requested ${amountEure} ${sourceAsset.symbol} exceeds configured live request limit ${requestPair.requestMaxNotional || config.intentRequestMaxAmountEure || 5} ${sourceAsset.symbol}.`, + `Requested ${amountEure} ${sourceAsset.symbol} exceeds configured live request limit ${requestPair.requestMaxNotional} ${sourceAsset.symbol}.`, ); } if (!Number.isInteger(slippageBps) || slippageBps < 0) { blockedBeforeQuote = true; throw codedError('invalid_slippage', 'Slippage must be a non-negative integer in basis points.'); } - if (slippageBps > Number(requestPair.requestMaxSlippageBps ?? config.intentRequestMaxSlippageBps ?? 200)) { + if ( + requestPair.requestMaxSlippageBps != null + && slippageBps > Number(requestPair.requestMaxSlippageBps) + ) { blockedBeforeQuote = true; throw codedError( 'slippage_exceeds_request_limit', - `Slippage ${slippageBps} bps exceeds configured limit ${requestPair.requestMaxSlippageBps ?? config.intentRequestMaxSlippageBps ?? 200} bps.`, + `Slippage ${slippageBps} bps exceeds configured limit ${requestPair.requestMaxSlippageBps} bps.`, ); } @@ -560,11 +565,9 @@ async function resolveIntentRequestPair({ body, config, getTradingConfig }) { priceRoute: pair.priceRoute, requestDefaultNotional: strategyConfig.requestDefaultNotional || config.intentRequestDefaultAmountEure, - requestMaxNotional: - strategyConfig.requestMaxNotional || config.intentRequestMaxAmountEure, + requestMaxNotional: strategyConfig.requestMaxNotional ?? null, slippageBps: strategyConfig.slippageBps ?? config.intentRequestDefaultSlippageBps, - requestMaxSlippageBps: - strategyConfig.requestMaxSlippageBps ?? strategyConfig.slippageBps ?? config.intentRequestMaxSlippageBps, + requestMaxSlippageBps: strategyConfig.requestMaxSlippageBps ?? null, minDeadlineMs: strategyConfig.minDeadlineMs ?? config.intentRequestMinDeadlineMs, priceMaxAgeMs: strategyConfig.priceMaxAgeMs ?? config.intentRequestPriceMaxAgeMs, inventoryMaxAgeMs: strategyConfig.inventoryMaxAgeMs ?? config.intentRequestInventoryMaxAgeMs, diff --git a/src/core/operator-dashboard.mjs b/src/core/operator-dashboard.mjs index d297d7a..c2f0e7a 100644 --- a/src/core/operator-dashboard.mjs +++ b/src/core/operator-dashboard.mjs @@ -900,14 +900,23 @@ function buildFundingSummary({ config, fundingObservations, recentDepositStatuse } function buildIntentRequestSummary({ config, intentRequests = [], executorState = {} } = {}) { + const defaultPair = config.defaultTakerPair || null; + const strategyConfig = defaultPair?.strategyConfig || null; + const usingDbPairConfig = Boolean(config.tradingConfigLoaded && strategyConfig); + const sourceAsset = defaultPair?.assetIn || config.tradingEure; + const destinationAsset = defaultPair?.assetOut || config.tradingBtc; return { defaults: { - source_symbol: config.tradingEure.symbol, - destination_symbol: config.tradingBtc.symbol, - amount_eure: String(config.intentRequestDefaultAmountEure || 5), - max_amount_eure: String(config.intentRequestMaxAmountEure || 5), - slippage_bps: Number(config.intentRequestDefaultSlippageBps ?? 200), - max_slippage_bps: Number(config.intentRequestMaxSlippageBps ?? 200), + source_symbol: sourceAsset?.symbol || 'Source', + destination_symbol: destinationAsset?.symbol || 'Destination', + amount_eure: String(strategyConfig?.requestDefaultNotional ?? config.intentRequestDefaultAmountEure ?? 5), + max_amount_eure: usingDbPairConfig + ? strategyConfig.requestMaxNotional ?? null + : String(config.intentRequestMaxAmountEure || 5), + slippage_bps: Number(strategyConfig?.slippageBps ?? config.intentRequestDefaultSlippageBps ?? 200), + max_slippage_bps: usingDbPairConfig + ? strategyConfig.requestMaxSlippageBps ?? null + : Number(config.intentRequestMaxSlippageBps ?? 200), }, executor_armed: executorState.armed ?? null, executor_paused: executorState.paused ?? null, diff --git a/src/core/trading-config.mjs b/src/core/trading-config.mjs index 98f9fbf..8e294ec 100644 --- a/src/core/trading-config.mjs +++ b/src/core/trading-config.mjs @@ -19,8 +19,9 @@ export const CURRENT_REVERSE_PAIR_KEY = pairKey(CURRENT_EURE_ASSET_ID, CURRENT_N export const CURRENT_EDGE_BPS = 49; export const CURRENT_STRATEGY_MAX_NOTIONAL = '150'; export const CURRENT_REQUEST_DEFAULT_NOTIONAL_EURE = '5'; -export const CURRENT_REQUEST_MAX_NOTIONAL_EURE = '5'; export const CURRENT_SLIPPAGE_BPS = 200; +export const CURRENT_REQUEST_MAX_NOTIONAL_EURE = null; +export const CURRENT_REQUEST_MAX_SLIPPAGE_BPS = null; export const CURRENT_MIN_DEADLINE_MS = 60_000; export const CURRENT_PRICE_MAX_AGE_MS = 30_000; export const CURRENT_INVENTORY_MAX_AGE_MS = 30_000; @@ -188,7 +189,7 @@ export function buildSeedStrategyConfig(pairId, { inventoryMaxAgeMs: CURRENT_INVENTORY_MAX_AGE_MS, requestDefaultNotional: CURRENT_REQUEST_DEFAULT_NOTIONAL_EURE, requestMaxNotional: CURRENT_REQUEST_MAX_NOTIONAL_EURE, - requestMaxSlippageBps: CURRENT_SLIPPAGE_BPS, + requestMaxSlippageBps: CURRENT_REQUEST_MAX_SLIPPAGE_BPS, createdBy, reason, }; diff --git a/src/lib/postgres.mjs b/src/lib/postgres.mjs index 3a41ff3..7e0f3ad 100644 --- a/src/lib/postgres.mjs +++ b/src/lib/postgres.mjs @@ -719,9 +719,9 @@ export async function createPairStrategyConfigVersion(pool, { minDeadlineMs = null, priceMaxAgeMs = null, inventoryMaxAgeMs = null, - requestDefaultNotional = null, - requestMaxNotional = null, - requestMaxSlippageBps = null, + requestDefaultNotional = undefined, + requestMaxNotional = undefined, + requestMaxSlippageBps = undefined, changedBy = 'operator', reason = 'operator config update', } = {}) { @@ -763,17 +763,17 @@ export async function createPairStrategyConfigVersion(pool, { inventoryMaxAgeMs: inventoryMaxAgeMs == null ? Number(active.inventory_max_age_ms) : Number(inventoryMaxAgeMs), requestDefaultNotional: - requestDefaultNotional == null + requestDefaultNotional === undefined ? active.request_default_notional == null ? null : String(active.request_default_notional) - : String(requestDefaultNotional), + : nullablePositiveNumberString(requestDefaultNotional, 'request_default_notional'), requestMaxNotional: - requestMaxNotional == null + requestMaxNotional === undefined ? active.request_max_notional == null ? null : String(active.request_max_notional) - : String(requestMaxNotional), + : nullablePositiveNumberString(requestMaxNotional, 'request_max_notional'), requestMaxSlippageBps: - requestMaxSlippageBps == null + requestMaxSlippageBps === undefined ? active.request_max_slippage_bps == null ? null : Number(active.request_max_slippage_bps) - : Number(requestMaxSlippageBps), + : nullableNonNegativeInteger(requestMaxSlippageBps, 'request_max_slippage_bps'), createdBy: changedBy, reason, }; @@ -874,9 +874,9 @@ export async function setTradingPairMode(pool, { minDeadlineMs = null, priceMaxAgeMs = null, inventoryMaxAgeMs = null, - requestDefaultNotional = null, - requestMaxNotional = null, - requestMaxSlippageBps = null, + requestDefaultNotional = undefined, + requestMaxNotional = undefined, + requestMaxSlippageBps = undefined, changedBy = 'operator', reason = 'operator pair mode update', } = {}) { @@ -1336,9 +1336,9 @@ function buildInitialPairStrategyConfig(pairId, { minDeadlineMs = null, priceMaxAgeMs = null, inventoryMaxAgeMs = null, - requestDefaultNotional = null, - requestMaxNotional = null, - requestMaxSlippageBps = null, + requestDefaultNotional = undefined, + requestMaxNotional = undefined, + requestMaxSlippageBps = undefined, changedBy = 'operator', reason = 'operator pair strategy config initialization', } = {}) { @@ -1396,11 +1396,6 @@ function nonNegativeIntegerOrDefault(value, fallback, field) { return next; } -function nullableNonNegativeIntegerOrDefault(value, fallback, field) { - if (!hasConfigOverride(value)) return fallback == null ? null : nonNegativeIntegerOrDefault(fallback, 0, field); - return nonNegativeIntegerOrDefault(value, 0, field); -} - function positiveNumberStringOrDefault(value, fallback, field) { if (!hasConfigOverride(value)) return String(fallback); const next = String(value).trim(); @@ -1416,10 +1411,25 @@ function nonNegativeNumberStringOrDefault(value, fallback, field) { } function nullablePositiveNumberStringOrDefault(value, fallback, field) { - if (!hasConfigOverride(value)) return fallback == null ? null : positiveNumberStringOrDefault(fallback, '1', field); + if (value === undefined) return fallback == null ? null : positiveNumberStringOrDefault(fallback, '1', field); + return nullablePositiveNumberString(value, field); +} + +function nullablePositiveNumberString(value, field) { + if (!hasConfigOverride(value)) return null; return positiveNumberStringOrDefault(value, '1', field); } +function nullableNonNegativeIntegerOrDefault(value, fallback, field) { + if (value === undefined) return fallback == null ? null : nonNegativeIntegerOrDefault(fallback, 0, field); + return nullableNonNegativeInteger(value, field); +} + +function nullableNonNegativeInteger(value, field) { + if (!hasConfigOverride(value)) return null; + return nonNegativeIntegerOrDefault(value, 0, field); +} + function normalizeStrategyConfigRow(row) { if (!row) return null; return { diff --git a/src/operator-dashboard/static/pages/FundsPage.jsx b/src/operator-dashboard/static/pages/FundsPage.jsx index 8988a68..a970d9d 100644 --- a/src/operator-dashboard/static/pages/FundsPage.jsx +++ b/src/operator-dashboard/static/pages/FundsPage.jsx @@ -279,6 +279,10 @@ function IdentifierLine({ label, value }) { function IntentRequestForm({ summary, onControl }) { const defaults = summary?.defaults || {}; + const sourceSymbol = defaults.source_symbol || 'EURe'; + const destinationSymbol = defaults.destination_symbol || 'BTC'; + const hasAmountCap = defaults.max_amount_eure != null && defaults.max_amount_eure !== ''; + const hasSlippageCap = defaults.max_slippage_bps != null && defaults.max_slippage_bps !== ''; const [form, setForm] = useState({ amount_eure: defaults.amount_eure || '5', slippage_bps: String(defaults.slippage_bps ?? 200), @@ -303,36 +307,40 @@ function IntentRequestForm({ summary, onControl }) {