Fix/cli book check referrer - #57
Merged
Merged
Conversation
book check matched only strikes[0], so any strike sitting on a structure's second/third/fourth leg was invisible to it. Roughly half the live book is multi-leg (spreads, flies, rangers), so it recommended RFQ for strikes that were live and fillable — routing traders off the book and forfeiting orderbook credit. The inverse happened too: a 4-strike RANGER whose first leg matched was reported as a fillable vanilla, with a vanilla ticker and the structure's premium quoted as a vanilla ask, and the nextStep it printed then failed to resolve. The root cause was two matchers: check had its own, while preview and fill used resolveOrderBySelector. Extract one shared predicate into bookMatch.ts and have both use it, so the commands cannot disagree about what the book holds. Move check's decision ladder into bookCheck.ts as a pure function so the invariant is directly testable: check may answer rfq only when the shared matcher finds nothing at the requested expiry. Measured against a live 220-order book, 206 of 384 strike positions were previously unreachable. Also compare strike vectors as multisets. Makers do not store strikes in a canonical order — 19 of 310 live orders store them descending — so element-wise comparison meant --strikes 64500,65000 reported "No live order matches" while --strikes 65000,64500 filled the same order. Safe because the fill encodes the signed order's own vector, never the caller's argument order. Drop the hardcoded ETH|BTC gate, which left live SOL/DOGE/XRP/BNB/AVAX orders unreachable, and stop returning RFQ unconditionally for --direction sell without consulting the book. recommendation keeps its existing orderbook/rfq values; the new detail arrives as additive fields (structureMatches, liveExpiries, didYouMean, cliExecutable) so existing consumers are unaffected. Two adjacent book.ts fixes ride along, since they touch the same file: - book fill --dry-run --output json emitted two concatenated JSON documents (preview, then calldata), which no JSON parser accepts. Machine output is now one object with the preview nested under `preview`. Table output is unchanged. - book fill receipts now surface referrer and referralFeePaid from the OrderFilled event, so a fill's attribution can be audited without decoding the receipt on a block explorer.
The SDK has supported a referrer address since 0.2.x — ThetanutsClient takes one and optionBook.fillOrder/previewFillOrder/encodeFillOrder all resolve `referrer ?? client.referrer ?? ZeroAddress`. The CLI never passed it, so every book fill went on-chain attributed to address(0) and silently earned no referral credit. Thread it at client construction so fill, preview and dry-run calldata all pick it up with no per-call-site changes. Resolution follows the existing pattern for rpcUrl and privateKey: --referrer flag, then THETANUTS_REFERRER, then the config file. Validated with ethers.isAddress so a bad value fails early naming its source. book fill warns on stderr when no referrer resolves, including an explicit zero address — that fill genuinely earns nothing, and the warning is what makes the loss visible instead of silent. It goes to stderr so --output json stays machine-parseable. Unrelated to RFQ's --referral-id, which is a numeric tracking ID rather than an address and does not participate in OptionBook fee sharing.
Record the book check and --referrer work in the CLI changelog, document the new check response fields, and add scripts/verify-book-fixes.ts — an end-to-end verifier that derives every strike and expiry from the live book at run time, so it stays valid as the book turns over. All of its fills run --dry-run. Also document two things that cost real debugging time and are not discoverable from this repo: the Odette web UI only renders positions whose referrer matches its own address, and it reads a different indexer than the SDK, refreshed on a 60-minute cron that the browser pokes after its own trades but the CLI does not.
Five findings from an adversarial review of the book check output, all about the gap between what check CLAIMS the book can do and what the command it recommends actually does. - `availableSize` summed every matching maker, but preview/fill resolve exactly one order. With 5 contracts at $1 and 5 at $2, `--size 8` read as "fully available" and then filled 5. Report the ladder as `priceLevels`, state what one invocation takes as `nextStepMaxSize`, and key `partialFillAvailable` off that instead of the aggregate. - Structures carrying the requested strike were sorted by whole-structure premium and the winner's command promoted to the top-level nextStep. A spread, fly, condor and ranger sharing one strike are different products, and the strike can be a long, short or middle leg — so "cheapest" could open exposure opposite to the vanilla being priced. They are now unranked alternatives carrying `legIndex`/`legCount`, and the top-level step is prose (`nextStepIsCommand: false`). - A sell-side match recommended `orderbook` and then handed back an `rfq build` command — the exact off-book route that recommendation exists to prevent. Sells now return a dApp action. Per-structure steps on a sell result no longer emit `book preview` either: preview filters to asks, so it cannot preview the bid that matched. - Structure-only results ignored `--size`, pairing `orderbook` with `availableSize: null`. Each match now carries `meetsRequestedSize`. - A one-off `--referrer` never reached the generated workflow, so a copied command silently fell back to address zero. It is now reported as `referrer` and baked into every emitted command, and global flags appear under Global Options in subcommand help.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes costly OptionBook routing and attribution issues in the CLI, and releases @thetanuts-finance/cli version 0.3.0.
Validation