true402
guide · Virtuals Protocol GAME SDK

Add a rug-check to your Virtuals Protocol (GAME) agent (Base honeypot gate)

If your Virtuals Protocol (GAME) agent trades or touches Base tokens, one honeypot can drain the run. This guide registers a real on-chain sellability check as a Worker function and uses it as a hard pre-trade gate — verdict first, buy second.

Install and register the true402 safety functions in a GAME Worker
pip install game_sdk game-true402

# agent.py
import os
from game_sdk.game.worker import Worker
from game_true402 import true402_functions

# true402_functions() returns six true402 safety Functions, already wired
# to speak x402 under the hood (402 -> pay in USDC on Base -> response):
#   check_token_report, check_token_safety, check_address_safety,
#   check_liquidity_history, preflight_transaction, check_deployer
# It reads PAYER_PRIVATE_KEY from the env (a Base wallet holding a little USDC).
# Omit the key to ride the free daily trial while you build (covers all but check_deployer).
worker = Worker(
    api_key=os.environ["GAME_API_KEY"],   # from https://console.game.virtuals.io/
    description="A cautious on-chain trader that vets tokens before buying.",
    instruction="Always run check_token_report before acting on a token.",
    get_state_fn=lambda function_result, current_state: (current_state or {}),
    action_space=true402_functions(),      # <-- the safety Functions live here
)

worker.run("Is token 0x... safe to buy on Base?")
§01 · why a static scan isn't enough

The honeypot problem GAME trading agents hit

A GAME agent that trades on Base is only as safe as the token it buys. The classic trap is the honeypot: a token you can buy but can never sell. Static scanners read the contract source or ABI and guess — but a malicious transfer hook, a hidden blacklist, or a sell tax set to 100% won't always show up in a static read.

true402's rug-check runs a real on-chain buy/sell simulation using a state-override eth_call. It injects a buy-then-sell into a simulated state and measures whether the tokens actually come back out — proving sellability, not just that the code looks fine. On top of that it checks liquidity, ownership and mint authority, and deployer reputation. That combination is what you want gating a trade the planner is about to fire.

§02 · install and register the function

Add true402_functions() to a Worker

In the GAME SDK you don't subclass anything — you construct Function objects and register them in a Worker's action space. The game-true402 package hands you those Functions pre-built, so you just drop them into action_space.

  1. Run pip install game_sdk game-true402.
  2. Import true402_functions and call it — it returns six true402 safety Functions: check_token_report, check_token_safety, check_address_safety, check_liquidity_history, preflight_transaction and check_deployer.
  3. It reads PAYER_PRIVATE_KEY from the environment (a Base wallet holding a little USDC); omit the key to ride the free daily trial while you build — it covers every one of the six except check_deployer.
  4. Pass the returned list to Worker(action_space=...) — that is the agent's action space.

Payment happens per call in USDC on Base and the wallet is the identity, so there is no account to create and no API key to manage. The full snippet is in the code block above. You still need a GAME API key in GAME_API_KEY from the GAME console to run the agent itself.

§03 · how the wiring works under the hood

Function → Worker → Agent

GAME custom actions are three layers: Functions group into a Worker via its action_space, and Workers group into an Agent. A bare Worker can also be run directly in task mode with worker.run("..."), which is the simplest way to test the gate.

If you want to wire it by hand instead of using the package, you construct Function(fn_name=..., fn_description=..., args=[Argument(name, type, description)], executable=your_callable). A few GAME-specific rules matter for the executable that calls the safety API:

  • The Python executable is invoked synchronously — use a sync HTTP client such as requests, or wrap async code with asyncio.run(...) inside the function.
  • The planner passes each declared Argument by name, so your parameter names must match, and you must accept **kwargs (the SDK injects extra keys — omitting it raises TypeError).
  • It must return a 3-tuple (FunctionResultStatus, message: str, info: dict) — statuses are FunctionResultStatus.DONE and FunctionResultStatus.FAILED. The info dict is fed into the state-update function on the next step, which is how the verdict reaches your gate logic.

There is also a Node/TypeScript SDK (@virtuals-protocol/game) whose executable is natively async and returns an ExecutableGameFunctionResponse. The game-true402 package targets the canonical Python SDK.

§04 · the pre-trade gate pattern

Check the verdict before you buy

The safe pattern is a hard gate: the agent must call the rug-check and read the verdict before any buy function fires. Encode that in the Worker's instruction — for example "Always run check_token_report before acting on a token" — so the planner treats it as a required step in the action space.

The token-report stall returns a composite verdict of avoid, caution, or ok. The rule is simple:

  1. avoid → do not buy. Abort the trade and report why.
  2. caution → require a second look (lower size, human confirm, or a deeper stall like deployer-check).
  3. ok → proceed to the buy step.

Because the check returns its verdict in the info dict that flows into the next step's state, your buy function can read it from state and refuse to act unless it is ok. Treat a FAILED status or a missing verdict as avoid — fail closed, never fail open.

§05 · which stall to call

The six safety stalls and their prices

Pick the stall by how much depth you need. All are POST endpoints under https://true402.dev/api, each x402-gated (402 → pay in USDC → response):

  • check_token_reportPOST /v1/base/token-report — composite avoid/caution/ok verdict, the best default for a trade gate (~$0.01). Free daily trial.
  • check_token_safetyPOST /v1/token-safety — structural safety score 0–100 when you want a raw number (~$0.005). Free daily trial.
  • check_address_safetyPOST /v1/base/address-safety — screen a counterparty or spender address before a send, approve, or call (~$0.005). Free daily trial.
  • check_deployerPOST /v1/base/deployer-check — deployer wallet reputation, useful on a caution verdict (~$0.008). No free trial — it depends on a keyed third-party explorer API, so it is paid from the first call.
  • preflight_transactionPOST /v1/base/tx-preflight — the last check before signing: pass the unsigned transaction (from, to, data, value) and get a simulation against current state, the decoded intent (an unlimited approval is judged on the spender), and the counterparty's removal history (~$0.008). It takes no private key and no signature, so it cannot broadcast or front-run. Free daily trial.
  • check_liquidity_historyPOST /v1/base/liquidity-history — what has already happened to the token's liquidity: every observed removal with amount, block and transaction hash, plus the other tokens drained in the same transaction (~$0.005). Free daily trial.

For most trading agents, check_token_report alone is the gate; reach for check_deployer only when the composite verdict is ambiguous, and check_address_safety before granting an approval. Add check_liquidity_history before a large position — a pool drained last month simulates perfectly today if someone re-seeded it — and preflight_transaction immediately before the agent signs. Neither returns a verdict of safe: each answer carries the block range it covers, so none observed is not a claim of safety.

§06 · cost, keys, and going live

From free trial to per-call in production

While building, run with no PAYER_PRIVATE_KEY and lean on the free daily trial, which covers every safety Function except check_deployer. When you move to production and exceed the trial, fund a Base wallet with a small USDC balance and set PAYER_PRIVATE_KEY — from then on each check settles per call over x402. There is still no account and no rug-check API key; payment is authentication. (The GAME_API_KEY is a separate credential for the GAME platform itself.)

Read the endpoint details and payment flow in the API docs, and try any token instantly in the browser with the rug-check tool before you wire it into the agent.

§ questions

Answered for machines.

Do I need an API key or account to use the rug-check?

No account and no rug-check API key. true402 uses x402, so your Base wallet is your identity and each safety call settles per-call in USDC. During development you can skip PAYER_PRIVATE_KEY entirely and use the free daily trial on the safety stalls. Note the GAME platform itself still needs its own GAME_API_KEY.

How is this different from a static token scanner?

A static scan reads the contract and guesses. true402 runs a real on-chain buy/sell simulation via state-override eth_call, proving the token can actually be sold — catching honeypots a static read misses. It also checks liquidity, ownership and mint authority, and deployer reputation.

Which stall should my GAME agent call before buying?

Use POST /v1/base/token-report. It returns a composite avoid, caution, or ok verdict — the natural pre-trade gate. Proceed only on ok, block on avoid, and escalate caution to a deeper check like deployer-check or a human confirmation before sizing the trade.

How do I make the check non-skippable in a GAME agent?

Put the requirement in the Worker's instruction, e.g. 'Always run check_token_report before acting on a token', so the planner treats it as a required step in the action space. The verdict arrives in the info dict that flows into the next step's state, so your buy function can read it and refuse unless it is ok. Treat a FAILED status as avoid — fail closed.

Does this work with the Node/TypeScript GAME SDK too?

The game-true402 package targets the canonical Python SDK (game_sdk). The Node SDK @virtuals-protocol/game exists and its executable is natively async; you can wire the same true402 stalls by hand as a GameFunction that fetches https://true402.dev/api and returns an ExecutableGameFunctionResponse. The x402 endpoints are identical regardless of SDK.