Skip to content

Enable TRON mainnet gasfree USDT - #9

Merged
Hades-Ye merged 2 commits into
mainfrom
feature/tron-mainnet-gasfree-usdt
Mar 27, 2026
Merged

Enable TRON mainnet gasfree USDT#9
Hades-Ye merged 2 commits into
mainfrom
feature/tron-mainnet-gasfree-usdt

Conversation

@Hades-Ye

Copy link
Copy Markdown
Contributor

Summary

  • enable GasFree on TRON mainnet in facilitator and server
  • expose exact_gasfree for USDT only (nile + mainnet) when GasFree keys are set
  • update README
  • bump bankofai-x402 to 0.5.1

@Hades-Ye
Hades-Ye merged commit 2f8639c into main Mar 27, 2026
2 checks passed
@github-actions

Copy link
Copy Markdown

Code Review Report

Project: x402-demo
PR: mainfeature/tron-mainnet-gasfree-usdt
Review Date: 2026-03-27
Reviewer: AI Code Reviewer (Code Review Skill v1.0.0)


PR Overview

Branch Information

Property Value
From Branch main
To Branch feature/tron-mainnet-gasfree-usdt
Commits 2
Files Changed 5
Lines Added +65
Lines Removed -22

Commit History

Hash Message
be8a7f2 Bump bankofai-x402 to 0.5.1
fd60338 Enable TRON mainnet gasfree USDT in demo

Review Summary

Verdict

Verdict: Request Changes

Findings at a Glance

Critical Major Minor Suggestion
Count 0 1 4 2

Summary

This PR extends an existing TRON GasFree (gasless USDT) integration from Nile testnet to TRON Mainnet, and bumps the bankofai-x402 SDK from 0.5.0 to 0.5.1. The scope is well-contained: new environment variables (GASFREE_API_KEY_MAINNET, GASFREE_API_SECRET_MAINNET) gate mainnet GasFree registration in both the server and facilitator, endpoint pricing lists are built conditionally at startup, and the README and startup logs are updated accordingly.

The primary concern is a backward-compatibility hazard: both server/main.py and facilitator/main.py fall back from network-specific keys (GASFREE_API_KEY_MAINNET) to the generic GASFREE_API_KEY. Any deployment that previously configured the generic key to enable Nile GasFree will silently activate Mainnet GasFree upon this upgrade — a live-money behaviour change with no warning. This must be addressed before merging.

Four additional minor issues are noted: the server reads GasFree credentials it never actually passes to any mechanism, mutable module-level lists are used where constants would be safer, commented-out policy code constitutes dead code, and a missing blank line violates PEP 8. Two suggestions are offered for improved clarity and defensiveness.


Change Summary

1. TRON Mainnet GasFree Activation (facilitator/main.py, server/main.py)

File Change Type Description
facilitator/main.py Modified Reads GASFREE_API_KEY_MAINNET / GASFREE_API_SECRET_MAINNET; adds GasFreeAPIClient for tron:mainnet to shared client dict; registers ExactGasFreeFacilitatorMechanism for mainnet when enabled
server/main.py Modified Reads same env vars to derive gasfree_enabled_mainnet; conditionally registers ExactGasFreeServerMechanism for TRON Mainnet; builds MAINNET_PRICES / MAINNET_SCHEMES lists dynamically

Purpose: Enables the GasFree (gasless) payment path for USDT on TRON Mainnet in addition to Nile testnet, gated by the presence of API credentials.


2. Dynamic Nile Endpoint Pricing (server/main.py)

File Change Type Description
server/main.py Modified Replaces hard-coded prices / schemes lists in @x402_protected for /protected-nile with conditionally built NILE_PRICES / NILE_SCHEMES lists

Purpose: Removes previously unconditional exact_gasfree scheme advertisement for Nile, instead advertising it only when the GasFree credentials are actually configured — making the server and facilitator consistent.


3. Client Policy Disabled (client/python/main.py)

File Change Type Description
client/python/main.py Modified Comments out x402_client.register_policy(PreferGasFreeUSDTPolicy)

Purpose: Disables the custom GasFree-preferring policy so the default SDK selection logic (balance-based) is used during the demo, allowing the test to exercise the standard flow.


4. SDK Version Bump (requirements.txt)

File Change Type Description
requirements.txt Modified bankofai-x402[tron,fastapi] bumped from 0.5.0 to 0.5.1

Purpose: Picks up SDK changes required for TRON Mainnet GasFree support.


5. Documentation Update (README.md)

File Change Type Description
README.md Modified Adds exact_gasfree (USDT only) to the TRON Mainnet row of the network table; clarifies which networks support GasFree; documents new env vars GASFREE_API_KEY_MAINNET and GASFREE_API_SECRET_MAINNET

Purpose: Keeps operator documentation in sync with the new functionality.


Detailed Findings


Major

[MJ-01] Generic Fallback Credential Silently Enables Mainnet GasFree on Existing Deployments

Property Value
Severity Major
Category Correctness / Compatibility
File server/main.py : Lines 59–65 · facilitator/main.py : Lines 87–89

Description

Both the server and facilitator derive gasfree_enabled_mainnet using the same fallback chain as gasfree_enabled_nile:

# server/main.py  (lines 59-65)
gasfree_api_key_nile    = os.getenv("GASFREE_API_KEY_NILE")    or os.getenv("GASFREE_API_KEY")
gasfree_api_secret_nile = os.getenv("GASFREE_API_SECRET_NILE") or os.getenv("GASFREE_API_SECRET")
gasfree_enabled_nile    = bool(gasfree_api_key_nile and gasfree_api_secret_nile)

gasfree_api_key_mainnet    = os.getenv("GASFREE_API_KEY_MAINNET")    or os.getenv("GASFREE_API_KEY")
gasfree_api_secret_mainnet = os.getenv("GASFREE_API_SECRET_MAINNET") or os.getenv("GASFREE_API_SECRET")
gasfree_enabled_mainnet    = bool(gasfree_api_key_mainnet and gasfree_api_secret_mainnet)

Any operator who previously set only the generic GASFREE_API_KEY / GASFREE_API_SECRET (to enable Nile GasFree) will have both gasfree_enabled_nile and gasfree_enabled_mainnet resolve to True after this upgrade — silently activating GasFree settlement on TRON Mainnet (real funds). This is an unannounced, breaking default-behaviour change.

The same pattern appears in facilitator/main.py lines 83–89, and consequently the gasfree_clients dict passed to both ExactGasFreeFacilitatorMechanism instances will include a tron:mainnet entry derived from the generic credentials.

Code

# facilitator/main.py lines 83-89
gasfree_api_key_nile = os.getenv("GASFREE_API_KEY_NILE") or os.getenv("GASFREE_API_KEY")
gasfree_api_secret_nile = os.getenv("GASFREE_API_SECRET_NILE") or os.getenv("GASFREE_API_SECRET")
gasfree_enabled_nile = bool(gasfree_api_key_nile and gasfree_api_secret_nile)

gasfree_api_key_mainnet = os.getenv("GASFREE_API_KEY_MAINNET") or os.getenv("GASFREE_API_KEY")
gasfree_api_secret_mainnet = os.getenv("GASFREE_API_SECRET_MAINNET") or os.getenv("GASFREE_API_SECRET")
gasfree_enabled_mainnet = bool(gasfree_api_key_mainnet and gasfree_api_secret_mainnet)

Recommendation

Remove the generic-key fallback only for the mainnet credential, requiring explicit opt-in for the production network:

# Nile: network-specific key, falls back to generic (testnet only, low risk)
gasfree_api_key_nile    = os.getenv("GASFREE_API_KEY_NILE") or os.getenv("GASFREE_API_KEY")
gasfree_api_secret_nile = os.getenv("GASFREE_API_SECRET_NILE") or os.getenv("GASFREE_API_SECRET")
gasfree_enabled_nile    = bool(gasfree_api_key_nile and gasfree_api_secret_nile)

# Mainnet: MUST be explicit — no generic fallback (real money)
gasfree_api_key_mainnet    = os.getenv("GASFREE_API_KEY_MAINNET")
gasfree_api_secret_mainnet = os.getenv("GASFREE_API_SECRET_MAINNET")
gasfree_enabled_mainnet    = bool(gasfree_api_key_mainnet and gasfree_api_secret_mainnet)

Apply the same fix symmetrically in both facilitator/main.py and server/main.py. Document this in the README under the upgrade / migration notes.


Minor

[MN-01] Server Reads GasFree Credentials It Never Uses

Property Value
Severity Minor
Category Code Quality
File server/main.py : Lines 59–65

Description

server/main.py reads GASFREE_API_KEY_* / GASFREE_API_SECRET_* from the environment solely to derive two boolean flags. The credential values themselves are never passed to ExactGasFreeServerMechanism(), which takes no arguments. The inline comment # GasFree flags (kept in sync with facilitator envs) acknowledges the intent, but reading credentials unnecessarily can confuse readers and static analysis tools into thinking they are consumed.

Code

# server/main.py lines 58-65
# GasFree flags (kept in sync with facilitator envs)
gasfree_api_key_nile = os.getenv("GASFREE_API_KEY_NILE") or os.getenv("GASFREE_API_KEY")
gasfree_api_secret_nile = os.getenv("GASFREE_API_SECRET_NILE") or os.getenv("GASFREE_API_SECRET")
gasfree_enabled_nile = bool(gasfree_api_key_nile and gasfree_api_secret_nile)

gasfree_api_key_mainnet = os.getenv("GASFREE_API_KEY_MAINNET") or os.getenv("GASFREE_API_KEY")
gasfree_api_secret_mainnet = os.getenv("GASFREE_API_SECRET_MAINNET") or os.getenv("GASFREE_API_SECRET")
gasfree_enabled_mainnet = bool(gasfree_api_key_mainnet and gasfree_api_secret_mainnet)

Recommendation

Read the env vars directly into the boolean expressions without storing intermediate credential variables:

gasfree_enabled_nile = bool(
    os.getenv("GASFREE_API_KEY_NILE") or os.getenv("GASFREE_API_KEY")
) and bool(
    os.getenv("GASFREE_API_SECRET_NILE") or os.getenv("GASFREE_API_SECRET")
)
gasfree_enabled_mainnet = bool(os.getenv("GASFREE_API_KEY_MAINNET")) and bool(
    os.getenv("GASFREE_API_SECRET_MAINNET")
)

This also naturally enforces the explicit-only requirement from MJ-01 for mainnet.


[MN-02] Mutable Module-Level Lists Used as Decorator Constants

Property Value
Severity Minor
Category Code Quality
File server/main.py : Lines 184–188, 238–242

Description

NILE_PRICES, NILE_SCHEMES, MAINNET_PRICES, and MAINNET_SCHEMES are defined as plain list objects at module scope and then mutated with .append() during startup. They are later consumed by @x402_protected decorators at decoration time. Because Python lists are mutable and these are module-level globals, any inadvertent late append (e.g., in a test harness or monkey-patch) would silently alter the decorator's payment requirements without error. Using mutable state as configuration signals is also harder to reason about than immutable constants.

Code

# server/main.py lines 184-188
NILE_PRICES = ["0.0001 USDT", "0.0001 USDD"]
NILE_SCHEMES = ["exact_permit", "exact_permit"]
if gasfree_enabled_nile:
    NILE_PRICES.append("0.0001 USDT")
    NILE_SCHEMES.append("exact_gasfree")

Recommendation

Build the final lists as tuples (or freeze them after construction) to make their immutability explicit:

_nile_prices = ["0.0001 USDT", "0.0001 USDD"]
_nile_schemes = ["exact_permit", "exact_permit"]
if gasfree_enabled_nile:
    _nile_prices.append("0.0001 USDT")
    _nile_schemes.append("exact_gasfree")
NILE_PRICES: tuple[str, ...] = tuple(_nile_prices)
NILE_SCHEMES: tuple[str, ...] = tuple(_nile_schemes)

Apply the same pattern for the MAINNET_* pair.


[MN-03] Dead Code — PreferGasFreeUSDTPolicy Defined but Never Registered

Property Value
Severity Minor
Category Code Quality
File client/python/main.py : Lines 31–41, 108

Description

The PreferGasFreeUSDTPolicy class (lines 31–41) remains in the file and is referenced in a comment on line 108, but the actual register_policy call is commented out. The class and its import (TokenRegistry) constitute dead code that will never execute, adding cognitive overhead for future readers and triggering linter warnings.

Code

# client/python/main.py line 108
# x402_client.register_policy(PreferGasFreeUSDTPolicy)

Recommendation

Either:

  1. Remove the PreferGasFreeUSDTPolicy class and any imports used exclusively by it (e.g., verify TokenRegistry is still needed elsewhere), replacing the commented line with a brief note explaining the deliberate omission; or
  2. Retain it with a # noqa annotation and a docstring clearly stating it is available for manual GasFree preference testing.

For a demo codebase, option 1 keeps the code clean; option 2 is acceptable if the policy is expected to be re-enabled during experimentation.


[MN-04] Missing Blank Line Before Decorator (PEP 8)

Property Value
Severity Minor
Category Code Quality
File server/main.py : Lines 188–190

Description

There is no blank line separating the if gasfree_enabled_nile: block from the @app.get("/protected-nile") decorator. PEP 8 requires two blank lines between top-level definitions; while decorators are not function bodies, the visual break is expected between a conditional block and the next top-level entity. The MAINNET_* block (lines 238–244) correctly has a trailing blank line before its decorator.

Code

if gasfree_enabled_nile:
    NILE_PRICES.append("0.0001 USDT")
    NILE_SCHEMES.append("exact_gasfree")

@app.get("/protected-nile")   # ← only one blank line here; should be two
@x402_protected(

Recommendation

Add a second blank line between the conditional block and the decorator to match the rest of the file's style:

if gasfree_enabled_nile:
    NILE_PRICES.append("0.0001 USDT")
    NILE_SCHEMES.append("exact_gasfree")

@app.get("/protected-nile")

Suggestions

[S-01] Pass Network-Scoped Client Dicts to Each GasFree Mechanism in the Facilitator

File: facilitator/main.py : Lines 122–135

Description: Both the Nile and Mainnet ExactGasFreeFacilitatorMechanism instances receive the same shared gasfree_clients dict, which may contain entries for multiple networks. If the mechanism internally selects a client by network key, this is harmless, but it means each mechanism instance holds a reference to the other network's API credentials unnecessarily. Passing a network-scoped slice makes intent explicit and reduces the risk of credential cross-contamination if the mechanism's routing logic ever changes.

Suggestion:

if network == "nile" and gasfree_enabled_nile:
    gasfree_mechanism = ExactGasFreeFacilitatorMechanism(
        tron_signer,
        clients={"tron:nile": gasfree_clients["tron:nile"]},  # scoped
        base_fee=TRON_BASE_FEE,
    )
    facilitator.register([f"tron:{network}"], gasfree_mechanism)
if network == "mainnet" and gasfree_enabled_mainnet:
    gasfree_mechanism = ExactGasFreeFacilitatorMechanism(
        tron_signer,
        clients={"tron:mainnet": gasfree_clients["tron:mainnet"]},  # scoped
        base_fee=TRON_BASE_FEE,
    )
    facilitator.register([f"tron:{network}"], gasfree_mechanism)

[S-02] Document the Generic-Key Fallback Behaviour and Upgrade Path in README

File: README.md

Description: The README documents GASFREE_API_KEY_MAINNET / GASFREE_API_SECRET_MAINNET as new optional vars, but does not mention that the generic GASFREE_API_KEY / GASFREE_API_SECRET values are also read as a fallback. An operator who set generic keys on a prior version (Nile-only) has no way of knowing that upgrading will also activate Mainnet GasFree unless they read the source code. A brief note under the GasFree section and/or an upgrade notice would prevent surprise in production environments.

Suggestion: Add a note such as:

Note for upgraders: If you previously set GASFREE_API_KEY / GASFREE_API_SECRET (generic keys) to enable GasFree on Nile, be aware that these keys also activate Mainnet GasFree in this release. To keep Mainnet GasFree disabled, unset the generic keys and use GASFREE_API_KEY_NILE / GASFREE_API_SECRET_NILE explicitly.


Positive Observations

Area Observation
Conditional registration Gating ExactGasFreeServerMechanism and ExactGasFreeFacilitatorMechanism registration behind gasfree_enabled_* booleans is correct and ensures GasFree is never advertised when credentials are absent.
Refactor: ternary → if-blocks Replacing the complex conditional expression for gasfree_clients in facilitator/main.py with sequential if blocks is a clear readability improvement.
Observability print(f"GasFree Mainnet Enabled: {gasfree_enabled_mainnet}") added to both server and facilitator startup banners makes the runtime configuration immediately visible in logs.
Symmetric implementation Nile and Mainnet credential loading, client construction, mechanism registration, and endpoint pricing follow a parallel and consistent pattern, making the code easy to extend for future networks.
README accuracy The network support table and GasFree section in the README are updated to accurately reflect USDT-only restriction on Mainnet GasFree.
SDK bump Pinning to bankofai-x402==0.5.1 picks up upstream library support; using == rather than >= prevents accidental breakage from future SDK releases in this demo.

Checklist Results

Category Items Checked Pass Fail N/A Notes
Correctness 8 7 1 0 Generic fallback silently enables mainnet (MJ-01)
Security 10 10 0 0 No hardcoded secrets; credentials properly env-sourced
Performance 7 7 0 0 Startup-time registration; no runtime perf concerns
Code Quality 10 6 4 0 Mutable globals (MN-02), dead code (MN-03), PEP 8 (MN-04), misleading reads (MN-01)
Testing 7 0 0 7 Demo project; no test suite in scope
Documentation 6 5 1 0 Fallback behaviour / upgrade path not documented (S-02)
Compatibility 5 4 1 0 Silent breaking change for existing deployments (MJ-01)
Observability 4 4 0 0 GasFree enabled flags added to startup logs

Disclaimer

This is an automated code review. It supplements but does not replace human review. The reviewer analyzed only the diff between main and feature/tron-mainnet-gasfree-usdt. Runtime behaviour, integration testing against the TRON Mainnet GasFree API, and deployment impact are not covered.


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

@Hades-Ye
Hades-Ye deleted the feature/tron-mainnet-gasfree-usdt branch March 27, 2026 04:38
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.

1 participant