Skip to content

feat(agentkit): add TAP Labs action provider (token and transaction s… - #1580

Open
Schatzy42 wants to merge 1 commit into
coinbase:mainfrom
Schatzy42:tap-labs-action-provider
Open

Schatzy42 wants to merge 1 commit into
coinbase:mainfrom
Schatzy42:tap-labs-action-provider

Conversation

@Schatzy42

Copy link
Copy Markdown

Description

Adds tapLabsActionProvider, two pre-trade safety checks for AgentKit agents from TAP Labs. Each check is paid per call in USDC on Base with x402, from the agent's own wallet:

  • check_token_safety runs the TAP Labs Safety Check ($0.01). It looks for scam and rug pull warning signs in a token on Base or Solana, or a contract on Base:
    • who can mint, freeze, blacklist, pause, tax or upgrade it;
    • liquidity and pool age;
    • holder concentration;
    • verified source and public scam lists (Base);
    • the OFAC list.
  • check_transaction runs the TAP Labs Wallet Firewall ($0.02). Before the agent signs a transaction, approval, permit, sign-in, EIP-7702 authorization or Solana transaction that came from outside its own tools, it decodes what the request does and compares it with the user's own words. It flags:
    • a wrong amount or recipient;
    • lookalike addresses;
    • approvals to the wrong spender;
    • sign-ins for another site;
    • hidden calls;
    • matches on public scam lists and the OFAC list.

Blockscout ended keyless API access on 1 July 2026. TAP Labs is moving its Base explorer reads (verified source, holders, the explorer's record) to other sources. Until then, those three show under notChecked and are never guessed. That is why the live run below lists 9 items not checked.

Both checks are advisory, not guarantees. They return block, warn, ok or unknown, with findings that link their sources and a list of what was not checked. unknown is never treated as safe. Neither service holds funds, and neither signs anything for the user.

Payment safety

The provider signs payments on its own, so it limits what it will sign:

  • What it will pay. Only an x402 v2 exact payment that meets every one of these conditions:

    • USDC on Base mainnet;
    • to TAP's address (overridable with payTo);
    • at or under a per-check cap (maxPaymentUsdc, default $0.03, at most $1);
    • an authorization valid for at most 10 minutes;
    • USDC's own EIP-712 domain.

    Any other payment is refused before signing.

  • Budget. Each provider instance has a total budget (maxTotalUsdc, default $1, at most $100). The amount is reserved before signing, so parallel calls cannot overspend.

  • Least privilege. The x402 library gets only the wallet's address and signTypedData, so it cannot send or approve anything. The 402's extensions are not passed on. The same limits are registered again as an x402 client policy.

  • One payment per action. Redirects are refused, responses are size-capped, and the output stays under 12,000 characters.

  • Prompt-injection hygiene. Text from the service (token names, sites' messages) is stripped of control and invisible characters. The output tells the model to treat quoted text as data.

  • If something goes wrong after signing, the message says so and tells the agent to check the wallet's USDC transfers before paying again. TAP Labs settles a payment only together with a successful answer.

Supports EVM wallet providers on base-mainnet.

Docs: a provider README, an entry in the agentkit README, and a changeset (patch).

Disclosure: this comes from TAP (The Atlas Project), which runs TAP Labs. Terms: https://theatlasproject.dev/docs/terms

Tests

Unit tests. cd typescript/agentkit && npx jest src/action-providers/tapLabs runs 71 tests, all passing. They use real x402 signing with a generated key and a mocked network, and cover:

  • the constructor's limits and network support;
  • hostile or unusable 402s, each refused before signing:
    • a price over the cap, zero or fractional;
    • another payee, network, asset or scheme;
    • a validity over 10 minutes, or given as text;
    • a Permit2 transfer method, another EIP-712 name or version, or unknown fields in extra;
    • x402 v1, or an unreadable 402;
  • the 402's extensions (such as EIP-2612 gas sponsoring) never reaching the signer;
  • the budget, including parallel checks and giving the amount back when nothing was signed;
  • settlement reporting (charged, not charged, unknown) and transaction-hash validation;
  • size limits, text cleaning and a custom payee.

ESLint and Prettier pass on the new files.

Live on Base mainnet, 21 October 2026. This went through AgentKit's action interface, with no LLM:

  • the wallet provider was ViemWalletProvider on Base mainnet, with a wallet holding USDC on Base;
  • the provider was tapLabsActionProvider({ maxPaymentUsdc: 0.02, maxTotalUsdc: 0.03 });
  • the actions came from getActions() and were invoked with their own schemas.
Network: Base mainnet

Action: TapLabsActionProvider_check_token_safety
Args:   { "address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "chain": "base" }   (USDC on Base)
-------------------
verdict:  warn
whatToDo: Show the user the findings and ask before going ahead.
summary:  Found for USDC 0x8335…2913 as of 2026-10-21 00:02 UTC: 4 high findings, led by Upgradeable: the
          contract's code can be replaced; Can create new tokens; Can block addresses from moving tokens.
          9 items were not checked; see the list.
payment:  { charged: true, usdc: "0.01", network: "eip155:8453",
            transaction: "0x220f3aae2108d0b3563223f0a74a5427fe5ed479d3c232969c9a678d8a4a181d" }
-------------------

Action: TapLabsActionProvider_check_transaction
Args:   { "intent": "Sign a Permit2 permit letting SwapRouter spend exactly 100 USDC.",
          "request": { "type": "typed_data", "typedData": <a Permit2 PermitSingle for USDC with amount 2^160-1> },
          "chain": "base", "addressBook": [ ... ] }
-------------------
verdict:  block
whatToDo: Do not go ahead. Tell the user what was found.
summary:  Do not sign. As of 2026-10-21 00:02 UTC: The permit allows any amount of USDC. You asked for exactly
          100 USDC. The amount in the request is the largest a permit can hold.
finding:  critical, wrong_amount: "The permit allows any amount of USDC"
steps:    let 0x5e2d…5e7d spend any amount of USDC (no limit) (by signature, no transaction needed) through
          Permit2, until 2027-01-01 00:00 UTC
youAsked: sign a permit letting SwapRouter spend 100 USDC
payment:  { charged: true, usdc: "0.02", network: "eip155:8453",
            transaction: "0xdf221a88854fd106f31823fc16abb221e6c59e6decef74f4c9c58cc6bb8bc6fc" }
-------------------

Third call (check_token_safety again, with the $0.03 budget used up):
{ "error": true, "message": "This session's TAP Labs budget (0.03 USDC) is used up, so the check was refused. Nothing was paid." }

Both settlements were then read back from Base:

  • each transaction succeeded and holds exactly one USDC transfer, from the wallet to TAP's address, for 10000 and
    20000 units;
  • the wallet's USDC balance fell by exactly 0.03.

Explorer links:

I have not run it through an LLM chatbot from examples/ yet. I can add a transcript if you'd like one.

Unrelated, found during the mainnet run. AgentKit's analytics endpoint answered HTTP 400 to every action invocation.

Checklist

  • Added documentation to all relevant README.md files
  • Added a changelog entry

🤖 Generated with Claude Code

https://claude.ai/code/session_01HZGGYkXaPLo54ceaP67829

…afety checks)

Adds tapLabsActionProvider with two actions an agent can run before it buys, swaps or signs:
- check_token_safety: TAP Labs' Safety Check for a token on Base or Solana, or a contract on Base
- check_transaction: TAP Labs' Wallet Firewall for a transaction, approval, permit or sign-in message

Each check is paid per call in USDC on Base with x402 from the agent's wallet. Before signing, the provider
refuses any payment that is not an exact USDC payment on Base mainnet to TAP's wallet, at or under a per-check
cap (default $0.03), valid for at most 10 minutes and using USDC's own EIP-712 domain; it reserves a total budget
per provider instance (default $1). The x402 library gets only the wallet's address and typed-data signing, and
the 402's extensions are not passed on. One payment per action, no retries of a paid request, redirects refused.
What goes back to the model is size-capped, cleaned of control and invisible characters and labelled as data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HZGGYkXaPLo54ceaP67829
@cb-heimdall

Copy link
Copy Markdown

🟡 Heimdall Review Status

Requirement Status More Info
Reviews 🟡 0/1
Denominator calculation
Show calculation
1 if user is bot 0
1 if user is external 0
2 if repo is sensitive 0
From .codeflow.yml 1
Additional review requirements
Show calculation
Max 0
0
From CODEOWNERS 0
Global minimum 0
Max 1
1
1 if commit is unverified 0
Sum 1

@github-actions github-actions Bot added documentation Improvements or additions to documentation action provider New action provider typescript labels Oct 21, 2026
@Schatzy42

Copy link
Copy Markdown
Author

Friendly bump — happy to make any changes for review. Context in case it helps: this action provider has been running live on Base mainnet since Oct 21 — two real checks settled through the x402 flow and a third was correctly stopped by the budget before signing, so pay-per-call is exercised end to end, not just unit-tested. It lets an AgentKit agent call TAP’s certification checks and pay per call over x402 — squarely the agent-commerce case AgentKit is for. One signed commit, following action-provider conventions. Glad to split it, add tests, or adjust naming — just point me at what you’d like changed. Thanks for the look.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action provider New action provider documentation Improvements or additions to documentation typescript

Development

Successfully merging this pull request may close these issues.

3 participants