Skip to content

Release 0.1.0 — first stable#1

Merged
roger-gan merged 38 commits into
mainfrom
smoke-test
May 8, 2026
Merged

Release 0.1.0 — first stable#1
roger-gan merged 38 commits into
mainfrom
smoke-test

Conversation

@boboliu-1010

@boboliu-1010 boboliu-1010 commented Apr 29, 2026

Copy link
Copy Markdown
Collaborator

Release 0.1.0 — first stable

Consolidates everything from 0.1.0-beta.5 through 0.1.0-beta.17 into the first stable release on PyPI.

After merge, the user-facing install drops the --pre flag:

pip install bankofai-x402-cli   # → 0.1.0

What you get

  • Three commandsx402-cli pay <url>, x402-cli serve, x402-cli roundtrip. Each has a polished --help listing supported networks, tokens, schemes, plus worked examples in the top-level --help.
  • Wallet — fully delegated to bankofai-agent-wallet (raw_secret / local_secure / privy / mnemonic). One private key derives both EVM and TRON addresses; no --wallet flag.
  • Settlement schemes — auto-picked per (network, token) registry; override with --scheme exact_gasfree | exact_permit | exact.
  • Networks — TRON (tron:mainnet / tron:nile / tron:shasta) and EVM (eip155:56 / eip155:97).
  • Tokens — USDT / USDC / USDD / DHLU through the registry, plus --asset + --decimals for any other ERC-20.
  • Friendly errors — 10 classified codes (e.g. WALLET_NOT_CONFIGURED, TRON_ACCOUNT_NOT_ACTIVATED, INSUFFICIENT_GASFREE_BALANCE, INSUFFICIENT_GAS, RATE_LIMITED, PERMIT_REVERTED), each with a one-line resolution hint.
  • TRON raw_data_hex compat shim (_tron_patch.py) — fills in the field that all released tronpy versions (0.4–0.6) leave empty, so privy-style wallets that strictly require it can sign approvals. Bit-correct vs tronpy's own offline txid calculation. Idempotent and forward-compatible.
  • TokenRegistry → AssetRegistry import-drift tolerance — pre-installed older / newer SDKs are auto-aligned by pip; cli accepts either symbol.
  • Pinned dependency upper boundsbankofai-x402[evm,tron]>=0.5.9,<0.6, bankofai-agent-wallet>=2.4,<3, plus caps on click/httpx/pydantic/fastapi/uvicorn. A future major-version release of any dependency cannot silently land in user environments.
  • English-only user-facing docs — README on PyPI plus docs/manual-test-guide.md with three full hands-on walkthroughs (TRON GasFree, TRON permit, BSC permit) ending in real on-chain receipts.

Verification

  • ✅ Python 3.11 + 3.12 CI green (test (3.11) PASS, test (3.12) PASS, all 30 unit tests).
  • pip install bankofai-x402-cli from a clean venv pulls 3 BofAI packages + full transitive stack, no pip check issues.
  • ✅ Live on-chain receipts on three networks × three schemes; full table in CHANGELOG.md [0.1.0].
  • ✅ QA-confirmed on Windows / PowerShell with TRON mainnet exact_permit after b16's raw_data_hex patch landed (3 mainnet TXs).

Post-merge release plan

  1. git checkout main && git pull
  2. python3 -m build
  3. twine upload dist/bankofai_x402_cli-0.1.0* (no --pre)
  4. git tag -a v0.1.0 -m "release: 0.1.0 — first stable" + git push --tags

Known limitations (not cli bugs, documented)

  • Fresh TRON addresses must be activated (receive any inflow once) before they can sign contract calls. The cli surfaces this as TRON_ACCOUNT_NOT_ACTIVATED with a one-line fix hint.
  • BSC mainnet USDC 0x8AC76a51… does not implement transferWithAuthorization or permit (verified by bytecode scan). --scheme exact against this token reverts at facilitator's settle step. (token, scheme) compatibility is a facilitator concern.

bobo and others added 26 commits April 29, 2026 16:02
- Import EvmClientSigner and TronClientSigner from correct module path
  (bankofai.x402.signers.client instead of bankofai.x402.signers)
- Fix EVM signer creation from private key using eth_account.Account
- Fix TRON signer creation from private key using tronpy.hdwallet
- Properly initialize signer addresses after creation

This enables both agent-wallet and env-var wallet modes to work correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add comprehensive documentation and AI-friendly project structure:

**New Directories:**
- .claude/ — AI development guidance and automation
- specs/ — Protocol and design specifications

**AI Structure (.claude/):**
- CLAUDE.md — Project overview and conventions
- rules/python.md — Python coding guidelines
- smoke-test.sh — Automated smoke test suite

**Specifications (specs/):**
- README.md — Documentation guide
- server.md — Server command design (architecture, endpoints, flow)
- client.md — Client command design (architecture, flow, signers)
- smoke-tests.md — Test specification (6 tests, all passing)

**Benefits:**
1. Enables Claude and other AI agents to contribute safely
2. Documents design decisions before implementation
3. Provides testing specification and coverage analysis
4. Follows x402 project conventions for consistency
5. Separates concerns: how to build vs. what we're building

**Test Coverage:**
✓ Server startup
✓ /health endpoint
✓ /.well-known/x402 configuration
✓ /pay GET (402 challenge)
✓ Signer initialization (EVM + TRON)
✓ Output formatting

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ield names

- Rename binary from x402-tools to x402-cli
- Rename commands: server → serve, client → pay
- Add new roundtrip command for one-shot testing
- Fix field naming: --decimal → --rawAmount, --max-decimal → --max-rawAmount
- Update all documentation and specs to reflect new names and field conventions
- Support daemon and foreground modes for serve command
- Update smoke test script with new command names and field names

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Update x402-tools references to x402-cli
- Fix --rawAmounts to --decimals where appropriate
- Update command examples in debug section
- Replace non-existent key_to_address with eth_keys.PrivateKey
- Works for TRON address derivation from private key
- Update error messages to use x402-cli instead of x402-tools
- Allow overriding facilitator URL via environment variable
- Default to https://facilitator.bankofai.io
- Enables testing with different facilitators
- Initialize ExactGasFreeClientMechanism with required clients kwarg
- Add LocalTronWallet/LocalEvmWallet for proper EIP-712/TIP-712 signing
- Forward 402 extensions (paymentPermitContext) to create_payment_payload
- Convert EVM hex to TRON Base58 before GasFree API calls
- Bump HTTP client timeout from 10s to 60s for facilitator settlement
- Bump version to 0.1.0-beta.5

Verified on-chain:
- TRON Nile (exact_gasfree)
- BSC Testnet (exact_permit)
…rences

- beta.5 imports fastapi/uvicorn/eth-account/eth-keys but did not declare
  them in pyproject.toml, so a fresh `pip install` fails at import.
- Replace remaining `x402-tools` references in README/FEATURES/specs with
  `x402-cli`; swap deprecated --max-decimal for --max-rawAmount.
- Bump to 0.1.0-beta.6.
beta.5 and beta.6 were broken on fresh installs:
- httpx 1.0.dev3 removed AsyncClient — pin to 0.x stable
- eth-account 0.14.0b1 has API changes — pin to 0.13
- web3 needed for EVM allowance checks
- tronpy needed for TRON balance reads

Verified locally (`pip install dist/*.whl` in clean venv):
- 5/5 smoke tests
- TRON Nile roundtrip + serve/pay
- BSC Testnet roundtrip
rawAmount = amount × 10^decimals — earlier betas had this backwards.

- --amount is now human-readable (e.g. 1.25)
- --rawAmount is now smallest-unit integer (e.g. 1250000)
- --max-amount and --max-rawAmount mirror the same split
- JSON output fields amount/rawAmount follow new convention
- --max-rawAmount actually wired up (was a no-op before)

Verified locally on 0.1.0b9 wheel in clean venv:
- 5/5 smoke tests
- TRON Nile roundtrip with --amount 0.0001 → tx 0cf5a907...
- TRON Nile roundtrip with --rawAmount 100      → tx 54dfd585...
- TRON Nile serve+pay with --max-amount 0.001  → tx 0d3a66cc...
- BSC roundtrip with --rawAmount 100000000000000 → tx 4c77ab52...
- --max-amount 0.00005 correctly rejects 0.0001 USDT payment
- --max-rawAmount 50 correctly rejects 100-unit payment
- /.well-known/x402 returns rawAmount=1250000, amount=1.25
…let only)

--token was a dead flag in pay: cli.py parsed it, threaded it to
cmd_client, but the filter dict passed to select_payment_requirements
contained only network+scheme. The SDK's selector also has no token key,
so the CLI now filters candidates itself by symbol (case-insensitive)
via TokenRegistry.find_by_address before delegating. --token foo now
errors out instead of silently picking whatever the server offered.

Wallet flow is consolidated onto bankofai-agent-wallet:
- Removed --wallet flag from serve/pay/roundtrip
- Removed LocalTronWallet/LocalEvmWallet/read_private_key from wallet.py
- agent-wallet itself resolves: encrypted ~/.agent-wallet store first,
  then env vars (TRON_PRIVATE_KEY, AGENT_WALLET_PRIVATE_KEY, mnemonic)
- If the local store is broken, fall back to agent-wallet's
  EnvWalletProvider directly (still inside agent-wallet, not LocalWallet)
- Added bankofai-agent-wallet>=2.4 as a direct dep
- Removed direct eth-account/eth-keys deps (SDK pulls them in)

README gains a 'Wallet — agent-wallet' section explaining one-time setup.

Verified locally on 0.1.0b10 wheel in clean venv:
- 5/5 smoke tests
- --token foo rejected: 'No payment options match --token foo'
- TRON Nile (--token USDT) → tx 485e057f...
- BSC Testnet → tx 80b2b1f1...
…hain test

Single doc that walks a human through three end-to-end flows on real
testnets:
  A. TRON Nile + exact_gasfree  (no TRX on main wallet)
  B. TRON Nile + exact_permit   (TRX gas + USDT on main wallet)
  C. BSC Testnet + exact_permit

Includes:
- AGENT_WALLET_DIR isolation pattern so users don't have to nuke their
  existing ~/.agent-wallet/
- Pre-flight balance scripts (tronpy + web3) for both chains
- Tronscan/BscScan URLs for verification
- Note that Nile USDT does support TIP-2612 permit (default-to-GasFree
  is conservative for mainnet stable-coins)
- One-page quick-reference card at the end
- Troubleshooting table covering the actual errors users hit (master
  password prompt, GasFree balance, rate limits, port-in-use, etc.)

README.md gains a one-line pointer to the guide.
Help text was previously a vague 'e.g. tron:nile, eip155:97' that left
users guessing what was actually supported. Now lists all five CAIP-2
network IDs and all four registry token symbols across serve/pay/roundtrip.

For 'pay --network' / 'pay --token' (filters, not server config), help
also says 'omit to accept any …' so the filter semantics are explicit.
The Python module name still carried the old project name (x402_tools)
even though the package on PyPI, the CLI binary, and the docs were all
already x402-cli. This was confusing in stack traces, in import paths,
and in subprocess invocations like 'python -m bankofai.x402_tools.cli'
inside the roundtrip command.

Done in one pass:
- git mv src/bankofai/x402_tools src/bankofai/x402_cli
- pyproject.toml entry point: bankofai.x402_cli.cli:main
- All 'from bankofai.x402_tools.X' imports updated (cli, server_cmd,
  client_cmd, wallet, schemes, output, tests).
- subprocess.Popen call in roundtrip uses '-m bankofai.x402_cli.cli'
- Docstrings in schemes.py / output.py: 'for x402-cli'
- Docs (.claude/CLAUDE.md, smoke-test.sh, CONTRIBUTING.md, specs/README.md,
  manual-test-guide.md, .github/workflows/test.yml) updated.
- CHANGELOG.md left as-is (the historical entry already documents the
  earlier x402-tools → x402-cli renaming).

Verified: pytest 11/11 pass; live roundtrip on TRON Nile + exact_permit
settled successfully (tx e9e7d8972cabdbd2f54ccc2829002ec35d268c21176e85126933e4747bfd0843).
Slipped in via 'git add -A' on the previous commit (ce8b360). Pure macOS
Finder metadata, no code or config inside it.
…s table

Wallet:
- Drop the multi-step setup walkthrough (env vars, agent-wallet start /
  list / use, single-key-for-both-chains explainer). The detail belonged
  in agent-wallet's own getting-started doc, not in this README.
- Keep two sentences explaining what the dependency is and the absence
  of a --wallet flag; link out to BofAI/agent-wallet getting-started.

Design:
- Remove. The 'we use SDK X402Server instead of reimplementing' note was
  about internal architecture, not user-facing behavior.
- Pull the only useful bit (per-network default scheme rationale) into a
  new compact 'Schemes' table that also covers BSC/EVM, not just TRON.

Reading flow is now: Install → Wallet (link out) → Amounts → Quick start
→ Schemes → Env vars → Development. Net delta ~30 lines shorter.
A user reported on a fresh `pip install bankofai-x402-cli==0.1.0b10`:

  ImportError: cannot import name 'TokenRegistry' from 'bankofai.x402'
  (...) Did you mean: 'AssetRegistry'?

Root cause: their environment had a newer `bankofai-x402` already
installed (anaconda3 base), and the SDK had renamed the registry from
`TokenRegistry` to `AssetRegistry`. Our pyproject pin was the loose
`bankofai-x402>=0.5.9`, so pip kept their existing version and we
mismatched the symbol.

Per user direction ("sdk 不要改, 就改 cli"), the fix is local to
this package — we do not touch the SDK. At each call site:

    try:
        from bankofai.x402 import TokenRegistry
    except ImportError:
        from bankofai.x402 import AssetRegistry as TokenRegistry

Both branches are real installable states today; whichever wins, the
rest of the file keeps using the `TokenRegistry` name unchanged.

Bumped to 0.1.0-beta.11 and verified in a clean venv:
  - pip install picks up the new wheel
  - x402-cli --help / --version / pytest 11/11 all green
  - On an SDK that exposes only `TokenRegistry` (PyPI 0.5.8/0.5.9):
    try succeeds, no fallback needed
  - On an SDK that exposes only `AssetRegistry` (the user's env):
    try fails, except branch resolves the alias

PyPI: https://pypi.org/project/bankofai-x402-cli/0.1.0b11/
…ADME

Two things:

1) Cap every dependency upper bound. b10's TokenRegistry breakage was
caused by writing 'bankofai-x402>=0.5.9' (no upper limit) and then
having pip silently accept a preinstalled v2-dev SDK that had renamed
the symbol. Same risk existed for every other dep that wasn't bounded.
Now:

  bankofai-x402>=0.5.9,<0.6
  bankofai-agent-wallet>=2.4,<3
  pydantic>=2.0,<3
  fastapi>=0.110,<1.0
  uvicorn>=0.27,<1.0
  click>=8.1.0,<10
  (httpx, web3, tronpy already had caps)

A future major version of any of these now has to be opted into via a
new cli release, not auto-resolved into a user's environment.

2) README rewritten for end users. Was a contributor-style doc with
'Design', 'Environment variables', 'Development' sections explaining
internals like 'we use the SDK's X402Server'. New flow targets someone
who wants to send a payment from the command line:

  install -> wallet (one cmd, link out) -> what each cmd is for ->
  copy-paste GasFree TRON-mainnet transfer -> other-network templates
  -> amount units -> common errors

Verified end-to-end on real testnets with the b12 wheel from a clean
venv:
  TRON Nile exact_permit:   887c65b63a81009ca7ccc1545575189bf4b604174205187638189b3d61e1cdcb
  TRON Nile exact_gasfree:  524d01f8ac1451bfd7d0fd835922c7930a4714749efe28471bd6a10dc064375b
  BSC Testnet exact_permit: 90bd524e7cda5a9587ab7212e3a9efea5e0725fe4d249e53b4a460f89bdd6e4e

PyPI: https://pypi.org/project/bankofai-x402-cli/0.1.0b12/
The b12 README rewrite was for end users (correct), but I wrote it in
mixed CN/EN — that doesn't fit the project's docs convention (everything
else under docs/ and specs/ is English) and makes the PyPI project page
unreadable for non-CN readers.

Verbatim same structure and content, just translated. No code changes.
The b12 rewrite changed the README's audience from contributor to end user
(correct), but I wrote it in mixed CN/EN. Other docs in the repo (specs/,
docs/, FEATURES.md, TROUBLESHOOTING.md, CONTRIBUTING.md) are all English,
and the PyPI project page renders the README directly — non-CN readers
saw a half-foreign README.

Pure docs change; same structure, same examples, no behavior change.
b12's three on-chain tests still cover runtime.
The SDK's tron_client logs a WARNING on every invocation when
TRON_GRID_API_KEY is unset:

    [bankofai.x402.utils.tron_client] WARNING: TRON_GRID_API_KEY is
    not set. Mainnet RPC calls will be routed to https://hptg.bankofai.io.
    Set TRON_GRID_API_KEY to use TronGrid.

For cli users, falling back to the BankofAI-hosted gateway is the
documented default, not a warning condition. So setup_logging() now
sets that specific logger to ERROR level. Users who want TronGrid
still set TRON_GRID_API_KEY exactly as before, no behavior change.

Verified: serve --network tron:mainnet on a clean b14 venv produces
no TRON_GRID line in its output.
Three classes of improvement, no behavior change for the happy path:

A. Dependencies
   - bankofai-x402[evm,tron]>=0.5.9,<0.6 (was bankofai-x402>=0.5.9,<0.6
     plus separate web3 and tronpy entries). cli source never imports
     web3 or tronpy directly — they were redundant top-level deps. Now
     pulled via the SDK's own [evm,tron] extras so the SDK owns those
     ranges. Resolved versions identical to b14.

B. UX
   - x402-cli with no args prints --help instead of an empty error.
   - -h is now an alias for --help.
   - Top-level docstring lists the three commands as 'common flows',
     a one-line first-time setup hint, and a copy-paste GasFree
     example so users don't have to leave the terminal.
   - --scheme help text lists every supported value with a one-phrase
     explanation and notes the auto-pick fallback.

C. Friendly errors
   - New module bankofai.x402_cli.errors with classify(err) → (code,
     message, hint). The hint line tells the user what to do, instead
     of leaving them alone with an SDK stack trace.
   - Wired into client_cmd.py and server_cmd.py outer except blocks.
   - Also wired into the post-retry !=200 path that previously emitted
     errors as 'successful' envelopes.
   - Mapped: WALLET_NOT_CONFIGURED, WALLET_CONFIG_CORRUPT,
     INSUFFICIENT_GASFREE_BALANCE, GASFREE_NOT_ACTIVATED,
     INSUFFICIENT_GAS, RATE_LIMITED, DEADLINE_TOO_SOON,
     PERMIT_REVERTED, SDK_API_DRIFT.

D. Doc fix
   - specs/smoke-tests.md was still using the long-removed --decimal
     flag. Updated to --amount.

Verified end-to-end on b15 wheel from a clean venv:
  TRON Nile exact_permit:   23ee1731547dc4fadc06f5d26d3e3df22c4d39799ae80d887dfa2fee0c82c42a
  TRON Nile exact_gasfree:  292bc6e17353dc976eac5925f1cd9b88a1b6edeb293c963e36fb67e01f2198ee
  BSC Testnet exact_permit: 43ed0a7d3a77baf30b559d39926fc63fd657eb4389553c1884f2a86edb5a4a72

PyPI: https://pypi.org/project/bankofai-x402-cli/0.1.0b15/
Twelve unused imports across cli.py / server_cmd.py / tests/test_output.py
that ruff (F401) flagged in the GitHub Actions test job. All auto-fixable;
no behavior change. Re-running pytest 11/11 still green; cli --version /
serve --help still work; runtime tests on a clean venv still pass.
@boboliu-1010 boboliu-1010 changed the title test(smoke): verify core server functionality with smoke tests Release 0.1.0-beta.15 — usability + docs + deps + release-ready May 2, 2026
bobo added 3 commits May 3, 2026 02:58
CI's mypy strict-mode job was producing 26 errors:
  22 × 'Skipping analyzing bankofai.x402.*: missing py.typed marker'
   3 × var-annotated / type-arg / unused-ignore in cli source

Fixed:
  - Added a [[tool.mypy.overrides]] for bankofai.x402.* with
    ignore_missing_imports = true. We lose type checking against the
    SDK (which we'd lose anyway without their py.typed marker), but
    the strict-mode job is now actionable for cli-only issues.
  - Replaced two '# type: ignore[no-redef]' (now redundant under the
    override) with '# noqa: F401' to keep ruff happy on the unused
    AssetRegistry alias.
  - Annotated networks_schemes: dict[str, set[str]].
  - Annotated FastAPI route handlers' return types: dict[str, bool] /
    dict[str, str] instead of bare dict.

Local: ruff + mypy + pytest all green.
Removed it in 0459863 because local mypy reported 'unused section(s):
module = [agent_wallet.*]'. But that's a benign note (mypy exit code 0),
not an error. On CI, wallet.py:24 'from agent_wallet import EnvWalletProvider'
still triggered import-untyped because agent_wallet has no py.typed marker.

Re-adding both 'agent_wallet' and 'agent_wallet.*' to the override list.
Local mypy now reports an unused-section note for one of them (whichever
isn't matched), which is fine — it's a note, not an error, and exit code
is 0 in both environments.
CI runs:
  pytest tests/ -v --cov=src/bankofai/x402_cli --cov-report=xml
But pytest-cov was missing from [project.optional-dependencies] dev,
so on a fresh CI venv pytest reports 'unrecognized arguments: --cov...'
and exits 4.

Adding pytest-cov>=4.0 fixes the CI step. Local 'pytest --cov ...' now
also works (writes coverage.xml; reports 'No data was collected' since
our 11 unit tests don't import most of cli source — that's expected
and harmless).
bobo and others added 5 commits May 7, 2026 11:31
…approval)

QA hit a hard failure when running:
  x402-cli pay --network tron:mainnet --scheme exact_permit
with an agent-wallet privy wallet that hadn't pre-approved the
PaymentPermit contract. The cli's allowance check trips, ensure_allowance
builds an approval txn, hands it to privy — privy rejects:

  SigningError: Payload must include raw_data_hex for TRON signing

Root cause turned out to be a long-standing integration gap, not a new
break:
  - tronpy 0.4–0.6 (verified all on-PyPI versions) do NOT expose
    raw_data_hex on the Transaction object — neither in to_json(), as
    an attribute, nor as a method.
  - SDK's TronClientSigner._build_unsigned_tx_payload tries those three
    sources, fails on all three, and ships raw_data_hex=None to the
    wallet adapter.
  - agent-wallet raw_secret/local_secure adapters tolerate None — they
    sign txID directly. Every internal cli test used those, so this
    never showed up.
  - agent-wallet 2.4's privy adapter requires non-empty raw_data_hex
    (Privy's hosted signer needs the full payload for compliance review).
    QA was the first to run cli pay through privy.

Fix is purely cli-side: a monkey-patch installed at cli import time
that wraps SDK's _build_unsigned_tx_payload. When raw_data_hex is
missing, it serializes txn._raw_data via tronpy's own protobuf helper
(tronpy.proto._raw_data_to_protobuf + tron_pb2.Transaction(raw_data=...).
SerializeToString()) — the same path tronpy itself uses for offline
txid calculation.

Verified bit-for-bit: sha256(our_raw_data_hex) == tronpy.txn.txid for
every test input. 6 new unit tests in tests/test_tron_patch.py cover
serialization correctness, idempotency, forward-compat (no overwrite if
SDK ever starts populating raw_data_hex itself), and the full SDK
patched call.

Live verified: TRON Nile exact_permit roundtrip on a fresh b16 install
settled successfully (tx f2c5ed31a5bb07ae7de4d35094b589f7197de6ccb444014eadcd6914e03e6e33),
proving raw_secret path still works and the patch isn't a regression.
The privy path can only be verified end-to-end with a real privy wallet
on QA's side; the unit tests prove the patched payload now satisfies
privy's strict isinstance(raw_data_hex, str) check.

Adds protobuf>=4.21,<7 as a direct dep (tronpy.proto's import gate).

This patch becomes a no-op the day SDK starts emitting raw_data_hex
natively, so it's not a long-term liability.
Slipped in via 'git add -A' on f9d57a7. Pure test-run output, not source.
QA on b16 hit a new failure once the raw_data_hex patch unblocked the
approval signing path:

  Contract validate error : account [TT3mqNohVNyzMr6H2SBHWCWzU7bXPaAGUX]
  does not exist

This is TRON's account activation rule, not a cli bug. Every TRON
address must have received any inflow (TRX / TRC10 / TRC20) at least
once before it can be the owner_address of a contract call. fresh
privy-derived addresses haven't, so the node validator refuses the
approval broadcast.

Cli now classifies this error as TRON_ACCOUNT_NOT_ACTIVATED with a
clear hint: send ~1 TRX once to bootstrap, or use --scheme exact_gasfree
which routes through the GasFree relayer and doesn't need main-wallet
activation. 30 unit tests in tests/test_errors.py lock down all the
classifier rules.
…aries

Two cosmetic UX polishes spotted during a help-output review. No
behavior change, no version bump (will roll into the next real release).

1. Top-level 'Commands' list was truncating each command's first
   docstring sentence at ~50 chars (Click's default for the summary
   line):

     pay        Pay an x402-protected URL when the server returns 402...
     roundtrip  One-shot roundtrip: start server, pay it, shut down (for...

   Each command's docstring now starts with a short title sentence (full
   sentence shows in the Commands list) followed by a longer paragraph
   that's only seen in 'x402-cli <cmd> --help'. Resulting Commands list:

     pay        Pay an x402-protected URL.
     roundtrip  One-shot transfer: serve → pay → tear down.
     serve      Run a local x402 paywall endpoint.

2. 'x402-cli pay --help --scheme' was a one-liner ('Require a specific
   x402 scheme') while 'x402-cli serve --help --scheme' listed every
   supported value with a one-phrase explanation. Inconsistent. pay's
   --scheme help is now the same detailed form, just rephrased in 'filter'
   semantics ('Omit to accept any scheme the server advertises').
Consolidates everything from 0.1.0-beta.5 through 0.1.0-beta.17 into
the first stable release.

Headline features:
- Three commands: pay <url>, serve, roundtrip — each with full --help
  including supported networks, tokens, schemes, and worked examples.
- Wallets fully delegated to bankofai-agent-wallet (raw_secret /
  local_secure / privy / mnemonic). One private key derives both EVM
  and TRON addresses.
- Auto-pick scheme per (network, token), override with --scheme.
- 10-rule friendly error classifier (WALLET_NOT_CONFIGURED,
  TRON_ACCOUNT_NOT_ACTIVATED, INSUFFICIENT_GASFREE_BALANCE, ... ),
  each with a one-line resolution hint.
- _tron_patch.py compat shim: fills in raw_data_hex on TRON unsigned
  txs that all released tronpy versions leave empty, so privy-style
  wallets that strictly require it can sign approvals. Bit-correct
  vs tronpy's own offline txid calculation, idempotent, becomes a
  no-op once SDK emits raw_data_hex natively.
- TokenRegistry/AssetRegistry import-drift tolerance.
- Pinned dependency upper bounds (no silent major bumps).
- English-only user-facing README + docs/manual-test-guide.md with
  three full hands-on walkthroughs ending in real on-chain receipts.

Verified on PyPI install:
- TRON Nile permit / gasfree / mainnet permit (3 distinct QA TXs)
- BSC Testnet permit
- All 30 unit tests pass on Python 3.11 and 3.12 in CI.

Known limitations (not cli bugs):
- Fresh TRON addresses need activation (cli now surfaces this clearly).
- BSC mainnet USDC does not implement transferWithAuthorization or
  permit — (token, scheme) compatibility is a facilitator concern,
  not cli's.
@boboliu-1010 boboliu-1010 changed the title Release 0.1.0-beta.15 — usability + docs + deps + release-ready Release 0.1.0 — first stable May 8, 2026
bobo added 2 commits May 8, 2026 10:38
mypy 1.x in CI flagged 5 issues introduced when _tron_patch.py landed:

  src/bankofai/x402_cli/_tron_patch.py:54: Missing type arguments for generic type "dict"
  src/bankofai/x402_cli/_tron_patch.py:61: import-untyped: tronpy.proto.transaction
  src/bankofai/x402_cli/_tron_patch.py:62: import-untyped: tronpy.proto
  src/bankofai/x402_cli/_tron_patch.py:66: Returning Any from function declared to return "str | None"
  src/bankofai/x402_cli/_tron_patch.py:105: Unused "type: ignore" comment

Fixes:
- raw_data parameter typed as dict[str, Any] (was bare 'dict').
- Both tronpy.proto.* imports tagged 'type: ignore[import-untyped]'
  because tronpy ships without a py.typed marker. Stable across 0.6.x.
- The serializer's hex() result coerced through a typed local 'result: str'
  before return, eliminating the 'returning Any' diagnostic.
- Removed the now-unused 'type: ignore[attr-defined]' tag on the
  _x402_cli_raw_data_hex_patched marker assignment (mypy 1.x learned to
  infer this on bare attr writes).

Local verification:
  mypy src/bankofai/x402_cli  →  Success: no issues found in 9 source files
  pytest -q tests/             →  30 passed

Behavior unchanged. Patch shim still byte-equal to tronpy's own offline
txid calculation (covered by tests/test_tron_patch.py).
…match 0.1.0

Spec files had drifted while the cli evolved code-first through b11..b17.
This commit aligns them with the actual 0.1.0 surface — no behavior or
source changes.

server.md:
  - --daemon row clarified as a reserved flag (currently no-op; the
    server still runs in the foreground). Real daemonization is not
    implemented yet.
  - /health endpoint response corrected: it returns just {"ok": true},
    not the wrapped envelope the doc previously claimed. Added a note
    explaining why /health is intentionally tiny (used by roundtrip's
    _wait_for_server_ready before any chain handshakes).
  - New 'Compat shim — _tron_patch.py' section describing the
    raw_data_hex monkey-patch layer added in b16. Includes deletion
    criteria so future maintainers know when to remove it.

client.md:
  - 'Register Mechanisms' code example now passes the required
    clients={network: GasFreeAPIClient(...)} kwarg to
    ExactGasFreeClientMechanism (added in b11).
  - 'Signer Resolution' rewritten: cli has no in-tree wallet plumbing,
    everything goes through agent-wallet. Removed the misleading
    'or TRON_PRIVATE_KEY' fallback line.
  - 'Error Codes' table replaced: was a generic 6-row list (IO_ERROR,
    VALIDATION_ERROR, ...); now reflects the 10-rule friendly classifier
    in errors.py (WALLET_NOT_CONFIGURED, TRON_ACCOUNT_NOT_ACTIVATED,
    INSUFFICIENT_GASFREE_BALANCE, ...) with one-line resolution hints
    each, plus pointers to errors.py + tests/test_errors.py.
  - Failure example now shows the structured triplet (code, message,
    hint) actually emitted in --json.

smoke-tests.md:
  - Status timestamp + version updated to 2026-05-08 / 0.1.0.
  - Added a 'Two complementary layers' overview at the top: pytest unit
    layer (30 cases across 4 files) is what CI runs every PR; the shell
    smoke is what this doc describes.
  - /health expected-response example corrected (matches the real body).
  - --daemon row in 'NOT Tested' updated: flag exists but cmd_server
    ignores it; planned for v0.2.

specs/README.md:
  - 'Updating These Specs' rewritten to acknowledge the project is
    code-first. CHANGELOG is canonical; specs are architecture
    references refreshed at version boundaries. Includes explicit
    'edit a spec file when ...' / 'don't bother when ...' lists so
    contributors don't waste time on spec round-trips for routine
    flag-tweak commits.
bobo added 2 commits May 8, 2026 14:41
…act_permit

The original default was set when exact_permit settlement was unproven
on TRON mainnet (the in-tree comment cited 'permitTransferFrom may
revert during broadcast'). That was b15-and-earlier territory.

Two things changed since:
  1. b16's _tron_patch.py compat shim removed the only known
     blocker for exact_permit on TRON (the empty-raw_data_hex issue
     that broke the privy wallet adapter).
  2. QA produced three independent on-chain mainnet receipts via
     exact_permit on b16+:
       - fd1c6678574f7d68454ad76c1ab2efaddaabbcbb61e5f123575619d8c662dba4
       - 9ad3be693d42c6aed3ff732f2072b6ca26fc0d878dac1326058a44de8636f078
       - 1383a0db66c46520fafb1ad98db7d7ce8a6b68ad0fb12131704ec78b7f1959a6

So exact_permit is now the better default for typical payers because:
  - Settlement reaches the recipient in a single permitTransferFrom
    on-chain — no relayer intermediary, no per-settlement transferFee,
    no one-time activateFee on a derived custodial address.
  - Fits the EVM mental model (the payer signs an EIP-2612-style
    permit, gas is the payer's responsibility).
  - QA-validated on mainnet through multiple wallet backends.

exact_gasfree remains a first-class option, registered as the SECOND
choice for every TRON network/token entry. Users who have USDT but
no TRX can still pass --scheme exact_gasfree to route through the
GasFree relayer exactly as before. The mechanism, helper API client
plumbing, and all docs for that path are unchanged.

Files touched:
  - src/bankofai/x402_cli/schemes.py
      Per-token list reordered: ['exact_permit', 'exact_gasfree']
      across tron:mainnet / tron:nile / tron:shasta. Comment rewritten
      to explain the new rationale + the opt-in fallback contract.
  - tests/test_schemes.py
      Two assertions flipped to expect exact_permit as default. New
      test 'test_tron_keeps_gasfree_as_second_choice' locks in the
      contract that gasfree must remain registered (regression guard).
      Result: 31/31 pass (was 30; +1 new test).
  - README.md
      'Copy-paste' section header retitled from 'GasFree transfer'
      to 'USDT transfer'. Output excerpt now shows scheme:
      exact_permit. The 'Why is this GasFree' callout swapped for
      'What just happened' (explains permit + transferFrom) plus a
      'No TRX in your wallet?' tip pointing at --scheme exact_gasfree.
      Network-template table footnote updated.
  - docs/manual-test-guide.md
      Section 3 'Pick a scheme' table marks exact_permit as default
      and exact_gasfree as opt-in. Walkthrough A title gains
      '(opt-in path)' + a top callout explaining when to use it
      vs Walkthrough B; its roundtrip command now shows the
      explicit --scheme exact_gasfree (since it's no longer the
      default and would otherwise auto-pick permit). Walkthrough B
      title gains '(DEFAULT)'. Note about Nile USDT permit support
      updated to reflect the new cli-wide default. Quick-reference
      card reordered: TRON permit shown first as the default,
      exact_gasfree shown second with explicit --scheme.
  - CHANGELOG.md
      [0.1.0] 'Settlement schemes' bullet rewritten to spell out
      the new default-vs-opt-in registry mapping.

No SDK / agent-wallet changes. The mechanism registration code in
client_cmd.py / server_cmd.py also untouched — the change is
purely in the auto-pick table.
…ment TRX)

Previous wording in README, manual-test-guide, schemes.py comment, and
CHANGELOG conflated the cli's one-time ensure_allowance step with the
per-payment cost. The cleaner mental model:

  - permit = signed OFF-CHAIN, never costs gas
  - permit + transferFrom settlement = broadcast by facilitator, *they*
    pay TRX
  - ensure_allowance approve = broadcast by payer, payer pays ~6 TRX
    BUT this only happens on the very first payment for a given token
    contract; subsequent payments skip this step entirely

So the user-friendly framing is: 'facilitator pays per-payment gas;
the only TRX you spend is a one-time approve when you first use a
token from a fresh wallet.' That's the message now everywhere.

Files updated:

  README.md
    - 'What just happened?' callout rewritten so the dominant
      message is 'You pay no TRX per payment.' The one-time approve
      is now a tip in italics, not the headline.
    - Templates table: TRON mainnet row says 'Facilitator pays
      per-payment gas. One-time ~6 TRX approve...'. BSC row
      similarly clarified.

  docs/manual-test-guide.md
    - Walkthrough B intro (5.0): now leads with the two-step flow
      (off-chain permit + facilitator-broadcast settlement) and
      labels the approve as a 'one-time exception'. No more
      'Payer must hold TRX for gas' as an absolute claim.
    - 5.1 balance prerequisites: TRX is now described as
      'only required if this is the wallet's first payment',
      not as a generic requirement.
    - 5.4 expected on-chain effect: distinguishes the one-time
      approve tx (where ~6 TRX comes from) from the actual
      permit + transferFrom settlement (which does NOT draw
      from the payer's wallet). Notes that from the second
      payment onward TRX stays put.

  src/bankofai/x402_cli/schemes.py
    - TRON registry comment rewritten to describe the steady-state
      gas model (facilitator pays per-payment) plus the one-time
      approve caveat. Also makes the trade-off vs exact_gasfree
      explicit (gasfree adds per-settlement fees, that's why
      permit is the better default).

  CHANGELOG.md
    - [0.1.0] 'Settlement schemes' bullet expanded with the same
      'facilitator pays per-payment, payer pays one-time approve'
      framing.

No behavior change, no source changes. 31/31 tests still pass.
@roger-gan roger-gan merged commit e513791 into main May 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants