Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
# /#447 rotations needed for agentic_hosting#26 (trader:v0.2 image
# rebuild). Keep the docker-publish.yml pin aligned on every bump.
env:
SPHERE_SDK_SHA: 550114c75e0b47151488d0c5d656475fd5a5077b
SPHERE_SDK_SHA: b2fd028fdf8dc86a598e7bf34c069dce50442b59
run: |
git clone https://github.com/unicity-sphere/sphere-sdk.git ../sphere-sdk
git -C ../sphere-sdk checkout --detach "$SPHERE_SDK_SHA"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
# counterparty transport pubkey), #464 PR #465 (MuxAdapter dispatch
# awaits async handler), #447 PR #461 (terminal swap visibility).
env:
SPHERE_SDK_SHA: 550114c75e0b47151488d0c5d656475fd5a5077b
SPHERE_SDK_SHA: b2fd028fdf8dc86a598e7bf34c069dce50442b59
run: |
git clone https://github.com/unicity-sphere/sphere-sdk.git sphere-sdk
git -C sphere-sdk checkout --detach "$SPHERE_SDK_SHA"
Expand Down
13 changes: 11 additions & 2 deletions src/trader/intent-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,16 @@ export function createIntentEngine(deps: IntentEngineDeps): IntentEngine {
// Transition DRAFT → ACTIVE (validates transition)
// Build description for market posting
const description = encodeDescription(tradingIntent);
const midpointRate = Number((rateMin + rateMax) / 2n);
// 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),
Expand All @@ -905,7 +914,7 @@ export function createIntentEngine(deps: IntentEngineDeps): IntentEngine {
description,
intentType: params.direction,
category: `${params.base_asset}/${params.quote_asset}`,
price: midpointRate,
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.
Expand Down
20 changes: 16 additions & 4 deletions src/trader/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,16 @@ export interface MarketPostRequest {
readonly description: string;
readonly intentType: 'buy' | 'sell';
readonly category: string;
readonly price: number;
/**
* 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.
*/
readonly price: string;
readonly currency: string;
readonly contactHandle: string;
readonly expiresInDays: number;
Expand All @@ -282,8 +291,10 @@ export interface MarketSearchOptions {
export interface MarketSearchFilters {
readonly intentType?: 'buy' | 'sell';
readonly category?: string;
readonly minPrice?: number;
readonly maxPrice?: number;
/** Decimal-string bigint — see {@link MarketPostRequest.price}. */
readonly minPrice?: string;
/** Decimal-string bigint — see {@link MarketPostRequest.price}. */
readonly maxPrice?: string;
readonly minScore?: number;
}

Expand All @@ -295,7 +306,8 @@ export interface MarketSearchResult {
readonly description: string;
readonly intentType: 'buy' | 'sell';
readonly category?: string;
readonly price?: number;
/** Decimal-string bigint — see {@link MarketPostRequest.price}. */
readonly price?: string;
readonly currency: string;
readonly contactHandle?: string;
readonly createdAt: string;
Expand Down
6 changes: 4 additions & 2 deletions test/e2e/trader-intent-lifecycle.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,10 @@ 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
expect(postCall.price).toBe(475);
// 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');
// 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');
Expand Down
Loading