LangChain toolkit for reliable Web3 execution via KeeperHub
Project description
langchain-keeperhub
LangChain toolkit for reliable Web3 execution via KeeperHub.
Give any LangChain agent the ability to transfer tokens, call smart contracts, and monitor on-chain executions — all backed by KeeperHub's retry logic, gas optimization, MEV protection, and full audit trail.
Built for the ETHGlobal OpenAgents hackathon.
Install
pip install langchain-keeperhub
Or install locally for development:
git clone https://github.com/devendra116/langchain-keeperhub.git
cd langchain-keeperhub
pip install -e ".[dev]"
Quick Start
pip install langchain-keeperhub langchain-google-genai langgraph python-dotenv
from dotenv import load_dotenv
from langchain_google_genai import ChatGoogleGenerativeAI
from langgraph.prebuilt import create_react_agent
from langchain_keeperhub import KeeperHubToolkit
load_dotenv()
toolkit = KeeperHubToolkit() # reads KEEPERHUB_API_KEY from env
tools = toolkit.get_tools()
agent = create_react_agent(
ChatGoogleGenerativeAI(model="gemini-3-flash-preview", temperature=0),
tools,
)
result = agent.invoke(
{"messages": [("user", "What blockchain networks does KeeperHub support?")]}
)
print(result["messages"][-1].content)
For safer local development, you can force write tools to only target testnets:
toolkit = KeeperHubToolkit(testnet_only=True)
This blocks transfer_funds, contract_call, and check_and_execute when the
resolved network is not marked as a testnet by KeeperHub's chain registry.
For stricter control, also allowlist the exact chain IDs your app may write to:
toolkit = KeeperHubToolkit(
testnet_only=True,
allowed_chain_ids={"11155111", "84532"}, # Sepolia, Base Sepolia
)
With an allowlist, all other write networks are treated as unsupported before any transaction request is sent.
See examples/basic_agent.py for a complete runnable script.
Environment Variables
| Variable | Required | Description |
|---|---|---|
KEEPERHUB_API_KEY |
Yes | Org-scoped API key (kh_ prefix) from app.keeperhub.com |
GOOGLE_API_KEY |
For example | Required by langchain-google-genai for Gemini |
Tools
| Tool | API Endpoint | Description |
|---|---|---|
keeperhub_list_chains |
GET /api/chains |
List supported blockchain networks |
keeperhub_fetch_contract_abi |
GET /api/chains/{id}/abi |
Fetch verified contract ABI |
keeperhub_get_wallet_address |
GET /api/user |
Get the connected KeeperHub wallet address from the user profile |
keeperhub_transfer_funds |
POST /api/execute/transfer |
Send native or ERC-20 tokens |
keeperhub_contract_call |
POST /api/execute/contract-call |
Read/write any smart contract |
keeperhub_check_and_execute |
POST /api/execute/check-and-execute |
Conditional read-then-write |
keeperhub_get_execution_status |
GET /api/execute/{id}/status |
Poll execution status and tx hash |
keeperhub_list_executions |
local DB | Query past write executions (only available when history=... is enabled) |
keeperhub_get_wallet_address reads the authenticated KeeperHub user profile and
returns its walletAddress. If no wallet is connected, the tool returns a
warning telling the agent to ask the user to create/connect a KeeperHub wallet
or explicitly provide the address to use.
Retries, timeouts & errors
KeeperHubClient keeps retry behavior narrow and explicit:
| Failure | Retries | Backoff |
|---|---|---|
Network error (httpx.HTTPError) on GET |
3 | linear: 1s, 2s, 3s |
Network error (httpx.HTTPError) on non-GET |
0 | no retry (prevents duplicate writes) |
| HTTP 429 (rate limit) | 3 | honors Retry-After header |
| HTTP 4xx (other) / 5xx | 0 — raises typed exception immediately | — |
- Per-request timeout: 60s (override via
KeeperHubClient(timeout=...)). - Every retry and every 4xx/5xx response body is logged at
WARNING/ERRORon thelangchain_keeperhub.clientlogger. - LangGraph agents have their own retry loop: when a tool raises, the error
is fed back to the LLM as a
ToolMessageand the model may call the tool again. Cap this withconfig={"recursion_limit": N}onagent.invoke/agent.stream(seeexamples/basic_agent.py).
To see retry/error logs, configure logging in your app:
import logging
logging.basicConfig(level=logging.INFO)
Write + poll pattern
Write tools (transfer_funds, contract_call, check_and_execute) return
structured output containing an execution_id. The agent should follow up with
get_execution_status to poll until the transaction settles and surface the
final tx hash to the user.
Execution history (opt-in)
Off by default. Pass history=True (or any custom ExecutionStore) and every
successful write — transfer_funds, contract_call writes, and
check_and_execute runs that fired — is persisted locally. Status polls are
folded back into the same row, so the DB always reflects the latest tx hash,
gas used, and terminal state. Reads are never persisted.
from langchain_keeperhub import KeeperHubToolkit
# Default: SqliteExecutionStore at ~/.keeperhub/executions.db
toolkit = KeeperHubToolkit(history=True)
# Or point at a custom path / swap in your own ExecutionStore
from langchain_keeperhub import SqliteExecutionStore
toolkit = KeeperHubToolkit(history=SqliteExecutionStore("./executions.db"))
When history is enabled, the toolkit additionally exposes
keeperhub_list_executions, which the agent can call to answer questions
about past activity. Direct (non-LLM) callers can use the same store from
Python:
recent = await toolkit.history.list(status="pending", limit=10)
for r in recent:
print(r.execution_id, r.kind, r.status, r.transaction_hash)
Why it's useful:
- Receipts / audit. "Show me everything I sent today" becomes one tool call.
- Don't double-pay. Agent checks history before issuing a similar transfer.
- Crash recovery. A new session can resume polling rows that were left in
pending/running. - Long-running automation. Treasury bots and streaming-payment loops get an audit trail without shipping their own DB layer.
The store interface is a small Protocol (record, update_status, list,
get, aclose); the SQLite default is stdlib-only and serializes blocking
calls through asyncio.to_thread. Failures from the store are logged but
never raised — history can never be the reason a successful transaction
looks like a failure.
Direct client usage (no LLM)
import asyncio
from langchain_keeperhub import KeeperHubClient
async def main():
client = KeeperHubClient() # reads KEEPERHUB_API_KEY from env
chains = await client.list_chains()
print(chains)
await client.aclose()
asyncio.run(main())
Architecture
LangChain Agent
└── KeeperHubToolkit
├── ListChainsTool ─────────┐
├── FetchContractABITool ───┤
├── GetWalletAddressTool ───┤
├── TransferFundsTool ──────┤
├── ContractCallTool ───────┼── KeeperHubClient (httpx) ──► KeeperHub REST API
├── CheckAndExecuteTool ────┤ │ (app.keeperhub.com)
├── GetExecutionStatusTool ─┤ │
└── ListExecutionsTool* ────┘ ▼
ExecutionStore
(SQLite default, optional)
* ListExecutionsTool only registered when history=... is set.
Development
pip install -e ".[dev]"
pytest
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file langchain_keeperhub-0.2.0.tar.gz.
File metadata
- Download URL: langchain_keeperhub-0.2.0.tar.gz
- Upload date:
- Size: 39.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20f774bc1a53e7f349d80a1f5982a316e294392298ba91046790d103903bb27f
|
|
| MD5 |
b84de12c51a81832c0c6b9e55f4c412a
|
|
| BLAKE2b-256 |
c6e65e6218420ba9544fb8861fd5df9d3b40b59a694dff342b6f8fa4a643077e
|
File details
Details for the file langchain_keeperhub-0.2.0-py3-none-any.whl.
File metadata
- Download URL: langchain_keeperhub-0.2.0-py3-none-any.whl
- Upload date:
- Size: 32.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.0.1 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c9af26340f3887f112c44dea68f11369be685b49ba0aaa73e85544a82b559aa
|
|
| MD5 |
34e97921a95700578e4ac82677e225ba
|
|
| BLAKE2b-256 |
85600ec3de0404e5fcb47388c76a582593b56a4c225d0b728781d5b68e17ee18
|