Zero-trust Ethereum signing via URL. For AI agents and humans.
- Review and sign EIP-712 typed data — orders, permits, attestations
- Review and send transactions — approvals, transfers, contract calls
- 39KB single HTML file — no backend, no tracking, no cookies, IPFS-hosted
- A service, script, or AI agent builds an unsigned payload and encodes it in a URL
- The user opens the link, reviews the decoded fields (human-readable, not raw hex)
- Connects their wallet (MetaMask, Rabby, Ledger, Trezor via EIP-6963)
- Signs on-device — private key never leaves the hardware wallet
The payload lives in the URL fragment (after #) — it's never sent to any server.
Python SDK:
from signthat import generate_signing_url
url = generate_signing_url("tx", {
"to": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"data": "0x095ea7b3" + spender[2:].zfill(64) + hex(amount)[2:].zfill(64),
"chainId": 137
})
# Send this URL to the userTypeScript SDK:
import { generateSigningUrl } from 'signthat';
const url = generateSigningUrl('712', {
primaryType: 'Order',
types: { /* ... */ },
domain: { name: 'Exchange', chainId: 137 },
message: { maker: '0x...', amount: '1000000', side: '0' }
});REST API:
curl -X POST https://api.signthat.org/api/v1/generate \
-H "Content-Type: application/json" \
-d '{"format": "tx", "payload": {"to": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", "chainId": 137, "data": "0x095ea7b3..."}}'MCP Server (for Claude Code, Cursor, etc.):
{
"mcpServers": {
"signthat": {
"command": "uvx",
"args": ["signthat-mcp"]
}
}
}Then your AI agent can call generate_signing_url("tx", payload) directly.
https://<host>/#<format>/<base64url-json>.<checksum>
| Component | Description |
|---|---|
format |
712 (EIP-712 signing) or tx (transaction broadcast) |
base64url-json |
JSON payload, base64url-encoded (RFC 4648 §5, no padding) |
checksum |
First 6 hex chars of SHA-256 of the base64url string |
To propose a transaction to a human user, generate a SignThat URL and send it to them (via chat, notification, or any channel). The user opens the link in their browser, reviews the decoded transaction details, connects their wallet, and confirms.
- EIP-712 signing: the user copies the resulting signature back to you
- Transactions: broadcast happens automatically; the user shares the tx hash
No API keys. No wallet connection sessions. No private key access required.
- Fragment-only URLs — the server never sees the payload (fragments aren't sent in HTTP requests)
- Single static HTML — all code inlined, no external scripts, no CDN
- IPFS-hosted — content hash (CID) guarantees the page hasn't been tampered with
- URL checksums — 6-char integrity check detects corruption or truncation
- Verifiable builds — git commit hash and SHA-256 displayed in footer
Ethereum, Polygon, Optimism, Arbitrum, Base, Sepolia (with block explorer links). Any EVM chain works — known chains get human-readable names and explorer integration.
- ERC-20 function calls decoded:
approve(),transfer(),transferFrom() - Known tokens labeled (USDC, USDC.e, USDT)
- Addresses linked to block explorers with copy buttons
- Token amounts formatted with correct decimals
- Unlimited approvals detected and labeled
# Build
bash build.sh
# Test (34 Playwright e2e tests)
npx playwright test
# Deploy to IPFS
bash deploy.sh
# Run API locally
cd api && pip install -r requirements.txt && uvicorn server:app --reloadMIT
Built by @uwwgo.