Skip to content

Release v0.1.3 - #121

Open
Kevin-Chen0 wants to merge 23 commits into
mainfrom
release/v0.1.3
Open

Kevin-Chen0 wants to merge 23 commits into
mainfrom
release/v0.1.3

Conversation

@Kevin-Chen0

@Kevin-Chen0 Kevin-Chen0 commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Release v0.1.3: release/v0.1.3 into main. The cycle opens from the v0.1.2 release commit on main (tag v0.1.2), with every version declaration at 0.1.3; the install pins stay on 0.1.2, the published version, until this pull request moves them before release.

Pull requests for this cycle target release/v0.1.3, not main.

In queue

Longer-running

#62 (more chains), #64 (Solana design), #31 (delegated agent wallet), #80 (an MCP server alongside the CLI, on demand).

🤖 Generated with Claude Code

Kevin-Chen0 and others added 23 commits October 4, 2026 23:53
CI asserts the declared version follows the release branch, so the branch
opens with every version declaration at 0.1.3: package.json, the lockfile,
the plugin's package.json and manifests, and the marketplace entry. The
documentation's install pins stay on 0.1.2, the published version, until
this cycle's release pull request moves them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
Dependabot opens against the default branch, and setting target-branch would
exclude the config from security updates. A workflow now moves each Dependabot
pull request to the newest release branch when it opens or is recreated, and
moves the ones still open when a new release branch is cut.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
…ate a concurrent move

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
…t 2E) and (Test 2F)

Both vaults are Morpho Vault V2 over USDC with 18-decimal shares, open to
any account, and each is the default on its chain; Base stays the default
chain. Every registry value was read on chain on 2026-10-11 and
registry:check reconciles both rows through the public endpoints.

Each chain reads only its own variables: TREASURY_RPC_ETHEREUM /
ETHEREUM_RPC_URL / TREASURY_LOGS_RPC_ETHEREUM, falling back to
ethereum.reth.rs, and TREASURY_RPC_MONAD / MONAD_RPC_URL /
TREASURY_LOGS_RPC_MONAD, falling back to rpc.monad.xyz. Measured: the
Ethereum endpoint serves 100,000-block eth_getLogs ranges and past state;
Monad's serves 100 blocks per request, so a history scan there needs the
logs variable, and the docs, the registry note and the scan's own message
say so.

The scan learns a provider's window from its error. The Ethereum
endpoint words it "query exceeds max block range 100000", which was not
recognised and would have failed every scan once the vault was older than
the window; it is now read as a window.

The earn skill lists all five chains when it asks, and says Ethereum gas
is far higher before a small deposit there. Explorer links are Etherscan
and MonadScan; the connect page accepts both chains.

Refs #62

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
…ault

Solana is not an EVM chain, so it is not a row of the chain table: a
Solana vault is its own registry entry (`solanaVaults`, `family:
"solana"`), read and built with @solana/kit, and every earn and connect
command branches on the vault's family. Base stays the default chain.

The kVault deposit and withdraw instructions and the VaultState and
Reserve decoders are hand-written and pinned byte for byte to Kamino's
own SDK by committed fixtures (vendor/kamino/, written by
generate-fixtures.mjs from a committed mainnet snapshot); that SDK is
not a dependency.

What differs from EVM, by design:
- A prepared deposit or withdrawal is ONE unsigned transaction that
  names a recent blockhash and expires in about a minute; it is
  simulated before it is returned. The connect page asks for it when
  the operator presses Confirm, so it is never stale there.
- The receiver is always the account: the program mints shares to the
  signer and pays a withdrawal to the account whose shares burn.
- Quotes and positions are read from simulated post-state. Measured on
  mainnet: each withdrawal keeps 0.001 USDC in the vault while the
  vault's own penalty settings read 0; `receives` is net of it.
- No event history: entry basis and yield read unknown on Solana.

connect: `connect wallet --chain solana` signs in a Solana wallet
(ed25519, checked locally) and keeps it apart from the EVM connection.
The acknowledgement covers the intent (account, vault, action, amount),
and a gate admits only a transaction whose every instruction is a
compute-unit limit, the account's own token-account creation, or this
vault's deposit or withdrawal for the connected account. The page uses
Privy's Solana wallets less WalletConnect, which this page does not
ship, and Privy's own Solana RPC.

registry:check reconciles the Solana row against the vault state; the
live tier covers Solana when TREASURY_RPC_SOLANA is set.

Refs #62, #64

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
…osed, gate pins the vault's PDAs

- ci.yml: `connect wallet` now takes `--chain`, so its pinned flag set is {"chain"}.
- The 0.001 USDC is Kamino's GLOBAL withdrawal penalty (the program's
  global config holds withdrawalPenaltyLamports = 1000), not this vault's
  setting, and Kamino can change it. Measured by simulation: it is charged
  per withdraw instruction (2,000,000 shares kept 1,000 as one instruction,
  1,929 as two), and a withdrawal worth 0.001 USDC or less is refused
  (error 7049). Disclosed in the registry note, docs, risks, README, skill
  and changelog; quotes stay exact because they are simulated.
- Kamino's program can require a co-signer on every deposit and every
  withdrawal of a vault. Disclosed, and the builders now refuse while one
  is set (unset today), with a test.
- Who can change the terms on Solana, with no timelock: the vault admin
  (fees, the vault's own penalty) and the vault admin or Kamino's global
  admin (crank fee, minimum amounts). Reserve liquidity limits each
  withdrawal leg; said so.
- A leg asking for more than it can pay is capped, not reverted: the
  landing check now calls that a partial withdrawal.
- The gate also pins the vault's token vault and authority PDAs and the
  second vault state a withdrawal names, with tests; its header says what
  stays with the program's own constraints.
- Wording: the approve/proxy note is EVM-only; `depositable` includes the
  Kamino vault; compute numbers re-measured; stale counts in titles and
  descriptions. Removes a scratch script committed by mistake.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
…e changelog's chain count

Wording only: docs/tools.md now says what every other page says, and the
Ethereum/Monad changelog entry no longer counts the chains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
ci: keep Dependabot pull requests on the newest release branch
earn: Ethereum and Monad, with Cash Plus USDC (Test 2E) and (Test 2F)
The fork tier ran a deposit and redeem only against one vault on Base and
one on Arbitrum One. It now round-trips every vault the registry marks
depositable, on every EVM chain whose RPC variable is set: Base (Test 2
and Test 2B), Arbitrum One (2C), Robinhood Chain (2D, USDG), Ethereum (2E)
and Monad (2F). The vault list comes from the registry, not the test, so a
new vault is covered when it is added.

Each chain gets an impersonated holder of its deposit asset (Morpho Blue
on Ethereum; the largest recent holder on Monad and Robinhood Chain,
probed 2026-10-11) and amounts use the asset's own decimals. The test logs
which exit each vault took, so a quote that refused the full redeem cannot
pass silently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
A deposit and withdrawal on the Solana vault now land for real, on a
local Surfpool fork of mainnet-beta, through the transactions this
package builds:

- deposit through `earn prepare_deposit`, which the connect gate must
  also admit;
- a partial withdrawal through the confirm page's builder (read, build,
  gate, simulate);
- the rest through `earn prepare_withdraw` with the exact share string
  `earn balance` reports.

Each is signed by a key generated for the run (funded on the fork only)
and checked by `verifySolanaLanded`, which must report `matched`. The
round trip's cost is bounded by Kamino's charge per withdraw instruction,
counted from the transactions sent (2 USDC cost 0.001939 USDC over two
withdraw instructions).

`npm run test:fork` runs it when `TREASURY_RPC_SOLANA` is set and
`surfpool` is on PATH. A send the fork refuses because its upstream
dropped an account fetch is resent with the same signed bytes, at most
three times.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
tests: fork round trip on every depositable EVM vault
Brings in the EVM fork round trips (#128). The one conflicting sentence,
in AGENTS.md and CONTRIBUTING.md, now says the fork tier covers every
chain: anvil for the EVM chains, Surfpool for Solana.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
A Kamino vault takes only the assets that back the shares it mints, so at
some share prices a 2 USDC deposit debits 1,999,999 base units. The test
asserted exact equality and failed there before any withdrawal ran. It now
bounds the debit: never more than the amount, at most 10 units less.

Also: a missing `surfpool` binary is reported by name instead of as an
unhandled ENOENT, and AGENTS.md names Surfpool beside Foundry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
earn and connect on Solana, with Cash Plus USDC (Test 2G)
The registry and the package version were read from files beside the
module at run time. A library re-bundled into another application looked
for registry/vaults.json beside the consumer's own bundle and read
whatever was there.

src/registry.ts and src/version.ts now import registry/vaults.json and
package.json with JSON import attributes, so tsup and a consumer's
bundler inline them. treasury --version reports the version the bundle
was built from, and a registry change now rebuilds the bundle.

- The boundary audit allows exactly these two JSON imports, by importing
  file and specifier; any other JSON import, or either of these from
  another file, still fails.
- The broken-registry tests build the CLI from source with the registry
  import pointed at a broken copy, instead of swapping a file beside the
  bundle.
- New tests run the committed bundle alone in a folder, with decoy
  registry and package.json files beside it, and a consumer's re-bundle
  of dist/index.js with a decoy registry beside that.
- The comments that described the run-time read are corrected.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
Importing package.json inlined its scripts and devDependencies into
dist/treasury.mjs, so a dev-only dependency bump (which Dependabot makes
without rebuilding) would have made the committed bundle stale.

tsup.config.ts now serves the root package.json to the bundler as
{ version }. The broken-registry test builds use the same plugin. A new
test checks the bundle carries no devDependency name and no script text.
The changelog notes the ExperimentalWarning that Node 22.0 to 22.11 print
when the library is imported.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
Importing package.json for its version inlined the whole manifest into
dist/treasury.mjs, scripts and devDependencies included, so any edit to
package.json (a dev-dependency bump) made the committed bundle stale and
failed the bundle check. An esbuild plugin in tsup.config.ts now serves
the root package.json to the bundle as { version } only.

A test holds the committed bundle to that: it contains the version, no
devDependencies, and no package.json script. Measured: a dev-dependency
and script edit leaves the bundle byte-identical; a version change still
reaches `treasury --version`.

The CHANGELOG also notes Node's ExperimentalWarning for JSON modules when
the library is imported unbundled on Node 22.0 to 22.11.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
Embed the registry and the version in the bundle (#107)
An agent that runs treasury without the plugin never sees the rules the
Earn skill carries. Measured on 0.1.2, such an agent read a guide 0 times
in 9 when it was only in the package, and 9 times in 9 when treasury
--help named it; with the guide it showed all of the first-deposit terms
instead of about a third.

- llms.txt is built by scripts/llms-txt.ts from the Earn skill's body and
  the command descriptions that `--help` prints. The skill's text changes
  in four exact places (the plugin's launch line becomes the plain
  command, and the sentences about the plugin's permission prompt go);
  the script stops if the skill no longer contains them.
- `treasury --help` and `treasury <skill> --help` carry a `guide` field:
  an instruction to read it first, the file's path when it sits beside
  the CLI, and the public URL.
- npm run build writes it and copies it into plugin/; CI fails when the
  committed file differs from what the sources produce. It ships in the
  npm package.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
The plugin's Earn skill is the text llms.txt is built from and is already
loaded, so a pointer in the plugin's --help sent every plugin user to
re-read it. guidePointer() now returns nothing when the plugin's manifest
sits beside the CLI's parent folder (Claude Code or Codex), and llms.txt is
no longer copied into plugin/.

A test runs the plugin copy's help at all three levels and a bundle laid
out as either host's plugin. prepack also checks that llms.txt is
committed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Kevin Chen <13785749+Kevin-Chen0@users.noreply.github.com>
Add a generated llms.txt and point to it from --help (#106)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant