Skip to main content

recensus-sdk

Label your agent's transactions on Robinhood Chain so they show up on Recensus. The Python counterpart of @recensus/sdk on npm: the same label, the same safety rules and the same request signing, with a smaller surface. See What is here for exactly what it has.

pip install recensus-sdk
from recensus_sdk import Recensus, derive_agent_id
from web3 import Web3

w3 = Web3(Web3.HTTPProvider("https://rpc.mainnet.chain.robinhood.com"))

recensus = Recensus(
    agent_id=derive_agent_id(operator_address, "price-watcher"),
    autonomous=True,   # no human approves each send
    # test=True        # in staging: excluded from every public number
    w3=w3,
)

account = recensus.wrap(account)
account.send_transaction({"to": recipient, "value": 1_000_000})

That is the whole integration. Every send now carries 24 bytes on the end of its calldata, costing 372 gas, and the transaction appears on the public scoreboard as your agent.

What is here

  • Recensus(agent_id, framework=, autonomous=, test=, w3=, simulate_before_send=, denylist=, deny_selectors=, on_unlabelled=, logger=)
    • .tag(calldata) appends the label, with no checks.
    • .parse(calldata) reads a label back, or None.
    • .decide(to, data) says whether a call's shape may carry the label.
    • .prepare(to, data, value, sender, gas) runs decide, simulate and fall back for one call and returns the calldata to send.
    • .wrap(account) wraps an eth_account LocalAccount so its send_transaction(tx) carries the label. It needs w3= on the constructor, because sending needs a provider.
    • .sign_request(method, url, account, body) returns the five agent-lane headers.
  • derive_agent_id(operator, name), has_label(calldata), and the label functions: build_label, parse_label, append_label, strip_label.

What the TypeScript SDK has and this one does not:

  • No verify middleware. A server that verifies agent-lane requests uses requireRecensus (Hono) or requireRecensusExpress (Express) from @recensus/sdk on npm.
  • No writeContract wrapper. wrap covers send_transaction on a local account only. For a contract call, encode the calldata yourself (for example with web3.py's encode_abi) and send it through the wrapped account, or pass it through prepare.
  • wrap takes an account, not a client, and sign_request takes the method and URL as separate arguments.

The label never breaks a transaction

Three layers, in order:

  1. Call shape. The label goes only where trailing calldata is inert. Contract deployments, EntryPoint handleOps, data sent to an address with no code, a call with no data to a contract (even one marked tagSafe: true), and anything marked tagSafe: false are refused outright. Calldata given as bytes or HexBytes is checked exactly as hex is.
  2. Simulation. Before sending, the labelled call is simulated. If it would revert where the unlabelled one succeeds, the unlabelled call is sent and a warning is raised. This is on by default and the standard forbids shipping it off by default.
  3. A catch-all. Any unexpected failure while deciding sends unlabelled rather than failing. So does prepare with no w3 to simulate with, unless you turned simulation off.

If both the labelled and unlabelled calls revert, your own calldata is sent, so the error you see is yours and not ours.

Without web3

The label itself has no dependencies, so a reader or writer can live anywhere:

from recensus_sdk import build_label, parse_label

label = build_label("0x9f2a0c1e7b5d4a8f36c20e91d7b4a5c3", framework=0x0003)
data  = existing_calldata + label[2:]

parse_label(data).agent_id   # '0x9f2a0c1e7b5d4a8f36c20e91d7b4a5c3'

parse_label returns None rather than raising for anything that is not a well-formed label of a version it knows. A reader that guesses is a reader that mislabels somebody's transaction.

The agent lane

from recensus_sdk import canonical_body

url = "https://api.example.com/v1/thing"
body = {"hello": "world"}
headers = recensus.sign_request("POST", url, account, body=body)

# Send the exact bytes that were signed. `requests.post(url, json=body)`
# re-serialises with spaces after separators, so the server hashes different
# bytes and answers BAD_SIGNATURE.
requests.post(url, data=canonical_body(body),
              headers={**headers, "content-type": "application/json"})

Five headers an app can verify with requireRecensus or requireRecensusExpress from @recensus/sdk on npm, so a labelled agent can be given its own rate limits instead of being throttled like a spam bot.

Development

pip install -e '.[dev]'
pytest

The test suite checks this implementation against the same vectors as the TypeScript one, so the two cannot drift.

The fork suite

tests/fork/test_conformance.py is the RECENSUS-1 §4.4 conformance test. It sends each call through Recensus.wrap(account) on an Anvil fork of Robinhood Chain, checks the SDK did not fall back to unlabelled, reads the label off the mined transaction, and compares state with the same call sent unlabelled.

anvil --fork-url https://rpc.mainnet.chain.robinhood.com --port 8546
FORK_RPC=http://127.0.0.1:8546 pytest -m fork tests/fork

It covers every synthetic call type: a native transfer to an EOA, ERC-20 transfer, approve and transferFrom, WETH deposit and withdraw, a Uniswap v3 SwapRouter02 exactInputSingle, a Uniswap v2 swapExactTokensForTokens, and a Permit2 approve. It also checks that a plain value send to a contract (WETH, a pool) goes out unlabelled and lands. It does not replay a Universal Router transaction: that case is covered only by the TypeScript SDK's replay suite (packages/sdk-ts/test/fork/replay.test.ts).

Release files for recensus-sdk 1.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for recensus-sdk 1.2.0
File Size Uploaded
recensus_sdk-1.2.0.tar.gz 26.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for recensus-sdk 1.2.0
File Interpreter ABI Platform
recensus_sdk-1.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 45.0 kB

Release files / recensus_sdk-1.2.0.tar.gz

Download URL recensus_sdk-1.2.0.tar.gz
Size 26.8 kB
Tags Source
SHA-256 checksum
How to use checksums
f66854395c8b2b4a5977bdb3e29f8645ba36b08926115b32f63654702f828383
BLAKE2b-256 checksum
How to use checksums
897a083b67d41cc27af8c3c2d3e40cef2cec35dc56d3acc811164bd8c4a455cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.7

Release files / recensus_sdk-1.2.0-py3-none-any.whl

Download URL recensus_sdk-1.2.0-py3-none-any.whl
Size 18.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
73e58029d4fad16e4d4bb8572bb4b36c92961e718257c936f7538b75d01ad786
BLAKE2b-256 checksum
How to use checksums
e308a9699856816befea336a000711b24cc13b2fdc2cd8a29647e2dd1512736d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.7

Release history Release notifications | RSS feed

2.1.0

2 release files

2.0.0

2 release files

This release

1.2.0 This release

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page