Skip to content

fix(transfer): refuse token transfers on a network other than the logged-in one - #236

Merged
brendanjryan merged 2 commits into
mainfrom
brendan/transfer-network-guard
Oct 15, 2026
Merged

brendanjryan merged 2 commits into
mainfrom
brendan/transfer-network-guard

Conversation

@brendanjryan

@brendanjryan brendanjryan commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Motivation

The accounts SDK falls back to wallet-app approval whenever the local access key cannot sign a call: no key on the selected network, scopes that miss the call, a key revoked on chain, or a failed prepare. The wallet app approves on the chain the wallet is logged in on. swap already refuses a --network other than the logged-in chain for this reason (#218), but a token transfer on another network could still be approved and sent on the logged-in chain, although its dry run, prompt, and chainId named the selected one.

Summary

  • A token transfer whose --network resolves to a chain other than the wallet's logged-in chain returns E_USAGE "Transfer not sent: wallet is logged in on , not ; log in with 'tempo wallet login --network ', then rerun the transfer." with that command as its cta, using swap's commandError pattern. It applies with or without an access key there.
  • withWallet takes a refuse hook that runs between its login and key checks. Transfer passes the network refusal through it, so an agent run without a key on the selected network gets the login cta instead of a refresh one. transferTokens also applies the refusal itself, after argument validation and before the unlisted-token metadata read, the fee-token read, the prompt, and the send.
  • At send time, a new requireChain(provider, network) checks the chain the provider loaded from the wallet store and throws a CtaError (E_USAGE with a tempo wallet login --network <network> cta, returned through ctaErrors) if another login switched it after the command's check, for example while the confirmation prompt was open.
  • --dry-run is not refused and still reviews the transfer on the selected network.
  • cli.ts passes the returned refusal to context.error, as for swap, so agents and MCP clients get the cta.
  • README, SKILL.md, and the changelog describe the rule.

Key design considerations

  • transfer --mach is unchanged: it pays through mppx with the selected network's stored access key, which signs locally on that chain and never falls back to the wallet app (it requires a usable key there).
  • transfer --credits is unchanged: it signs a Coinflow permit whose typed-data chain ID is checked against the selected network before signing, so a wallet-app signature cannot move funds on another chain.
  • The send-time check reads the provider's own loaded store, the state its wallet-app fallback uses, rather than re-reading the file, so no window remains between the check and the provider. It throws a CtaError because it runs below run, so ctaErrors keeps its cta.
  • swap has the same two gaps (a refresh cta for an agent run without a key there, and no send-time re-check); both helpers are reusable there in a separate change.
  • Same-chain sends keep passing chainId, from (through requireSender), and the pinned fee token.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-15T18:24:37.879058Z 92286c2 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@tempoxyz-bot

tempoxyz-bot commented Oct 15, 2026 •

Copy link
Copy Markdown

✅ Cyclops Security Review: safe to merge

Last reviewed: 69c2e5d (latest) · no open findings

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 92286c21dc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/commands/transfer.ts Outdated
Comment thread src/commands/transfer.ts Outdated
@brendanjryan
brendanjryan force-pushed the brendan/transfer-network-guard branch 2 times, most recently from 3f58003 to 2e75506 Compare October 15, 2026 20:38
…ged-in one

The wallet app, which approves a transfer whenever no access key can sign it, approves only on the chain the wallet is logged in on. Like swap, a token transfer on another --network now returns E_USAGE with a 'tempo wallet login --network <network>' cta before any lookup or prompt; dry runs still review it.
…at send time

An agent transfer on another network without a key there now gets the login cta instead of a refresh one: withWallet takes a refuse hook that runs between its login and key checks. The send also re-checks the provider's logged-in chain, so a login on another chain while the prompt is open cannot redirect it.
@brendanjryan
brendanjryan force-pushed the brendan/transfer-network-guard branch from 2e75506 to 69c2e5d Compare October 15, 2026 20:50
@brendanjryan

Copy link
Copy Markdown
Contributor Author

+1

@tempo-voight-kampff tempo-voight-kampff Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving on behalf of @brendanjryan, who approved this pull request (head 69c2e5da8da6) with a +1 comment via Voight-Kampff.

GitHub branch protection considers pull request reviews, not +1 comments. Voight-Kampff is recording this approval on the reviewer's behalf so branch protection requirements are met.

@brendanjryan
brendanjryan merged commit cf02e3d into main Oct 15, 2026
23 checks passed
@brendanjryan
brendanjryan deleted the brendan/transfer-network-guard branch October 15, 2026 21:38
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.

2 participants