Add sbor skill: benchmark lending rate for Stacks - #422
vidardigital wants to merge 17 commits into
Conversation
|
Some context on why I built this. I use lending on Stacks and kept opening three apps to work out whether a rate was any good. There was no neutral place that would just tell me, so I built SBOR and published it as a public good. It has been fixing daily since 1 September and reads rates from Zest and Granite contract state rather than from either app. What made me think it belonged here rather than just on a website: several skills already in this repo are making rate decisions with nothing to judge against. Reading the descriptions in your README, sbtc-yield-maximizer, zest-auto-repay, bitflow-zest-sbtc-leverage-cycle and yield-hunter are each implicitly answering "is this rate good," and there was no benchmark to answer it with. Happy to change anything about the shape of it. I followed the SKILL.md frontmatter spec and the AGENT.md convention from the README, and the script is Commander with JSON to stdout like the others, but tell me if I have got a convention wrong. One thing worth flagging: SBOR takes no payment from any venue it measures and does not trade on its own rate. That is written into the skill docs and into the methodology at sbor.xyz. A benchmark that trades on itself is the LIBOR failure and I would rather rule it out in writing. |
biwasxyz
left a comment
There was a problem hiding this comment.
Thanks for this. A neutral reference rate is useful for the lending skills here. I ran every subcommand against the live endpoint and they all work. A few things need to change before it can land, mostly because compare can currently return a confident verdict from bad input or bad data, and AGENT.md tells agents to rely on it before lending actions.
Blocking
- Repo checks.
bun run validatefails.sbor/SKILL.mdis missingmetadata.requires(use"") andmetadata.author-agent.sbor/AGENT.mdhas no frontmatter (name,skill,description). The PR also needs a row in the README Skills table and a regeneratedskills.json(bun run manifest). See CLAUDE.md, "Adding a New Skill". compareinput (sbor.ts~141-162).--rate 0.042(4.2% entered as a fraction) returns "better than market, 230 bps below".--rate -5,--rate 420and an empty--rateare also accepted with exit 0. Please enforce percent units with bounds, e.g.0 < rate ≤ 100, and reject empty input.comparewith missing data. Ifix.borrow/ix.supplyis absent,diffisNaNand the verdict falls through to "better than market". That breaks the AGENT.md rule "never treat a missing index as zero". Fail with exit 1 instead.- Staleness is only a flag.
comparestill returns a verdict on stale data. A missing or malformedfixinggivesstaleHours: NaN→stale: false.compareshould exit 1 when data is stale or the timestamp can't be parsed.
Should fix
- Add a fetch timeout (
AbortSignal.timeout) on both fetches. compareon an unpublished index (SBOR-BTC today) returnspublished: falsewith exit 0. Exit 1 is safer for agents that only check exit codes.- Docs vs code: the SKILL.md example shows
"verdict": "above market", but the code emits "worse than market". The documentedrate --date 2026-09-01returns 404 because the archive starts 2026-09-03. historyaverages across methodology versions 1.0.0–1.9.0. Consider averaging only within the current version, or returning per-version stats.- AGENT.md's "a weight above 0.9 means one venue" misses SBOR-STX, which is single-venue with a max weight of 0.56. Key it on venue count instead.
- The skill is tagged
mainnet-onlybut ignoresNETWORK. Consider erroring whenNETWORK !== "mainnet".
For maintainers to weigh (not a code change): this makes a single, young (series from 2026-09-01), unsigned third-party feed a pre-trade guardrail, and the methodology changed seven times in about two weeks. Some staleness and methodology-version reporting in the output would help agents judge how much to trust a given fixing.
|
Thank you for running every subcommand. Three of these were real safety bugs and I would not have found them. Blocking, all done.
One I did not do as asked, and I want to be explicit about it.
So it is accepted and flagged. Below 0.5 the output carries a Should fix, all done.
On the maintainer question, which I think is the right one to ask. You are correct that this is a young, unsigned, single feed whose methodology has changed repeatedly. The series began 2026-09-01 and I have revised it every time I found something wrong, most recently when the venue confirmed its contract returns nominal rather than compounded rates. One correction to my own record while you are looking at it: the first two fixings, 1 and 2 September, carry no Every change since is published as a documented step rather than smoothed over, but that is a reason to read the version, not a reason to trust the number more. What I would offer in mitigation is the direction of the guardrail. Every response now carries If you would rather this not be a pre-trade guardrail at all until the record is longer, I would understand that and would rather you say so than merge it on my framing. |
Adds a read-only skill for SBOR, the benchmark lending rate for Stacks. sbor.xyz
Several skills here make rate decisions with no reference rate to judge against: sbtc-yield-maximizer compares Zest against Bitflow HODLMM, zest-auto-repay guards LTV, bitflow-zest-sbtc-leverage-cycle runs the loop, yield-hunter routes idle sBTC. This gives them a market rate.
The main command is compare: give it a rate you have been offered and it returns whether it is above or below the market, by how many basis points, and which venue is cheapest with its utilisation.
No wallet, no keys, no funds, no rate limit. It reads a public endpoint and returns JSON. Tagged read-only, mainnet-only, defi, l2.
AGENT.md carries the guardrails: never substitute an estimate if SBOR is unreachable, never treat a missing index as zero, never compare the PoX staking yield with a lending rate.
SBOR takes no payment from any venue it measures and does not trade on its own rate.