Rug, price and copy-trade firewall for NEAR trading agents
An AI trading agent proposes a swap. Rugwall checks it for a rug-shaped token, a quote that is off the live pool price, a copied wallet with an insider track record, and the human's spending caps. Only then does it sign an ed25519 verdict. The NEAR contract stores the guard's public key and refuses any settle without a fresh, unused signature, so an agent that skips the guard can't settle, even inside its caps.
Everything on this page is read live from public NEAR testnet RPC and the public NearBlocks API, except the guard's verdicts, which are the saved output of the last live testnet demo run. Every transaction behind them is re-read from the chain while you watch. This page never asks for a wallet, a key or any personal data.
Try it in 1 minute
no wallet, no setupPick what the agent tries to do. You see what the guard decided in the last live testnet run, then your browser checks the guard's ed25519 signature itself and reads the real transaction from NEAR testnet now. Clicking sends nothing to the chain.
Start with 1. Safe swap, then try the attacks.
Demo video
The real INTENTGUARD_NETWORK=testnet python3 guard/demo.py run against this contract, recorded with live 1Click dry quotes and Ref Finance pool reserves.
On-chain activity
The latest transactions on the contract account, from NearBlocks. A rejected settle shows the contract's own panic code, read from the transaction outcome over RPC.
| Time (UTC) | Method | Signer | Result | Tx |
|---|---|---|---|---|
| Loading… | ||||
Guard verdict log
The 13 scenarios from the last live testnet run. A guard DENY never reaches the chain. An ALLOW carries a signed verdict and settles. Scenarios 8 to 13 are a compromised agent calling the contract directly. Each transaction is re-read from NEAR testnet when you open this page.
| # | Scenario | Guard | Contract | Why | Data | Tx |
|---|
Add Rugwall to your agent
3 steps: ask the guard → get a signed verdict → settle with itGive any MCP client (Claude Desktop, Cursor, an agent framework) a read-only pre-trade check. It runs the four checks on a real 1Click dry quote and the live Ref pool. It never signs.
{"mcpServers": {"rugwall": {
"command": "python3",
"args": ["/path/to/intentguard/mcp/rugwall_mcp.py"]
}}}
// then the agent calls, before every swap:
rugwall_check_swap {"amount_usd": 50} → ALLOW + live quote vs pool
rugwall_check_swap {"amount_usd": 150, "copied_wallet": "insider_demo"} → DENY + 3 reasons
rugwall_policy {"agent": "bot1"} → live caps, kill switch, guard keyThe guard process holds the verdict key, outside the agent and outside the repo. The agent only gets the signed verdict and passes it to settle.
from intentguard_guard import guard, verdict
from intentguard_guard.contract_testnet import ContractTestnet
contract = ContractTestnet("intentguard-5821c5.testnet", "guard2.intentguard-5821c5.testnet",
"~/.near-credentials/testnet/agent-keys/bot1.json") # agent's settle-only key
signer = verdict.GuardSigner.from_key_file("~/.near-credentials/testnet/agent-keys/guard-verdict.json")
ctx = {"contract": contract, "treasury_usd": 1000, "signer": signer,
"contract_id": "guard2.intentguard-5821c5.testnet"}
v = guard.evaluate(proposal, ctx) # risk caps, rug check, quote vs pool, copied wallet
if v["allow"]: # ALLOW comes with an ed25519 verdict; DENY is never signed
contract.settle(contract.agent_public_key(), v["amount_raw"], proposal["token_in"],
proposal["token_out"], v["intent_hash"], verdict=v["signed_verdict"])
else:
print("blocked:", v["reasons"])What actually reaches NEAR. The agent's function-call key can only call settle on this contract. Without a fresh, unused guard signature over these exact fields, the call fails.
near contract call-function as-transaction guard2.intentguard-5821c5.testnet settle \
json-args '{"amount":"50000000","token_in":"<usdc>","token_out":"wrap.near",
"intent_hash":"<sha256 hex>",
"verdict":{"nonce":"<u64>","expires_at_ns":"<now + ≤10 min>","signature":"<128 hex chars>"}}' \
prepaid-gas '30 Tgas' attached-deposit '0 NEAR' \
sign-as guard2.intentguard-5821c5.testnet network-config testnet \
sign-with-access-key-file agent-key.json send
# no verdict → ERR_NO_VERDICT · changed field → ERR_BAD_SIG · reused → ERR_REPLAYThe whole loop in one file, offline, no keys: a safe swap settles, a rigged quote is denied, and skipping the guard gets ERR_NO_VERDICT.
git clone https://github.com/Crypthorious/intentguard.git && cd intentguard python3 -m pip install -r requirements.txt python3 examples/agent_quickstart.py
safe $50 swap -> guard ALLOW, signed; contract settled 50.00 USDC, 200.00 left today rigged quote (-8%) -> guard DENY, nothing sent: price: quote deviates -8.00% from pool-reserve fair price (> 2.0%) skip the guard -> contract rejected: ERR_NO_VERDICT
Check it yourself: do X → see Y
- Do: press Try it in 1 minute and click through the 8 cases. See: the safe swap settles; the rug, the rigged quote and the insider copy are denied and never reach the chain; the 4 attacks by a compromised agent are rejected on-chain with
ERR_NO_VERDICT,ERR_REPLAYandERR_BAD_SIG, and your browser shows why each signature does or doesn't verify. - Do: look at Live contract policy. See:
guard_public_keyset to the guard's ed25519 key, per-tx cap 100 USDC and daily cap 250 USDC for agentbot1, and the kill switch off. - Do: open the On-chain activity table. See: green
settlerows (the guard signed them) and red rows withERR_NO_VERDICT,ERR_REPLAY,ERR_BAD_SIG,ERR_KILL_SWITCH_ONandERR_OVER_PER_TX_CAP. Click any hash to check it on NearBlocks. - Do: find scenario
9_bypass_no_verdictin the verdict log. See: a $10 settle, inside every cap, rejected on-chain withERR_NO_VERDICT, because the agent skipped the guard. - Do: clone the repo and run
./run.sh. See: 27 contract tests, 53 guard tests andRESULT: 13/13 scenarios as expected. It works offline too: the demo falls back to fixtures and says so. - Do: run
python3 scripts/verify_testnet_code.py. See: the committed wasm is byte-identical to the code on this contract.
Repo: github.com/Crypthorious/intentguard · Contract: guard2.intentguard-5821c5.testnet
What is real here
- Real, on testnet: the Rust contract, its caps, the kill switch, the function-call key limited to
settle, and on-chain ed25519 verification of every guard verdict, with replay and expiry protection. - Real, read-only, mainnet data: the 1Click quote (
dry: true, so no deposit address and no funds), Ref Finance pool 4512 reserves, and theft_metadataof USDC and wNEAR. - Fixtures: the two meme tokens (
goodmeme.pump,rugmeme.pump) and the copied-wallet histories. - Saved, then re-checked live: the guard's verdicts come from the last live testnet run (the guard runs off-chain and its key never touches this page). The transactions behind them, the guard key and the nonce state are read from the chain while you watch.
- Not done:
settlerecords the spend and emits an event. It does not forward tointents.nearor move tokens. There is one guard key, held by one guard process. No mainnet, no real money.