Why
Today the sphere trader create-intent CLI declares --rate-min <bigint> / --rate-max <bigint> / --volume-min <bigint> / --volume-max <bigint> and forwards the literal strings through to the trader's ACP CREATE_INTENT payload without conversion (see src/trader/trader-commands.ts:386-389 + :224-231). Operators must type fully-scaled smallest-unit integers — for example 80000000000000000 for 0.08 ETH per UCT given UCT is 18-decimal.
Per project owner UX guidance, the intended CLI surface is human-friendly floats: --rate-min 0.08, --volume-min 50. The CLI converts to bigint smallest-units internally via a token-registry decimals lookup before sending the ACP payload. The ACP wire format stays bigint-string (canonical JSON preserves precision); only the CLI surface changes.
This is the third leg of the three-way drift documented in sphere-sdk/blob/main/docs/uxf/PROTOCOL-SPEC-DRIFT-474.md finding D-NEW:
- Design intent: float at CLI surface, bigint at ACP wire (convert at CLI boundary).
- Spec (trader-service
docs/protocol-spec.md §2.4 / §4.2): number end-to-end.
- Implementation today: bigint-string end-to-end including the CLI.
The implementation side will stay on bigint (the ACP wire shouldn't change), the spec side is tracked separately in trader-service. This issue is the CLI side.
Scope
sphere trader create-intent is the primary surface. Same UX should apply to anywhere else in sphere trader (and sphere swap) that touches amounts or rates.
Behavior
- Default: accept floats.
--rate-min 0.08 --rate-max 0.12 --volume-min 50 --volume-max 50. Convert via BigInt(Math.round(value × 10**decimals)) using each asset's decimals from the token registry. Send the bigint-string on the ACP wire.
- Power-user fallback:
--rate-min-bigint <int> / --volume-min-bigint <int> etc. for operators who want to type the smallest-unit form explicitly. The two flag families are mutually exclusive per axis.
- Validation, pre-flight (CLI-side, before any DM):
- Reject non-finite floats (NaN, Infinity).
- Reject negatives / zero.
- Reject more decimal digits than the asset supports (e.g.
--rate-min 0.123456789012345678999 against an 18-decimal asset).
- Reject
--rate-min > --rate-max and --volume-min > --volume-max.
- Report the resulting bigint in the CLI's confirmation log line so operators can sanity-check.
- Decimals source: token registry (
@unicitylabs/sphere-sdk TokenRegistry.decimalsFor(coinId) or equivalent). If the registry has no entry for the asset, fall back to a reasonable default (18) and warn loudly. Do NOT silently assume 18 decimals — the warning is critical because UCT-on-mainnet may differ from UCT-on-testnet in some future revision.
- Help text: clarify the units.
--rate-min <float> "Minimum acceptable rate (quote per base unit, decimal; e.g. 0.08 for 0.08 ETH per UCT)". --volume-min <float> "Minimum volume per match (in whole base-asset units; e.g. 50 for 50 UCT)".
Acceptance
Out of scope
- The trader-service spec patch (D1 in the drift audit) — tracked separately against vrogojin/trader-service.
- The trader-agent image rebuild — tracked separately against vrogojin/agentic_hosting.
- The ACP wire format — stays bigint-string.
Related
- sphere-sdk#474 — trader-roundtrip soak (PR #475) — writes the float form already, with a bigint-shim fallback that this issue eliminates.
- sphere-sdk PR #475 —
manual-test-trader-roundtrip.sh exercises this UX.
- sphere-sdk PROTOCOL-SPEC-DRIFT-474.md (in PR #475) — Finding D-NEW for the full three-way drift discussion.
Why
Today the
sphere trader create-intentCLI declares--rate-min <bigint>/--rate-max <bigint>/--volume-min <bigint>/--volume-max <bigint>and forwards the literal strings through to the trader's ACPCREATE_INTENTpayload without conversion (seesrc/trader/trader-commands.ts:386-389+:224-231). Operators must type fully-scaled smallest-unit integers — for example80000000000000000for0.08 ETH per UCTgiven UCT is 18-decimal.Per project owner UX guidance, the intended CLI surface is human-friendly floats:
--rate-min 0.08,--volume-min 50. The CLI converts to bigint smallest-units internally via a token-registry decimals lookup before sending the ACP payload. The ACP wire format stays bigint-string (canonical JSON preserves precision); only the CLI surface changes.This is the third leg of the three-way drift documented in sphere-sdk/blob/main/docs/uxf/PROTOCOL-SPEC-DRIFT-474.md finding D-NEW:
docs/protocol-spec.md§2.4 / §4.2):numberend-to-end.The implementation side will stay on bigint (the ACP wire shouldn't change), the spec side is tracked separately in trader-service. This issue is the CLI side.
Scope
sphere trader create-intentis the primary surface. Same UX should apply to anywhere else insphere trader(andsphere swap) that touches amounts or rates.Behavior
--rate-min 0.08 --rate-max 0.12 --volume-min 50 --volume-max 50. Convert viaBigInt(Math.round(value × 10**decimals))using each asset's decimals from the token registry. Send the bigint-string on the ACP wire.--rate-min-bigint <int>/--volume-min-bigint <int>etc. for operators who want to type the smallest-unit form explicitly. The two flag families are mutually exclusive per axis.--rate-min 0.123456789012345678999against an 18-decimal asset).--rate-min > --rate-maxand--volume-min > --volume-max.@unicitylabs/sphere-sdkTokenRegistry.decimalsFor(coinId)or equivalent). If the registry has no entry for the asset, fall back to a reasonable default (18) and warn loudly. Do NOT silently assume 18 decimals — the warning is critical because UCT-on-mainnet may differ from UCT-on-testnet in some future revision.--rate-min <float>"Minimum acceptable rate (quote per base unit, decimal; e.g. 0.08 for 0.08 ETH per UCT)".--volume-min <float>"Minimum volume per match (in whole base-asset units; e.g. 50 for 50 UCT)".Acceptance
sphere trader create-intent --tenant @x --direction sell --base UCT --quote ETH --rate-min 0.08 --rate-max 0.12 --volume-min 50 --volume-max 50 --expiry-ms 3600000 --jsonsucceeds against a v0.1+ trader-agent image and the ACP payload'srate_min/rate_max/volume_min/volume_maxare bigint strings of the right magnitude.sphere trader create-intent --helpdocuments the float UX.buildCreateIntentParams.manual-test-trader-roundtrip.shis re-run after this lands, the bigint-shim fallback (create_intent_with_shimsecond branch) is never taken — the first-attempt float form succeeds.Out of scope
Related
manual-test-trader-roundtrip.shexercises this UX.