feat: dca@6 — exact token + chain, tokenized stocks, symbol titles - #16
Merged
Merged
Conversation
CHAIN_ID is now required, so recipe_show lists it under `decide` beside TOKEN and SIZE_USD. Both settings' descriptions say what the owner confirms: the match they picked from a card showing its name, chain and address. TOKEN is that match's contract address, and every buy carries --chain-out. A bare ticker used to go to the rail on every fire, and the rail bought whichever deployment it ranked first — possibly a wrapper or a lookalike on another chain, and not necessarily the same one each time. duty.py now buys only an address whose kind matches CHAIN_ID, or ETH / BNB / SOL on their own chains. Anything else buys nothing: one note at start, one log line per fire. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TOKEN goes back to what the owner named, a ticker or an address, so the title shows the symbol, or the address when they gave one. A new ADDRESS setting pins a crypto ticker to its exact contract on CHAIN_ID. A tokenized stock is its ticker alone, with no ADDRESS and no CHAIN_ID. It is bought in the stock shape, and the rail routes a stock buy to the venue with the most shares. CHAIN_ID is optional again: a mint implies Solana, and BNB and SOL imply their chains. A ticker with a chain but no address still buys nothing. The card is shown only when several matches fit; one clear match needs no card. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A live run of "Buy $5 SHIT every 2 minutes" showed that token-search on a
ticker returns only the Base SHIT (unverified but marked relevant). The
Solana SHIT shows up only when searched by its mint. The earlier wording
("when several matches fit") let the butler take that one hit as the
answer and file the Base token.
TOKEN's description now says: if the owner named no chain and gave no
address, ask them to pick on a card that lists each match plus a tile to
paste their own address, even when there is only one match. Re-run with
this wording: the butler asked, and after the mint was pasted it filed
{TOKEN: SHIT, ADDRESS: <mint>} with no CHAIN_ID.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
What
Every DCA buy is now one exact shape, so the rail never picks the token. When the owner hasn't named the chain, the butler confirms it with them.
TOKENADDRESSCHAIN_IDduty.pysends--token-out <address> --chain-out <id>ETH/BNB/SOLBNBandSOLimply theirs)--token-out ETH --chain-out <id>--token TSLA --amount-usdc <usd>(the rail routes a stock buy to the venue with the most shares)ADDRESSholds the exact contract behind a crypto ticker.TOKENstays what the owner named, so the title and notes show the symbol, or the address when the owner gave one.ETHon BSC. The owner gets one note when the program starts, and each fire logs the reason.TOKEN's description says: if the owner named no chain and gave no address, ask them to pick on a card listing each token-search match, with a tile to paste their own address, even when there is only one match. When they named the chain or gave an address, no card is shown.recipe.jsongoes to version 6 and supersedesdca@5.descriptionandkeywordsnow mention stocks.Why an address, not just a chain
A ticker with no chain lets the trade bot pick the deployment. It can pick a wrapper or a lookalike, and it re-picks on every fire. Pinning only the chain is worse: the server's alias rewrite (BTC → cbBTC), its
UNVERIFIED_TICKERrefusal and its stock reroute all skip a leg that carries an explicit chain (bevo-serverdocs/trading.md§3). So a crypto ticker needs its contract.A stock buy can't be pinned to a venue at all.
tradePlandropsprotocol/stockChainon buys and auto-routes, so a stock takes no chain.Live test (real DeepSeek V4 Flash, real staging reads, writes blocked)
The butler was
virtuals-agentatorigin/main(660ca7e), with a local registry serving this branch'sdca@6. A guard in front ofBEVO_API_URLforwarded onlyGET /api/butler-read/*to staging and answered the duty list as empty. It blocked every write: 19duty_createPOSTs, none of which reached staging.acpwas a fake binary that ran only--version.0xc005…1c8f, unverified, marked relevant). The Solana SHIT appears only when searched by its mint. With the first wording ("when several matches fit"), the butler filed the Base token without asking. That led to the last commit. With the final wording it asked: "Which token do you mean by SHIT?" offering SHIT on Base or "paste a different contract address". With the Solana mint pasted, it filed{TOKEN: "SHIT", ADDRESS: "ShitJu…gETRH", SIZE_USD: 5}with noCHAIN_ID. The title renders as "Buy $5 of SHIT every 2 minutes".{TOKEN: "VIRTUAL", ADDRESS: "0x0b3e…7e1b", CHAIN_ID: 8453, SIZE_USD: 5}.{TOKEN: "TSLA", SIZE_USD: 10}with no address or chain, bought in the stock shape. It forked the template to add the Monday check, because a timer trigger has no weekly cadence.Checks
validate.py --standalone --maintainer(butler-skillsorigin/main): OK. README is 3,502 bytes.replay.pyagainsttrade-activity-pageandtrade-activity-mixed: both pass. Each records only the startup note, because CI runs withTOKENunset.Merging
The registry follows this repo's
main, so merging publishesdca@6to every butler on the next hourly build. Afterwards, check the live index:curl -s https://virtual-protocol.github.io/butler-skills/index.json | jq '.templates[]|{name,version}'🤖 Generated with Claude Code