Skip to content

Dev/agent wallet 0316 - #8

Merged
Hades-Ye merged 10 commits into
mainfrom
dev/agent_wallet_0316
Mar 21, 2026
Merged

Dev/agent wallet 0316#8
Hades-Ye merged 10 commits into
mainfrom
dev/agent_wallet_0316

Conversation

@leo8tron

Copy link
Copy Markdown
Contributor

No description provided.

@github-actions

Copy link
Copy Markdown

Code Review Report

Project: x402-demo
PR: maindev/agent_wallet_0316
Review Date: 2026-03-21
Reviewer: AI Code Reviewer (Code Review Skill v1.0.0)


PR Overview

Branch Information

Property Value
From Branch main
To Branch dev/agent_wallet_0316
Commits 10
Files Changed 8
Lines Added +3,127
Lines Removed -100

Note: 3,057 of the added lines are from the newly committed client/typescript/package-lock.json. Substantive code changes total ~70 lines added and ~100 lines removed.

Commit History

Hash Message
6a7b608 fix: upgrade x402 sdk version
f88dd1d fix: x402 sdk version
0c81e08 Create audit-pr.yml
838ddaf fix: del private key
7c41036 fix: support agent wallet
9690598 fix: upgrade agent wallet
95c707a fix: facilitator gasfree
3561317 feat: agent wallet
a7bba8b feat: agent wallet
3d84324 feat: agent wallet

Review Summary

Verdict

Verdict: Request Changes

Findings at a Glance

Critical Major Minor Suggestion
Count 0 3 4 3

Summary

This PR migrates the x402-demo project from explicit private-key environment variables (TRON_PRIVATE_KEY, BSC_PRIVATE_KEY) to an async "agent wallet" abstraction (SomeClass.create()), leveraging the new @bankofai/agent-wallet package bundled with @bankofai/x402 ^0.5.0. The Python SDK dependency is simultaneously moved from a GitHub source tag (v0.4.4) to a pinned PyPI release (0.5.0). The GasFree facilitator is enhanced to conditionally enable itself based on the presence of API credentials. The assets/openclaw.jpg binary is deleted and server/protected.png is updated.

The architectural intent is sound — removing raw private keys from environment variables reduces the risk of accidental secret exposure and simplifies onboarding. However, the PR introduces three major concerns: BSC/EVM wallets are now unconditionally initialised in all components (whereas they were previously optional), the startup event for the facilitator has no error handling around the wallet creation calls, and the TRON facilitator address is no longer logged at startup, reducing operational observability. Several minor inconsistencies around GasFree credential passing and a deprecated FastAPI API also warrant attention before merge.


Change Summary

1. Agent Wallet Integration (Core Feature)

File Change Type Description
client/python/main.py Modified Replace from_private_key(env_var) with await TronClientSigner.create() / await EvmClientSigner.create()
client/typescript/src/main.ts Modified Replace new TronClientSigner(key) with await TronClientSigner.create() / await EvmClientSigner.create()
facilitator/main.py Modified Migrate signer creation to async startup event, replace from_private_key calls

Purpose: Eliminate raw private keys from environment variables by delegating key management to the new @bankofai/agent-wallet SDK. Signer construction becomes async, requiring await.


2. SDK Version Upgrade

File Change Type Description
requirements.txt Modified bankofai-x402[tron,fastapi] from GitHub tag v0.4.4 → PyPI ==0.5.0
client/typescript/package.json Modified @bankofai/x402 from 0.4.1^0.5.0
client/typescript/package-lock.json Added New lockfile resolving @bankofai/x402@0.5.0 + @bankofai/agent-wallet@2.3.0

Purpose: Align both Python and TypeScript clients with the published v0.5.0 SDK that bundles the agent-wallet dependency.


3. GasFree Conditional Enablement (Facilitator)

File Change Type Description
facilitator/main.py Modified GasFree client for Nile is only constructed when GASFREE_API_KEY + GASFREE_API_SECRET are present; mechanism is only registered when enabled

Purpose: Allow the facilitator to run without GasFree credentials by degrading gracefully to non-GasFree TRON mechanics.


4. Startup Validation Removal

File Change Type Description
client/python/main.py Modified Removed early exit(1) guard for missing TRON_PRIVATE_KEY
client/typescript/src/main.ts Modified Removed early process.exit(1) guard for missing TRON_PRIVATE_KEY
facilitator/main.py Modified Removed raise ValueError("TRON_PRIVATE_KEY environment variable is required") guard

Purpose: Startup validation is no longer needed at the env-var level because key management is now internal to the agent wallet.


5. Asset Changes

File Change Type Description
assets/openclaw.jpg Deleted Binary image removed
server/protected.png Modified Protected-resource image replaced (25 KB → 8 KB)

Detailed Findings


Major

[MJ-01] No Error Handling Around Async Wallet Initialization in Startup Event

Property Value
Severity Major
Category Correctness / Observability
File facilitator/main.py : Lines 108–152 (startup handler)

Description

The register_mechanisms() function is registered as a FastAPI startup event handler. It calls await TronFacilitatorSigner.create() and await EvmFacilitatorSigner.create() without any try/except block. If the agent wallet service is unavailable, unconfigured, or returns an unexpected error, the exception will propagate unhandled. In FastAPI, an uncaught exception in a startup handler causes the entire application process to abort with a raw Python traceback — offering no actionable guidance to the operator. The old code, by contrast, raised a clear ValueError("TRON_PRIVATE_KEY environment variable is required") before the server even started.

Code

@app.on_event("startup")
async def register_mechanisms():
    """Register all mechanisms with async wallet initialization."""
    tron_signer = await TronFacilitatorSigner.create()   # no try/except
    ...
    bsc_signer = await EvmFacilitatorSigner.create()      # no try/except

Recommendation

@app.on_event("startup")
async def register_mechanisms():
    """Register all mechanisms with async wallet initialization."""
    try:
        tron_signer = await TronFacilitatorSigner.create()
    except Exception as e:
        logger.critical(
            "Failed to initialise TRON agent wallet — is the agent-wallet "
            "CLI configured? Run `agent-wallet init` to set up. Error: %s", e
        )
        raise SystemExit(1) from e

    try:
        bsc_signer = await EvmFacilitatorSigner.create()
    except Exception as e:
        logger.critical("Failed to initialise EVM agent wallet: %s", e)
        raise SystemExit(1) from e
    ...

The same pattern should be applied in client/python/main.py and client/typescript/src/main.ts.


[MJ-02] EVM/BSC Signer Is Now Unconditionally Required

Property Value
Severity Major
Category Correctness / Compatibility
File facilitator/main.py : Line ~130; client/python/main.py : Line ~50; client/typescript/src/main.ts : Line ~99

Description

Previously, EVM/BSC support was entirely optional and guarded by if BSC_PRIVATE_KEY:. A user running only TRON networks could omit the BSC key and everything would work. After this PR, await EvmFacilitatorSigner.create() (and its client-side equivalents) are called unconditionally. If the agent-wallet tool has not been configured with an EVM key, create() will likely fail — breaking TRON-only deployments that previously worked fine. This is a silent breaking change with no migration guidance.

Code (facilitator/main.py)

# Previously guarded:
# if BSC_PRIVATE_KEY:
#     bsc_signer = EvmFacilitatorSigner.from_private_key(BSC_PRIVATE_KEY)
#     ...

# Now unconditional:
bsc_signer = await EvmFacilitatorSigner.create()       # always runs

Recommendation

Wrap the EVM signer creation in a try/except and make BSC registration conditional on success, preserving backward compatibility:

try:
    bsc_signer = await EvmFacilitatorSigner.create()
    # register BSC mechanisms ...
    print(f"BSC Facilitator Address: {bsc_signer.get_address()}")
except Exception as e:
    logger.warning(
        "EVM agent wallet not available — BSC networks will be disabled. "
        "Configure an EVM key with `agent-wallet` to enable BSC. Error: %s", e
    )

Apply the same pattern in the Python and TypeScript clients.


[MJ-03] TRON Facilitator Address No Longer Logged at Startup

Property Value
Severity Major
Category Observability
File facilitator/main.py : startup handler (print block ~Lines 155–172)

Description

After the refactor, the startup banner logs the BSC facilitator address and fee configuration, but the TRON facilitator address is never printed. This is a significant regression in operational visibility: operators cannot verify which TRON wallet is signing transactions, making debugging and auditing significantly harder. The corresponding client-side addresses (TRON Address, EVM Address) are also omitted from client/python/main.py with no replacement.

Code

    # BSC address is printed:
    print(f"BSC  Facilitator Address: {bsc_facilitator_address}")

    # TRON address is MISSING — tron_signer.get_address() is never called
    print("=" * 80)
    print("X402 Payment Facilitator - Configuration")
    print("=" * 80)
    print(f"TRON Base Fee: {TRON_BASE_FEE}")
    # ... no TRON address here

Recommendation

    print(f"TRON Facilitator Address: {tron_signer.get_address()}")
    print(f"BSC  Facilitator Address: {bsc_facilitator_address}")

In client/python/main.py, restore the address log lines after signer creation:

    tron_signer = await TronClientSigner.create()
    evm_signer  = await EvmClientSigner.create()
    print(f"TRON Address : {tron_signer.get_address()}")
    print(f"EVM  Address : {evm_signer.get_address()}")
    print(f"Resource URL : {RESOURCE_URL}")

Minor

[MN-01] @app.on_event("startup") Is Deprecated in FastAPI

Property Value
Severity Minor
Category Code Quality / Compatibility
File facilitator/main.py : Line ~107

Description

FastAPI deprecated @app.on_event("startup") in v0.93.0 (released Feb 2023) in favour of the lifespan context manager pattern. While the decorator still works, it emits a deprecation warning in recent FastAPI releases and may be removed in a future major version.

Recommendation

from contextlib import asynccontextmanager
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    await register_mechanisms()
    yield
    # optional teardown here

app = FastAPI(
    title="X402 Facilitator",
    lifespan=lifespan,
    ...
)

[MN-02] GasFree API Credentials Not Passed in Python Client

Property Value
Severity Minor
Category Correctness / Consistency
File client/python/main.py : Lines ~67–72

Description

The facilitator's GasFree client is updated to pass api_key and api_secret from environment variables. However, the Python client's GasFreeAPIClient instances are still created with only the base URL — no credentials. If the GasFree API begins requiring authentication for client-side use, or if authenticated calls receive preferential rate limits, the client will break or be throttled while the facilitator works correctly.

Code (client/python/main.py)

gasfree_clients = {
    "tron:nile":    GasFreeAPIClient(NetworkConfig.get_gasfree_api_base_url("tron:nile")),
    "tron:shasta":  GasFreeAPIClient(NetworkConfig.get_gasfree_api_base_url("tron:shasta")),
    "tron:mainnet": GasFreeAPIClient(NetworkConfig.get_gasfree_api_base_url("tron:mainnet")),
}

Recommendation

Mirror the facilitator pattern: read GASFREE_API_KEY / GASFREE_API_SECRET from the environment and pass them to each client constructor (or document explicitly that the client-side GasFree API does not require credentials).


[MN-03] BSC Configuration Printed Before the Section Header Banner

Property Value
Severity Minor
Category Code Quality
File facilitator/main.py : Lines ~153–158

Description

print(f"BSC Facilitator Address: {bsc_facilitator_address}") and print(f"BSC Base Fee: {BSC_BASE_FEE}") appear before the "=" * 80 / "X402 Payment Facilitator - Configuration" banner. This is inconsistent — the BSC information logically belongs inside the configuration block.

Recommendation

Move the BSC address/fee prints to after the banner header:

    print("=" * 80)
    print("X402 Payment Facilitator - Configuration")
    print("=" * 80)
    print(f"TRON Facilitator Address: {tron_signer.get_address()}")
    print(f"TRON Base Fee: {TRON_BASE_FEE}")
    print(f"BSC  Facilitator Address: {bsc_facilitator_address}")
    print(f"BSC  Base Fee: {BSC_BASE_FEE}")
    print(f"GasFree Nile Enabled: {gasfree_enabled_nile}")
    ...

[MN-04] EVM Address No Longer Logged in TypeScript Client

Property Value
Severity Minor
Category Observability
File client/typescript/src/main.ts : Lines ~86–94

Description

The TypeScript client still logs the TRON address at startup but the EVM address line was removed. Users running BSC payments have no way to confirm which EVM wallet is being used without examining the agent wallet configuration separately.

Recommendation

const evmSigner = await EvmClientSigner.create();
console.log(`  TRON Address : ${tronSigner.getAddress()}`);
console.log(`  EVM  Address : ${evmSigner.getAddress()}`);

Suggestions

[S-01] No User-Facing Documentation for Agent Wallet Setup

File: README.md / project docs (not changed in this PR)
Description: The PR removes all private-key env-var references (TRON_PRIVATE_KEY, BSC_PRIVATE_KEY) but adds no documentation explaining what the new TronClientSigner.create() / EvmClientSigner.create() calls require. Existing users upgrading from the main branch will see their configuration break with no clear migration path. The @bankofai/agent-wallet package exposes an agent-wallet CLI binary (visible in package-lock.json) but this is never mentioned anywhere in the changed code.

Suggestion: Add a migration note or update README.md documenting the new setup flow (e.g., npx agent-wallet init).


[S-02] Deletion of assets/openclaw.jpg Unexplained

File: assets/openclaw.jpg
Description: A 570 KB binary asset was deleted without any commit explanation. If this file is referenced in documentation or the README it will create broken links.
Suggestion: Confirm no documentation references this file; if so, update those references simultaneously.


[S-03] Python SDK Pinned Exactly; TypeScript Uses Caret Range

File: requirements.txt vs client/typescript/package.json
Description: The Python SDK is pinned exactly (==0.5.0) while the TypeScript SDK uses a caret range (^0.5.0), allowing automatic minor/patch upgrades. For a demo project this inconsistency is low risk, but aligning the versioning strategy across languages improves predictability.
Suggestion: Either pin both exactly for reproducibility, or use compatible ranges in both.


Positive Observations

Area Observation
Security Removing raw private keys from environment variables is a significant security improvement — it eliminates the most common accidental secret-exposure vector in blockchain projects.
GasFree handling The conditional GasFree enablement in facilitator/main.py (based on presence of API credentials) is a clean, non-breaking degradation pattern.
BSC signer deduplication The old code created two separate EvmFacilitatorSigner instances (bsc_signer and bsc_mainnet_signer) from the same private key. The new code correctly uses a single bsc_signer for both testnet and mainnet mechanisms.
SDK migration Moving from a GitHub source reference to a published PyPI package (==0.5.0) improves reproducibility and CI reliability.
TypeScript policy inline The TypeScript PreferGasFreeUSDTPolicy is expressed as a clean anonymous object implementing the PaymentPolicy interface, which is more idiomatic TypeScript than a separate class.
Lockfile committed Adding package-lock.json ensures reproducible installs for the TypeScript client.

Checklist Results

Category Items Checked Pass Fail N/A Notes
Correctness 8 5 3 0 Unhandled wallet init errors (MJ-01), unconditional BSC creation (MJ-02), GasFree credential inconsistency (MN-02)
Security 10 9 0 1 Private key removal is a net security improvement; no new injection or auth issues
Performance 7 7 0 0 No performance regressions introduced
Code Quality 10 7 3 0 Deprecated API (MN-01), print ordering (MN-03), missing addresses (MJ-03, MN-04)
Testing 7 0 0 7 No tests in scope (demo project)
Documentation 6 3 2 1 No migration docs, missing address logging (S-01), unexplained asset deletion (S-02)
Compatibility 5 3 1 1 BSC now unconditionally required breaks TRON-only deployments (MJ-02)
Observability 4 2 2 0 TRON facilitator address missing (MJ-03), EVM address missing in TS client (MN-04)

Disclaimer

This is an automated code review. It supplements but does not replace human review. The reviewer analysed only the diff between main and dev/agent_wallet_0316. Runtime behaviour of the @bankofai/agent-wallet SDK (e.g., key derivation, storage security, network calls made by create()), integration testing, and deployment impact are not covered by this report.


Report generated by Code Review Skill v1.0.0
Date: 2026-03-21

@github-actions

Copy link
Copy Markdown

Code Review Report

Project: x402-demo
PR: maindev/agent_wallet_0316
Review Date: 2026-03-21
Reviewer: AI Code Reviewer (Code Review Skill v1.0.0)


PR Overview

Branch Information

Property Value
From Branch main
To Branch dev/agent_wallet_0316
Commits 11
Files Changed 9
Lines Added +3,182
Lines Removed -120

Note: ~3,057 of the added lines are the newly committed client/typescript/package-lock.json. Net application code change is approximately +125 / -120 lines.

Commit History

Hash Message
aeb8279 fix: ci
6a7b608 fix: upgrade x402 sdk version
f88dd1d fix: x402 sdk version
0c81e08 Create audit-pr.yml
838ddaf fix: del private key
7c41036 fix: support agent wallet
9690598 fix: upgrade agent wallet
95c707a fix: facilitator gasfree
3561317 feat: agent wallet
a7bba8b feat: agent wallet
3d84324 feat: agent wallet

Review Summary

Verdict

Verdict: Request Changes

Findings at a Glance

Critical Major Minor Suggestion
Count 0 3 3 3

Summary

This PR migrates private-key handling from raw environment variables (TRON_PRIVATE_KEY, BSC_PRIVATE_KEY) to the @bankofai/agent-wallet encrypted keystore, replacing *.from_private_key() constructors with async await *.create() factory calls across the Python client, TypeScript client, and Python facilitator. The FastAPI facilitator is refactored to use the modern lifespan context-manager pattern for async startup. The Python SDK dependency is moved from a git-source reference to a stable PyPI release (bankofai-x402==0.5.0), and per-network GasFree API credential support is added.

The security direction is positive — eliminating plaintext private keys from .env files reduces credential-exposure risk significantly. However, two behavioral regressions are introduced that would break real deployments: (1) EVM/BSC signer creation is now unconditional, meaning any environment that previously ran TRON-only will crash at startup instead of operating gracefully; and (2) all await *.create() calls lack error handling, so a missing or uninitialised keystore produces cryptic unhandled exceptions rather than the user-friendly messages that were in place before. A third major issue is the loss of startup address diagnostics in the Python client. These issues should be resolved before merging.


Change Summary

1. Agent Wallet Key Store Integration (Core Feature)

File Change Type Description
client/python/main.py Modified Replace from_private_key(env_var) with await *.create() for both TRON and EVM signers
client/typescript/src/main.ts Modified Same migration for TypeScript signer constructors
facilitator/main.py Modified Same migration; startup registration moved into lifespan async context
client/typescript/package.json Modified Bump @bankofai/x402 from 0.4.10.5.0
client/typescript/package-lock.json Added New lock file (3,057 lines); records @bankofai/agent-wallet@2.3.0 as transitive dep
requirements.txt Modified Switch from git-source to PyPI release bankofai-x402[tron,fastapi]==0.5.0
README.md Modified Document agent-wallet keystore setup and migration path

Purpose: Improve secret management by storing TRON and EVM private keys in a local encrypted keystore managed by @bankofai/agent-wallet, instead of exposing them in .env files.


2. GasFree Per-Network Credentials

File Change Type Description
client/python/main.py Modified Add per-network GASFREE_API_KEY_* / GASFREE_API_SECRET_* env vars with fallback
facilitator/main.py Modified Add gasfree_enabled_nile flag; GasFree client only instantiated when credentials present

Purpose: Allow operators to configure distinct GasFree API credentials per TRON network (nile / shasta / mainnet), while preserving backward-compatible fallback to a single GASFREE_API_KEY / GASFREE_API_SECRET.


3. FastAPI Lifespan Refactor

File Change Type Description
facilitator/main.py Modified Move all mechanism registration from module-level into @asynccontextmanager async def lifespan(app)

Purpose: Correct placement of async initialisation into an async context so that await calls are valid and the FastAPI app is fully configured before handling requests.


4. Asset Cleanup

File Change Type Description
assets/openclaw.jpg Deleted Remove unused binary asset (583 KB)
server/protected.png Modified Replace protected image (binary update)

Detailed Findings


Major

[MJ-01] Unconditional EVM Signer Creation Breaks TRON-Only Deployments

Property Value
Severity Major
Category Correctness
File client/python/main.py : Lines ~101–104, client/typescript/src/main.ts : Lines ~110–115, facilitator/main.py : Lines ~121–135

Description

In the original code, the EVM/BSC signer was guarded behind if BSC_PRIVATE_KEY:, making it optional. After the migration, await EvmClientSigner.create() (Python / TypeScript) and await EvmFacilitatorSigner.create() (facilitator) are called unconditionally. If the agent-wallet keystore was initialised with only a TRON key (a legitimate and previously supported configuration), the *.create() call will raise an exception at startup, preventing the entire process from starting — including TRON-only payment flows.

Code

# client/python/main.py — now unconditional
evm_signer = await EvmClientSigner.create()
for bsc_network in [NetworkConfig.BSC_TESTNET, NetworkConfig.BSC_MAINNET]:
    x402_client.register(bsc_network, ExactPermitEvmClientMechanism(evm_signer))
    x402_client.register(bsc_network, ExactEvmClientMechanism(evm_signer))
// client/typescript/src/main.ts — now unconditional
const evmSigner = await EvmClientSigner.create();
x402.register('eip155:97', new ExactPermitEvmClientMechanism(evmSigner));
# facilitator/main.py — now unconditional
bsc_signer = await EvmFacilitatorSigner.create()
bsc_facilitator_address = bsc_signer.get_address()

Recommendation

Wrap each EVM create() call in a try/except (Python) or try/catch (TypeScript), and skip BSC registration if the key is unavailable. This preserves the opt-in nature of BSC support:

# Python pattern
try:
    evm_signer = await EvmClientSigner.create()
    for bsc_network in [NetworkConfig.BSC_TESTNET, NetworkConfig.BSC_MAINNET]:
        x402_client.register(bsc_network, ExactPermitEvmClientMechanism(evm_signer))
        x402_client.register(bsc_network, ExactEvmClientMechanism(evm_signer))
    print(f"EVM  Address: {evm_signer.get_address()}")
except Exception:
    print("EVM: not configured (no EVM key in agent-wallet keystore)")

[MJ-02] No Error Handling on Async Wallet create() Calls

Property Value
Severity Major
Category Correctness / Observability
File client/python/main.py, client/typescript/src/main.ts, facilitator/main.py

Description

None of the await TronClientSigner.create(), await EvmClientSigner.create(), await TronFacilitatorSigner.create(), or await EvmFacilitatorSigner.create() calls are wrapped in error handling. If the agent-wallet keystore has not been initialised (npx agent-wallet init not yet run), is corrupted, or is otherwise inaccessible, the process will crash with a raw library exception stack trace.

The old code provided an explicit, user-friendly failure message (e.g., "❌ Error: TRON_PRIVATE_KEY not set in .env file") and a clean exit(1). The new code provides no equivalent feedback, making diagnosis significantly harder for operators.

Code

# facilitator/main.py — no guard or error message
async def lifespan(app: FastAPI):
    tron_signer = await TronFacilitatorSigner.create()   # crashes with raw exception if keystore missing
    ...
    bsc_signer = await EvmFacilitatorSigner.create()     # same

Recommendation

Add try/except around the mandatory TRON signer creation with a helpful error message, and exit cleanly:

try:
    tron_signer = await TronFacilitatorSigner.create()
except Exception as exc:
    print(f"\n❌ Failed to load TRON key from agent-wallet keystore: {exc}")
    print("Run: npx agent-wallet init")
    sys.exit(1)

Apply the same pattern in all three entry points.


[MJ-03] TRON and EVM Addresses Silently Removed from Startup Output (Python Client)

Property Value
Severity Major
Category Observability
File client/python/main.py : Lines ~111–116 (removed)

Description

The old code printed the active TRON and EVM addresses on startup:

TRON Address: T...
EVM  Address: 0x...
Resource URL: http://...

These lines were entirely removed from the Python client with no replacement. Operators lose the ability to verify which wallet addresses are active — critical for payment flows. The TypeScript client still prints TRON Address after the refactor, but removes the EVM address line (a lesser version of the same problem).

Code

# Removed — was previously in client/python/main.py
print(f"TRON Address: {tron_signer.get_address()}")
if BSC_PRIVATE_KEY:
    print(f"EVM  Address: {evm_signer.get_address()}")
print(f"Resource URL: {RESOURCE_URL}")

Recommendation

Restore address logging after successful signer creation in both Python and TypeScript clients. Minimally:

print(f"TRON Address : {tron_signer.get_address()}")
print(f"Resource URL : {RESOURCE_URL}")
# After conditional EVM init:
print(f"EVM  Address : {evm_signer.get_address()}")

Minor

[MN-01] BSC Facilitator Address Printed Before Config Banner

Property Value
Severity Minor
Category Code Quality
File facilitator/main.py : Lines ~147–163

Description

Due to code restructuring inside the lifespan function, the BSC facilitator address and fee are printed before the "X402 Payment Facilitator – Configuration" banner. The output order is now:

BSC  Facilitator Address: 0x...
BSC  Base Fee: {...}
================================================================================
X402 Payment Facilitator - Configuration
================================================================================
TRON Base Fee: ...

This is cosmetically inconsistent and confusing: the BSC details appear before the section header that is supposed to contain them.

Recommendation

Move the BSC print statements to inside the config banner block, after the header:

print("=" * 80)
print("X402 Payment Facilitator - Configuration")
print("=" * 80)
print(f"TRON Base Fee: {TRON_BASE_FEE}")
print(f"BSC  Facilitator Address: {bsc_facilitator_address}")
print(f"BSC  Base Fee: {BSC_BASE_FEE}")
...

[MN-02] EVM Address Removed from TypeScript Client Startup Log

Property Value
Severity Minor
Category Observability
File client/typescript/src/main.ts : Lines ~84–88 (removed)

Description

The TypeScript client used to conditionally print the EVM address when BSC_PRIVATE_KEY was set. After the refactor, the EVM signer is always created but its address is never displayed, making it impossible for the operator to confirm which EVM wallet is active.

Recommendation

Add console.log( EVM Address : ${evmSigner.getAddress()}); after the unconditional evmSigner creation (mirroring how TRON address is shown just above).


[MN-03] Single Signer Instance Used for Both BSC Testnet and Mainnet in Facilitator

Property Value
Severity Minor
Category Correctness
File facilitator/main.py : Lines ~121–145

Description

The old code created two separate signer instances from BSC_PRIVATE_KEY:

  • bsc_signer for BSC Testnet
  • bsc_mainnet_signer for BSC Mainnet

The new code creates a single bsc_signer and reuses it for both networks. While this is likely functionally correct (same key on both networks), it's a silent behavioral change. If the underlying signer object maintains network-specific state (e.g., nonce tracking, connection pooling, or RPC endpoints), sharing one instance across two networks could cause unexpected behaviour under concurrent settlement operations.

Recommendation

Verify that EvmFacilitatorSigner is stateless (or safely reentrant across multiple concurrent networks). If any network-specific state is held, create separate instances per network. Add a comment documenting the intentional sharing.


Suggestions

[S-01] package-lock.json Is a New First-Time Commit — Verify CI Compatibility

File: client/typescript/package-lock.json
Description: A 3,057-line package-lock.json was added to the repository for the first time. This is correct practice for reproducible builds. However, if any CI or Docker build step uses npm install instead of npm ci, the lock file will be silently ignored.
Suggestion: Audit Dockerfile and CI scripts to ensure they run npm ci (which enforces lock-file fidelity) rather than npm install.


[S-02] Exact Version Pin for bankofai-x402 May Be Too Strict

File: requirements.txt
Description: The dependency is now pinned as bankofai-x402[tron,fastapi]==0.5.0. Exact pins prevent automatic uptake of patch-level security fixes.
Suggestion: Consider using a compatible-release specifier ~=0.5.0 to allow patch updates while pinning to the 0.5.x line.


[S-03] Trust Model of @bankofai/agent-wallet Not Documented

File: README.md, client/typescript/package-lock.json
Description: The PR introduces a new dependency on @bankofai/agent-wallet@2.3.0 which manages encrypted private-key storage. The README documents how to use it but says nothing about where the keystore is stored on disk, what encryption algorithm is used, or what the threat model is.
Suggestion: Add a brief security note to the README (e.g., "keystore is stored at ~/.agent-wallet/ encrypted with AES-256-GCM; protect this directory") so operators can make informed security decisions.


Positive Observations

Area Observation
Security Replacing plaintext .env private keys with an encrypted keystore is a meaningful, correct security improvement.
Async architecture Migrating FastAPI startup to the @asynccontextmanager lifespan pattern is the modern, recommended approach and allows await to be used safely for key initialisation.
GasFree flexibility The new per-network GASFREE_API_KEY_{NETWORK} env vars with generic fallback is a clean, backward-compatible design.
Dependency hygiene Switching from a git-source reference (git+https://...@v0.4.4) to a stable PyPI release (==0.5.0) improves build reproducibility and supply chain transparency.
Code deduplication The facilitator now uses a single bsc_signer for both testnet and mainnet BSC mechanisms instead of instantiating two identical signers — reducing redundant initialization.
Documentation The README upgrade path for existing users (TRON_PRIVATE_KEYnpx agent-wallet init) is clear and helpful.

Checklist Results

Category Items Checked Pass Fail N/A Notes
Correctness 8 5 3 0 Unconditional EVM init; missing error handling; removed address diagnostics
Security 10 9 0 1 Positive improvement overall; no injection risks; CORS allow_origins=["*"] unchanged from main
Performance 7 7 0 0 No regressions introduced
Code Quality 10 8 2 0 Print ordering bug; leading-underscore var names slightly inconsistent
Testing 7 0 0 7 No tests present in diff; test suite not in scope of changed files
Documentation 6 5 1 0 Agent-wallet security model not documented
Compatibility 5 3 2 0 Breaking: TRON-only deployments break; SDK version bump from 0.4.x to 0.5.x
Observability 4 2 2 0 TRON/EVM addresses removed from Python client output; BSC address out-of-order in facilitator

Disclaimer

This is an automated code review. It supplements but does not replace human review. The reviewer analyzed only the diff between main and dev/agent_wallet_0316. Runtime behaviour, integration testing, keystore encryption correctness, and deployment impact are not covered.


Report generated by Code Review Skill v1.0.0
Date: 2026-03-21

@Hades-Ye
Hades-Ye merged commit 65f711d into main Mar 21, 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.

2 participants