Skip to content

feat: add TRON substreams for ERC20FeeProxy payment detection#2

Merged
rodrigopavezi merged 29 commits intomainfrom
add-tron-substreams
Feb 9, 2026
Merged

feat: add TRON substreams for ERC20FeeProxy payment detection#2
rodrigopavezi merged 29 commits intomainfrom
add-tron-substreams

Conversation

@rodrigopavezi
Copy link
Member

@rodrigopavezi rodrigopavezi commented Jan 29, 2026

  • Move substreams-tron from requestNetwork monorepo
  • Add tron/ folder with Rust WASM module for indexing payments
  • Add GitHub Actions CI workflow with optional integration tests
  • Index TransferWithReferenceAndFee events from ERC20FeeProxy
  • Support both Nile testnet and mainnet contracts

Summary by CodeRabbit

  • New Features

    • TRON Substreams module to index ERC20FeeProxy payment events on TRON mainnet
    • GraphQL schema for payments to support subgraph/GraphQL integrations
    • SQL schema and sink for persistent storage, including energy/fee metadata
  • Chores

    • CI pipeline for build, test, integration, and package publishing
    • Docker and Docker Compose deployment configs and production compose
  • Documentation

    • Expanded README with setup, build, run, and deployment guides

- Move substreams-tron from requestNetwork monorepo
- Add tron/ folder with Rust WASM module for indexing payments
- Add GitHub Actions CI workflow with optional integration tests
- Index TransferWithReferenceAndFee events from ERC20FeeProxy
- Support both Nile testnet and mainnet contracts
@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a complete TRON Substreams module: Rust map/db handlers and generated protobufs, Substreams manifest and SQL/GraphQL schemas, packaging and Docker sink, CI workflow for build/integration/publish, README and environment configs.

Changes

Cohort / File(s) Summary
Substreams runtime & protobufs
tron/src/lib.rs, tron/src/pb/*, tron/proto/request/tron/v1/payments.proto, tron/Cargo.toml, tron/buf.gen.yaml, tron/substreams.yaml
New TRON mapping implementation for TransferWithReferenceAndFee, ABI decoding utilities, energy/fee extraction, Payment/Payments protobufs, generated prost modules, Cargo manifest, and Substreams manifest declaring map_erc20_fee_proxy_payments and db_out.
Build, CI and packaging
.github/workflows/tron-build.yml, tron/Makefile, tron/buf.gen.yaml
Adds GitHub Actions multi-job pipeline (build-and-test, integration-test-mainnet, publish-package), Makefile targets for build/package/test/gui, and Buf generate config for prost generation.
SQL schema & sink
tron/schema.sql, Dockerfile.sink, docker-compose.yml, docker-compose.prod.yml
Adds payments table (energy_used/energy_fee/net_fee added) with multiple indexes, Docker image for substreams-sink-sql with entrypoint and healthcheck, local and prod docker-compose definitions.
GraphQL / subgraph schema
tron/schema.graphql
Adds GraphQL Payment entity for subgraph with fields matching Payment protobuf.
Docs, examples & env
tron/README.md, README.md, .env.example, .env.prod.example
Comprehensive README updates and TRON module README, example production/dev env templates and deployment instructions.
Packaging & artifacts
tron/*.spkg (packaging references), .gitignore, tron/Makefile
Packaging targets and CI artifact naming/version extraction; .gitignore updated to exclude build artifacts and spkg except tron/*.spkg.
Generated prost modules & hashes
tron/src/pb/.last_generated_hash, tron/src/pb/*.rs
Adds prost-generated Rust modules (google.api, sf.* types, request.tron.v1) and a generation hash file.

Sequence Diagram(s)

sequenceDiagram
participant Map as map_erc20_fee_proxy_payments
participant Pack as db_out / substreams pack
participant Sink as substreams-sink-sql
participant Postgres as Postgres
participant Mainnet as Tron Mainnet (endpoint)

Map->>Mainnet: read blocks & transactions (logs)
Map-->>Pack: emit Payments proto
Pack->>Sink: package .spkg / send DatabaseChanges
Sink->>Postgres: apply DB schema & insert/update payments
Postgres-->>Sink: ack
Sink-->>Pack: run healthchecks/logs
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title directly and accurately describes the main change: adding a TRON substreams module for detecting ERC20FeeProxy payment events.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-tron-substreams

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@rodrigopavezi rodrigopavezi marked this pull request as ready for review January 29, 2026 01:23
- Add deploy_subgraph workflow input to trigger deployment
- Add deploy_network choice (nile or mainnet)
- Install Graph CLI and authenticate with deploy key
- Package substreams and deploy to The Graph Studio
- Requires GRAPH_STUDIO_DEPLOY_KEY secret
- Push to main → deploy to Nile testnet (with commit SHA in version)
- GitHub release published → deploy to mainnet (with release tag as version)
- Manual workflow_dispatch still available for both networks
- Integration tests run before testnet deployment
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tron/subgraph.yaml`:
- Around line 16-19: The subgraph source.file references a package file with
hyphens ("./request-network-tron-v0.1.0.spkg") but the actual built package name
uses underscores from the substreams package (request_network_tron), so update
the source.package.file value in tron/subgraph.yaml to
"./request_network_tron-v0.1.0.spkg" so it matches the package name; keep the
same moduleName (map_erc20_fee_proxy_payments) and ensure the filename spelling
matches the package name used in tron/substreams.yaml (request_network_tron).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In @.github/workflows/tron-build.yml:
- Around line 227-234: The deploy-mainnet job currently only depends on
build-and-test, so it can run without running integration-test; update the job's
needs array for the deploy-mainnet job to include integration-test (i.e., change
needs: [build-and-test] to needs: [build-and-test, integration-test]) so mainnet
deployments wait for integration tests to pass.
- Around line 129-143: The current step runs "substreams run ./substreams.yaml
map_erc20_fee_proxy_payments" and redirects output to output.json but masks
failures with "|| true" and then uses a fragile grep -q "error" on output.json;
remove the "|| true" so the command's exit code is preserved, check the
command's exit status immediately (fail if non-zero), and replace the blind grep
with a precise JSON check against output.json (e.g., using jq to test for a
top-level error field or non-success status) instead of matching any substring
"error"; update references to the exact symbols "substreams run
./substreams.yaml map_erc20_fee_proxy_payments", "output.json", and the current
grep -q "error" usage when making this change.
🧹 Nitpick comments (4)
.github/workflows/tron-build.yml (4)

49-60: Consider including Cargo.lock in the cache key for reproducible builds.

The cache key only uses Cargo.toml hash, which doesn't capture locked dependency versions. If Cargo.lock exists in the repository, including it ensures cache invalidation when dependencies are updated.

Also, caching ~/.cargo/bin/ may persist stale tool binaries across workflow runs. Consider removing it unless specific tools are installed there.

♻️ Suggested improvement
-          key: ${{ runner.os }}-cargo-${{ hashFiles('tron/Cargo.toml') }}
+          key: ${{ runner.os }}-cargo-${{ hashFiles('tron/Cargo.lock', 'tron/Cargo.toml') }}

112-116: Consider pinning the Substreams CLI version for reproducible CI.

Using releases/latest can cause unexpected CI failures or behavior changes when a new CLI version is released with breaking changes.

♻️ Suggested fix
       - name: Install Substreams CLI
         run: |
-          curl -sSL https://github.com/streamingfast/substreams/releases/latest/download/substreams_linux_x86_64.tar.gz | tar xz
+          # Pin to a specific version for reproducible builds
+          SUBSTREAMS_VERSION="v1.12.1"  # Update as needed
+          curl -sSL "https://github.com/streamingfast/substreams/releases/download/${SUBSTREAMS_VERSION}/substreams_linux_x86_64.tar.gz" | tar xz
           sudo mv substreams /usr/local/bin/

292-308: Consider validating version format before deployment.

The mainnet deployment extracts version from release tag or Cargo.toml without validation. Malformed versions could cause deployment issues or make it harder to track releases.

♻️ Suggested improvement
       - name: Deploy to Mainnet
         working-directory: tron
         run: |
           # For releases, use the release tag as version
           if [ "${{ github.event_name }}" = "release" ]; then
             VERSION="${{ github.event.release.tag_name }}"
           else
             VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
             VERSION="v${VERSION}"
           fi
           
+          # Validate version format (basic semver check)
+          if ! echo "$VERSION" | grep -qE '^v?[0-9]+\.[0-9]+\.[0-9]+'; then
+            echo "WARNING: Version '$VERSION' may not follow semver format"
+          fi
+          
           echo "Deploying to request-payments-tron with version: $VERSION"

207-208: The suggested environment variable approach is not supported by Graph CLI.

Graph CLI does not support GRAPH_AUTH_TOKEN or similar environment variables for authentication. The graph auth --studio command expects the deploy key as a direct argument, which is the documented pattern. While the security concern about passing secrets via command-line arguments is valid, the suggested alternative will not work.

For CI environments, consider using graph deploy --access-token <token> instead, which is designed for non-interactive usage and avoids keychain storage complications.

- Add unit tests for base58 encoding, address parsing, uint256 parsing
- Add test for full TransferWithReferenceAndFee event data structure
- Add test for edge cases (short data, zero values)
- Improve integration test to output JSON and validate structure
- Integration test processes 100 blocks and validates payment fields
Copy link
Member

@MantisClone MantisClone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

Thanks for moving the Tron substreams to the dedicated payments-substream repo - this follows the established convention (like price-aggregators-subgraph) and keeps the SDK repo focused.

Items to Address

  1. Amount format (tron/src/lib.rs) - see pending comment thread about hex vs decimal

  2. Package filename (tron/subgraph.yaml:19) - please verify the filename produced by make package matches what's referenced here (underscores vs hyphens)

Suggestions

  1. Package naming (tron/substreams.yaml:3) - worth considering what naming convention will work as the repo grows to support other protocols (see inline comment)

  2. CI error handling (.github/workflows/tron-build.yml:153) - set +e masks failures; consider explicit exit code checking

Questions

  • What's the plan for packages/substreams-tron in the requestNetwork monorepo PR #1691? Should that code be removed now that this repo is the canonical location?

What Looks Good

  • Rust implementation is solid with good test coverage
  • Base58Check address encoding is correct
  • Event signature detection is properly validated
  • CI/CD pipeline is comprehensive (build, test, deploy stages)
  • README documentation is helpful

- Fix package filename in subgraph.yaml (use underscores to match substreams.yaml)
- Improve CI error handling by checking exit code directly instead of fragile grep
- Convert uint256 to decimal strings for TheGraph BigInt compatibility
…ingfast

- Update substreams.yaml for mainnet only (block 79216121)
- Use mainnet-evm.tron.streamingfast.io:443 endpoint
- Add CI workflow for build, test, and publish on merge to main
- Add Dockerfile.sink and docker-compose.yml for SQL sink deployment
- Remove subgraph.yaml (Substreams-Powered Subgraphs not supported for non-EVM)
- Add .gitignore to exclude build artifacts
- Simplify lib.rs for mainnet-only proxy address
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tron/src/lib.rs`:
- Around line 194-201: The function extract_owner_address currently slices raw
protobuf bytes (parameter.value[0..21]) and thus picks up wire-format tag/length
bytes; instead decode the Any.value as the actual TriggerSmartContract protobuf
message using prost::Message::decode (or the generated TriggerSmartContract
struct), then read the owner_address field from that decoded struct and
base58_encode the actual 20-byte address; handle decode errors by
logging/returning an empty string or Result as appropriate. Ensure you reference
extract_owner_address and use prost::Message::decode on parameter.value to get
TriggerSmartContract.owner_address before encoding.

Include the .spkg file in the repository so Easypanel can build the Docker image.
Simplify database connection config to avoid variable substitution issues.
Use list format instead of map format to properly pass through
environment variables from Easypanel to the container.
Join the easypanel network to allow DNS resolution of other
Easypanel services like PostgreSQL.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.gitignore:
- Around line 4-6: Remove the tracked .spkg artifact by deleting the
"!tron/*.spkg" negation entry from .gitignore and remove the committed .spkg
file from Git history/working tree (unstage/remove the tracked tron/*.spkg and
commit the deletion, e.g., git rm --cached or git rm then commit) so the
CI-built tron .spkg artifact is used instead; ensure only the general pattern
"*.spkg" remains in .gitignore and commit the .gitignore change along with the
removal of the tracked file.
🧹 Nitpick comments (1)
.gitignore (1)

1-20: Consider adding optional ignore patterns.

While the current patterns cover the essentials, you might want to add:

  • *.log – Log files that may be generated during development or testing
  • node_modules/ – If any JavaScript/TypeScript tooling is added later
  • .env.* – More general pattern to catch variants like .env.production, .env.test, etc.

These are optional improvements and not critical for the current PR.

- Added 'chain' field to Payment protobuf message
- Updated schema.sql with chain column and indexes
- Modified Rust code to parse chain from params (defaults to "tron")
- Updated db_out to include chain in database changes
- Key now includes chain for uniqueness across networks
- Added tests for chain parameter parsing

This allows the same payments table to be used by multiple
substreams targeting different blockchain networks.
- Prerequisites and installation instructions
- Project structure overview
- Development workflow (changes, build, test)
- Local stream and sink testing
- Production deployment to Easypanel
- Multi-chain support documentation
- Troubleshooting section
- Add substreams-entity-change dependency for graph_out module
- Implement graph_out function to convert payments to EntityChanges
- Add subgraph.yaml manifest for Substreams-powered subgraph deployment
- Update schema.graphql with chain field for multi-chain support
- Update README with The Graph deployment instructions
- Rebuild .spkg with new graph_out module
- Add deploy-subgraph job that runs on merge to main
- Install Graph CLI and deploy to Subgraph Studio
- Add manual workflow_dispatch option for subgraph deployment
- Deploy uses version from Cargo.toml as version label
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tron/src/lib.rs`:
- Around line 146-148: The code is parsing payment.amount and payment.fee_amount
to i64 then falling back to 0 (causing data corruption for large values);
instead pass the original string values into the .set calls so the Graph BigInt
can accept them. Replace the .set("amount",
payment.amount.parse::<i64>().unwrap_or(0)) and .set("feeAmount",
payment.fee_amount.parse::<i64>().unwrap_or(0)) uses with .set("amount",
payment.amount) and .set("feeAmount", payment.fee_amount) (or
payment.amount.clone()/payment.fee_amount.clone() as needed) so values are
stored as strings rather than truncated i64s. Ensure payment.payment_reference
handling remains unchanged.

Use request-payments-tron to follow existing naming pattern:
- request-payments-mainnet (Ethereum)
- request-payments-matic (Polygon)
- request-payments-arbitrum-one
- etc.
The Graph only supports Substreams on native TRON - not subgraphs.
Subgraphs are only available for TRON EVM, which is a different network.

Removed:
- subgraph.yaml
- graph_out module and function
- substreams-entity-change dependency
- deploy-subgraph GitHub Actions job
- The Graph deployment docs from README

SQL sink remains as the primary deployment option for native TRON.
- Add 0x prefix to payment_reference to match SDK expectations
- Add Hasura GraphQL API documentation to README
- Update substream package with payment_reference fix
- Add energy_used, energy_fee, and net_fee fields to Payment proto
- Extract TRON resource info from transaction receipt
- Update SQL schema with new columns
- Regenerate protobuf bindings and spkg
Copy link
Member

@aimensahnoun aimensahnoun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need the log file replay.log file ? or is it something we can add to the gitingore

Same with the spkg binary

Copy link
Member

@aimensahnoun aimensahnoun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the coderabbit comments @rodrigopavezi worth taking a look into

…tract

The previous implementation sliced raw protobuf bytes (parameter.value[0..21])
which incorrectly included wire-format tag/length bytes. Now uses
prost::Message::decode to properly deserialize the Any.value as a
TriggerSmartContract message and reads the owner_address field directly.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tron/src/lib.rs`:
- Line 112: The composite key built as format!("{}:{}:{}", payment.chain,
payment.tx_hash, payment.payment_reference) can collide when a single tx emits
multiple events with the same payment_reference; change the key construction in
tron/src/lib.rs (the variable named key) to include a per-event unique
identifier such as the log/event index (or an incrementing counter) so each
event produces a unique id (e.g., format!("{}:{}:{}:{}", payment.chain,
payment.tx_hash, payment.payment_reference, event_index)). Update the
surrounding loop that calls push_change so it passes the event index (or
counter) into where key is created (adjust function signatures if needed) to
ensure the PRIMARY KEY in schema.sql is always unique.

Remove the committed .spkg binary artifact and build it during
docker build instead. This ensures the deployed package always
matches the source code and avoids tracking a 636KB binary in git.
…nt_reference

When a single transaction emits multiple events with the same
payment_reference, the composite PRIMARY KEY (chain:tx_hash:payment_reference)
collides. Include the log index from the transaction's log array so each event
produces a unique id (chain:tx_hash:payment_reference:log_index).
- Use glob pattern for .spkg COPY and dynamic discovery in entrypoint
  so version bumps in substreams.yaml don't break the build
- Fix DSN scheme from psql:// to postgres:// in docker-compose.yml
  to prevent database connection failures
Prevent transient Substreams CLI replay log from being accidentally committed.
- Use native TRON endpoint (mainnet.tron.streamingfast.io) consistently
  across CI, Makefile, and docs to match production deployments
- Fix timestamp conversion to divide before casting to u64, preventing
  incorrect values from signed integer reinterpretation
- Write composite key to the id column in db_out so SQL sink inserts
  succeed with the PRIMARY KEY constraint
Copy link
Member Author

Do we need the log file replay.log file ? or is it something we can add to the gitingore

Same with the spkg binary

Fixed. Cheers @aimensahnoun

Copy link
Member Author

rodrigopavezi commented Feb 9, 2026

Merge activity

  • Feb 9, 12:50 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • Feb 9, 12:50 PM UTC: @rodrigopavezi merged this pull request with Graphite.

@rodrigopavezi rodrigopavezi merged commit c7c2228 into main Feb 9, 2026
4 checks passed
@rodrigopavezi rodrigopavezi deleted the add-tron-substreams branch February 9, 2026 12:50
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