Skip to content

feat(core): optional beforeSign hook on BaseWallet (refuse-before-sign) - #599

Open
twzrd-sol wants to merge 2 commits into
sendaifun:v2from
twzrd-sol:twzrd/before-sign-wallet-hook
Open

feat(core): optional beforeSign hook on BaseWallet (refuse-before-sign)#599
twzrd-sol wants to merge 2 commits into
sendaifun:v2from
twzrd-sol:twzrd/before-sign-wallet-hook

Conversation

@twzrd-sol

Copy link
Copy Markdown

Problem

SAK actions spend through BaseWallet (signTransaction / signAndSendTransaction / signAllTransactions, plus optional sendTransaction). Once an agent can sign, the expensive failure mode is sending to a bad counterparty.

Today there is no first-class place for a host to refuse before sign without forking N plugins. Hooking only signOrSendTX is incomplete: many plugins call agent.wallet.signAndSendTransaction directly.

Proposal (minimal, zero default behavior change)

  1. Add optional config.beforeSign?: (ctx) => void | Promise<void>.
  2. When set, SolanaAgentKit wraps the provided BaseWallet at construction so every sign/send path invokes the hook first; throw/reject aborts sign.
  3. Unset = no behavior change (agent.wallet === the original wallet).
  4. signMessage is intentionally not guarded (not a spend path).
  5. No network calls in core. No vendor SDK in core. Policy is pluggable.
const agent = new SolanaAgentKit(wallet, rpcUrl, {
  beforeSign: async ({ publicKey, transaction, mode }) => {
    // throw to refuse — allowlist, velocity, external observation, etc.
  },
});

Also exported: wrapWallet(wallet, beforeSign) for hosts that want to wrap without going through the constructor.

Why wallet wrap (not only signOrSendTX)

This is the only complete refuse-before-sign seat: plugins that call the wallet directly cannot bypass the policy.

What this is not

  • Not a facilitator
  • Not a ranking / “trusted seller” marketplace
  • Not a required network dependency
  • Not a new skill or chat action

Tests

9 unit tests in packages/core/src/utils/wrapWallet.test.ts:

cd packages/core && npx tsx --test src/utils/wrapWallet.test.ts

Covers: hook invocation, throw/async reject aborts, signAll, send path, VersionedTransaction, constructor wiring, identity when unset.

Files

  • packages/core/src/utils/wrapWallet.ts (new)
  • packages/core/src/utils/wrapWallet.test.ts (new)
  • packages/core/src/types/index.tsConfig.beforeSign
  • packages/core/src/agent/index.ts — constructor wrap
  • packages/core/src/index.ts — export

Happy to adjust the type shape or naming if maintainers prefer a different surface.

Add a dependency-free pre-sign policy seat on the only complete force
point: wrap BaseWallet at SolanaAgentKit construction so every
sign / signAndSend / signAll / send path can refuse before signature.

Default unset = zero behavior change. signMessage is not guarded.
Plugins that call agent.wallet directly cannot bypass the hook.

Config.beforeSign + exported wrapWallet + unit tests (9).
@twzrd-sol

Copy link
Copy Markdown
Author

@thearyanag, could you review this v2 core API change? Cold validation is green: 9 focused tests, repo-wide Biome, TypeScript, and the full monorepo build; the hook is opt-in and adds no dependency or default behavior.

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