diff --git a/src/trader/intent-engine.ts b/src/trader/intent-engine.ts index c362cef..1bfc8fd 100644 --- a/src/trader/intent-engine.ts +++ b/src/trader/intent-engine.ts @@ -891,18 +891,23 @@ export function createIntentEngine(deps: IntentEngineDeps): IntentEngine { intents.set(intentId, draftRecord); // Transition DRAFT → ACTIVE (validates transition) - // Build description for market posting + // Build description for market posting. + // + // The market-api is a semantic-search database: counterparties + // discover us by matching the description. We do NOT send a + // structured `price` field — the description embeds all the + // structured rate/volume info already (encodeDescription is the + // canonical TIP-0 form, parseDescription extracts the fields on + // the receiving side). Sending price separately is redundant AND + // hits an unhandled exception in the deployed market-api server + // (HTTP 500 with HTML body) — sphere-sdk#475 §6 surfaced this. + // + // category + currency are still sent because they're useful + // semantic tags for the search index (and the server accepts + // strings there without issue). The `price` field stays + // OPTIONAL in PostIntentRequest for callers with a separate + // numeric pricing flow — the trader doesn't. const description = encodeDescription(tradingIntent); - // Bigint midpoint of the rate band, serialized to a decimal - // string for the wire. NEVER cast to Number — for a typical - // 18-decimal quote asset, the midpoint of a 0.08–0.12 band is - // 1e17 which is past Number.MAX_SAFE_INTEGER (≈9e15). Coercing - // loses precision, the market-api server rejects with HTTP 500, - // and the trader logs only an opaque status code. The SDK's - // PostIntentRequest.price field is `string` for exactly this - // reason (see sphere-sdk PR #483); pass `.toString()` through - // and the JSON serialization is correct end-to-end. - const midpointRate = (rateMin + rateMax) / 2n; const expiresInDays = Math.max( 1, Math.ceil((expiryMs - nowMs()) / 86_400_000), @@ -914,7 +919,6 @@ export function createIntentEngine(deps: IntentEngineDeps): IntentEngine { description, intentType: params.direction, category: `${params.base_asset}/${params.quote_asset}`, - price: midpointRate.toString(), currency: params.quote_asset, // Prefer @nametag for contactHandle — it resolves faster than DIRECT:// // which requires a binding event lookup that may not have propagated yet. diff --git a/src/trader/types.ts b/src/trader/types.ts index 008548e..bd878ea 100644 --- a/src/trader/types.ts +++ b/src/trader/types.ts @@ -269,15 +269,15 @@ export interface MarketPostRequest { readonly intentType: 'buy' | 'sell'; readonly category: string; /** - * Price as a decimal-string bigint in the quote currency's smallest - * units. Same convention as token amounts in sphere-sdk (TXF amount - * fields, transfer payloads): internally bigint, over the wire - * decimal-string. Avoids JavaScript's `Number` precision loss for - * values above 2^53 (an 18-decimal coin hits that ceiling around - * 0.09 in human units). See sphere-sdk PR #483 for the SDK-side - * type that aligns with this. + * Optional structured price hint (decimal-string bigint in the + * quote currency's smallest units). The trader does NOT set this — + * the market-api is a semantic-search database, so we let the + * description (which encodes the full rate band per TIP-0) be the + * source of truth. `price` stays in the shape for callers that have + * a single-point pricing use case, but emitting it from the trader + * is both redundant and trips the deployed market-api server. */ - readonly price: string; + readonly price?: string; readonly currency: string; readonly contactHandle: string; readonly expiresInDays: number; diff --git a/test/e2e/trader-intent-lifecycle.e2e.test.ts b/test/e2e/trader-intent-lifecycle.e2e.test.ts index a15f29c..3078291 100644 --- a/test/e2e/trader-intent-lifecycle.e2e.test.ts +++ b/test/e2e/trader-intent-lifecycle.e2e.test.ts @@ -249,10 +249,12 @@ describe('E2E: Trader Intent Lifecycle (T1 + T10)', () => { expect(postCall.category).toBe('ALPHA/USDC'); expect(postCall.currency).toBe('USDC'); expect(postCall.contactHandle).toBe(AGENT_ADDRESS); - // Midpoint of 450-500 = 475, serialized as decimal-string bigint - // per sphere-sdk PostIntentRequest.price convention (no Number - // cast — see trader/intent-engine.ts comment block). - expect(postCall.price).toBe('475'); + // No structured `price` field — the trader leans on the + // semantic-search engine in the deployed market-api. The + // description (asserted below) already carries the rate band + // in TIP-0 form, so a structured price hint would be redundant + // (and trips the server). See intent-engine.ts comment block. + expect(postCall.price).toBeUndefined(); // Description must match canonical format expect(postCall.description).toContain('Selling 100-1000 ALPHA for USDC'); expect(postCall.description).toContain('Rate: 450-500 USDC per ALPHA');