diff --git a/plugins/x402BazaarPlugin/.npmignore b/plugins/x402BazaarPlugin/.npmignore new file mode 100644 index 0000000..6d6c6ff --- /dev/null +++ b/plugins/x402BazaarPlugin/.npmignore @@ -0,0 +1,5 @@ +src +example +tsconfig.json +*.test.ts +plugin_metadata.yml diff --git a/plugins/x402BazaarPlugin/README.md b/plugins/x402BazaarPlugin/README.md new file mode 100644 index 0000000..d19510b --- /dev/null +++ b/plugins/x402BazaarPlugin/README.md @@ -0,0 +1,61 @@ +# x402 Bazaar Plugin for G.A.M.E. + +Give a [G.A.M.E.](https://docs.game.virtuals.io/) agent onchain intelligence on +Base — token risk, wallet net worth, OFAC sanctions, an AI token verdict and a +market brief — each **paid per call in USDC over the [x402](https://x402.org) +protocol**, with no API keys. + +> G.A.M.E. decides & acts · **x402 Bazaar tells the agent what to act on** — +> risk, intelligence and AI verdicts — before it commits. + +Backed by [x402 Bazaar](https://402.com.tr), a marketplace of 118 pay-per-call +services on Base — the live list is always at +. The agent's wallet key is used locally to +sign x402 payments and is **never sent anywhere**. + +## Installation + +```bash +npm install @virtuals-protocol/game-x402-bazaar-plugin +``` + +## Usage + +```ts +import { GameAgent } from "@virtuals-protocol/game"; +import X402BazaarPlugin from "@virtuals-protocol/game-x402-bazaar-plugin"; + +const plugin = new X402BazaarPlugin({ + credentials: { privateKey: process.env.AGENT_PRIVATE_KEY }, // Base wallet w/ USDC +}); + +const agent = new GameAgent(process.env.GAME_API_KEY, { + name: "Base Safety Agent", + goal: "Vet Base tokens before acting", + description: "...", + workers: [plugin.getWorker()], +}); + +await agent.init(); +await agent.run(60); +``` + +## Functions + +| Function | What it does | +|---|---| +| `get_ai_token_report` | AI buy/avoid verdict for a token (safety score, risks, positives) | +| `get_token_risk` | Fast token safety scan (honeypot, taxes, ownership, holders) | +| `get_wallet_networth` | Wallet token portfolio + USD net worth | +| `check_sanctions` | OFAC sanctions screening for an address | +| `get_market_brief` | AI situational brief of the Base token market | + +## Credentials + +| Option / Env | Purpose | +|---|---| +| `credentials.privateKey` / `AGENT_PRIVATE_KEY` | Base wallet (holds USDC) used to pay x402 calls. Local only. | +| `origin` / `X402_BAZAAR_ORIGIN` | optional — defaults to `https://402.com.tr` | + +Discover all services: · +docs: · MCP: `npx x402-bazaar-mcp` diff --git a/plugins/x402BazaarPlugin/package.json b/plugins/x402BazaarPlugin/package.json new file mode 100644 index 0000000..8b1ecb1 --- /dev/null +++ b/plugins/x402BazaarPlugin/package.json @@ -0,0 +1,32 @@ +{ + "name": "@virtuals-protocol/game-x402-bazaar-plugin", + "version": "0.1.0", + "description": "A plugin for Virtuals Protocol G.A.M.E. to get onchain data & AI reports for Base agents — token risk, wallet net worth, OFAC sanctions, an AI token verdict and a market brief — each paid per call in USDC over the x402 protocol (no API keys).", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "scripts": { + "tsup": "tsup src/index.ts --dts --format cjs,esm --out-dir dist", + "build": "npm run tsup" + }, + "author": "x402 Bazaar", + "license": "MIT", + "devDependencies": { + "@types/node": "^22.10.3", + "tsup": "^8.3.5", + "typescript": "^5.7.3" + }, + "dependencies": { + "@virtuals-protocol/game": "^0.1.7", + "@x402/fetch": "^2.16.0", + "@x402/evm": "^2.16.0", + "@x402/extensions": "^2.16.0", + "viem": "^2.21.0" + }, + "files": [ + "dist" + ], + "repository": { + "type": "git" + } +} diff --git a/plugins/x402BazaarPlugin/plugin_metadata.yml b/plugins/x402BazaarPlugin/plugin_metadata.yml new file mode 100644 index 0000000..17ebe16 --- /dev/null +++ b/plugins/x402BazaarPlugin/plugin_metadata.yml @@ -0,0 +1,18 @@ +# General Information +plugin_name: "x402 Bazaar" +author: "x402 Bazaar (@sukrutkrdg)" +logo_url: "https://402.com.tr/brand/icon" +release_date: "2026-06" + +# Description +short_description: "Onchain data & AI reports for Base agents — token risk, wallet intel, OFAC sanctions, AI verdicts — paid per call over x402, no API keys." +detailed_description: "x402 Bazaar is a marketplace of 118 pay-per-call services on Base; this plugin surfaces the ones a trading or research agent needs as G.A.M.E. functions: AI token due-diligence verdict, fast token risk scan, wallet net worth, OFAC sanctions screening, and an AI market brief. Each call settles a tiny USDC micro-payment over the x402 protocol on Base (gasless for the payer); the wallet key stays local. Use it to give an agent the intelligence to know what to act on before it acts. Also available as an MCP server (npx x402-bazaar-mcp)." + +# Media & Assets +plugin_logo_url: "https://402.com.tr/brand/icon" +documentation_url: "https://402.com.tr/agents" + +# Contact & Support +x_account_handle: "@sukrutkrdg" +support_contact: "sukrutkrdg@gmail.com" +community_url: "https://t.me/Bazaar402_bot" diff --git a/plugins/x402BazaarPlugin/src/example.ts b/plugins/x402BazaarPlugin/src/example.ts new file mode 100644 index 0000000..0259a5d --- /dev/null +++ b/plugins/x402BazaarPlugin/src/example.ts @@ -0,0 +1,20 @@ +import { GameAgent } from "@virtuals-protocol/game"; +import X402BazaarPlugin from "./x402BazaarPlugin"; + +// A Base wallet holding a little USDC; used locally to sign x402 payments. +const plugin = new X402BazaarPlugin({ + credentials: { privateKey: process.env.AGENT_PRIVATE_KEY }, +}); + +const agent = new GameAgent(process.env.GAME_API_KEY as string, { + name: "Base Safety Agent", + goal: "Vet Base tokens and wallets for safety before acting.", + description: + "An agent that uses x402 Bazaar to pull token risk, wallet net worth, OFAC sanctions, AI token verdicts and a market brief — paying per call over x402.", + workers: [plugin.getWorker()], +}); + +(async () => { + await agent.init(); + await agent.run(60, { verbose: true }); +})(); diff --git a/plugins/x402BazaarPlugin/src/index.ts b/plugins/x402BazaarPlugin/src/index.ts new file mode 100644 index 0000000..191a2a3 --- /dev/null +++ b/plugins/x402BazaarPlugin/src/index.ts @@ -0,0 +1,3 @@ +import X402BazaarPlugin from "./x402BazaarPlugin"; + +export default X402BazaarPlugin; diff --git a/plugins/x402BazaarPlugin/src/x402BazaarPlugin.ts b/plugins/x402BazaarPlugin/src/x402BazaarPlugin.ts new file mode 100644 index 0000000..2f8cc31 --- /dev/null +++ b/plugins/x402BazaarPlugin/src/x402BazaarPlugin.ts @@ -0,0 +1,171 @@ +import { + GameWorker, + GameFunction, + ExecutableGameFunctionResponse, + ExecutableGameFunctionStatus, +} from "@virtuals-protocol/game"; +import { x402Client, wrapFetchWithPayment } from "@x402/fetch"; +import { ExactEvmScheme } from "@x402/evm/exact/client"; +import { BuilderCodeClientExtension } from "@x402/extensions/builder-code"; +import { privateKeyToAccount } from "viem/accounts"; + +interface IX402BazaarPluginOptions { + id?: string; + name?: string; + description?: string; + credentials?: { privateKey?: string }; + origin?: string; +} + +/** + * x402 Bazaar plugin for G.A.M.E. + * + * Gives a GAME agent onchain intelligence on Base — token risk, wallet net + * worth, OFAC sanctions, an AI token verdict and a market brief — each paid per + * call in USDC over the x402 protocol (no API keys). The wallet key is used + * locally to sign x402 payments and is never sent anywhere. + */ +class X402BazaarPlugin { + private id: string; + private name: string; + private description: string; + private privateKey?: string; + private origin: string; + private payingFetch: ReturnType | null = null; + + constructor(options: IX402BazaarPluginOptions = {}) { + this.id = options.id || "x402_bazaar_worker"; + this.name = options.name || "x402 Bazaar Worker"; + this.description = + options.description || + "Onchain intelligence for Base agents via x402 Bazaar — token risk, wallet net worth, OFAC sanctions, AI token verdicts and a market brief, each paid per call in USDC over x402 (no API keys)."; + this.privateKey = options.credentials?.privateKey || process.env.AGENT_PRIVATE_KEY; + this.origin = (options.origin || process.env.X402_BAZAAR_ORIGIN || "https://402.com.tr").replace(/\/$/, ""); + } + + // Lazy paying-fetch — the key is needed only when a function actually runs. + private getPayingFetch() { + if (this.payingFetch) return this.payingFetch; + if (!this.privateKey) { + throw new Error( + "x402 Bazaar: a Base wallet private key is required to pay for calls. Pass credentials.privateKey or set AGENT_PRIVATE_KEY.", + ); + } + const key = (this.privateKey.startsWith("0x") ? this.privateKey : `0x${this.privateKey}`) as `0x${string}`; + const account = privateKeyToAccount(key); + const client = new x402Client(); + client.register("eip155:8453", new ExactEvmScheme(account)); // Base mainnet + client.registerExtension(new BuilderCodeClientExtension("x402_bazaar_cli")); + this.payingFetch = wrapFetchWithPayment(fetch, client); + return this.payingFetch; + } + + private async call(service: string, query?: string): Promise { + const url = `${this.origin}/api/x402/${service}${query ? `?${query}` : ""}`; + const res = await this.getPayingFetch()(url); + const text = await res.text(); + if (!res.ok) throw new Error(text.slice(0, 200)); + return text; + } + + private addressFunction(name: string, service: string, description: string) { + return new GameFunction({ + name, + description, + args: [ + { name: "address", type: "string", description: "A 0x… Base address (token or wallet)" }, + ] as const, + executable: async (args, logger) => { + try { + const address = (args.address || "").trim(); + if (!/^0x[a-fA-F0-9]{40}$/.test(address)) { + return new ExecutableGameFunctionResponse( + ExecutableGameFunctionStatus.Failed, + "A valid 0x… Base address is required.", + ); + } + logger?.(`x402 Bazaar: ${service} ${address}`); + const data = await this.call(service, `address=${address}`); + return new ExecutableGameFunctionResponse(ExecutableGameFunctionStatus.Done, data); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : String(e); + return new ExecutableGameFunctionResponse( + ExecutableGameFunctionStatus.Failed, + `x402 Bazaar (${service}) failed: ${msg}`, + ); + } + }, + }); + } + + public get aiTokenReportFunction() { + return this.addressFunction( + "get_ai_token_report", + "ai-token-report", + "AI due-diligence verdict for a Base token (avoid→favorable) with a 0-100 safety score, risks and positives. Use before buying or interacting with a token.", + ); + } + + public get tokenRiskFunction() { + return this.addressFunction( + "get_token_risk", + "token-risk", + "Fast token safety scan (honeypot, taxes, ownership, holder concentration) for a Base token.", + ); + } + + public get walletNetworthFunction() { + return this.addressFunction( + "get_wallet_networth", + "wallet-networth", + "Full token portfolio and USD net worth for a Base wallet.", + ); + } + + public get sanctionsFunction() { + return this.addressFunction( + "check_sanctions", + "sanctions", + "Screen a Base address against the OFAC sanctions list before sending funds or interacting.", + ); + } + + public get marketBriefFunction() { + return new GameFunction({ + name: "get_market_brief", + description: + "AI situational brief of the Base token market — mood, highlights, new & notable launches, cautions. Use for market context before trading.", + args: [] as const, + executable: async (_args, logger) => { + try { + logger?.("x402 Bazaar: ai-market-brief"); + const data = await this.call("ai-market-brief"); + return new ExecutableGameFunctionResponse(ExecutableGameFunctionStatus.Done, data); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : String(e); + return new ExecutableGameFunctionResponse( + ExecutableGameFunctionStatus.Failed, + `x402 Bazaar (market-brief) failed: ${msg}`, + ); + } + }, + }); + } + + public getWorker(): GameWorker { + return new GameWorker({ + id: this.id, + name: this.name, + description: this.description, + functions: [ + this.aiTokenReportFunction, + this.tokenRiskFunction, + this.walletNetworthFunction, + this.sanctionsFunction, + this.marketBriefFunction, + ], + }); + } +} + +export default X402BazaarPlugin; diff --git a/plugins/x402BazaarPlugin/tsconfig.json b/plugins/x402BazaarPlugin/tsconfig.json new file mode 100644 index 0000000..31503ec --- /dev/null +++ b/plugins/x402BazaarPlugin/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +}