Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
67844e0
feat: add Tron substreams indexing package
rodrigopavezi Jan 26, 2026
72911b7
fix: correct event signature hash and add validation in substreams
rodrigopavezi Jan 27, 2026
89f4b38
fix: address PR review comments
rodrigopavezi Jan 27, 2026
f0f2e91
refactor: use params instead of hardcoded addresses in substreams
rodrigopavezi Jan 27, 2026
2b5cb47
fix: address remaining PR review comments
rodrigopavezi Jan 27, 2026
8992a96
fix: address substreams review comments
rodrigopavezi Jan 27, 2026
1c90463
feat: add CircleCI job for building substreams-tron Rust package
rodrigopavezi Jan 27, 2026
f6e5035
fix: use pre-generated proto types instead of missing substreams-tron…
rodrigopavezi Jan 27, 2026
51d5ef4
fix: update Rust version to 1.85 for edition 2024 support
rodrigopavezi Jan 27, 2026
67f9ab1
fix: downgrade prost to 0.11 to match substreams dependency
rodrigopavezi Jan 27, 2026
85fb33b
fix: update package.json to comply with npmPkgJsonLint rules
rodrigopavezi Jan 27, 2026
f8aa913
fix: skip Rust tests in Node.js test environment
rodrigopavezi Jan 27, 2026
7c06b8c
fix: correct import path in trc20-fee-proxy test
rodrigopavezi Jan 28, 2026
0b9235f
refactor: move substreams-tron to payments-substream repo
rodrigopavezi Jan 29, 2026
b6ff31a
fix: update codegen schema URL from sepolia to base
rodrigopavezi Jan 29, 2026
42d0b6f
Merge: resolve conflicts in TRON payment detection
rodrigopavezi Feb 5, 2026
9d91904
feat(payment-detection): add Hasura client for TRON payment detection
rodrigopavezi Feb 5, 2026
80b2546
resolve merge conflicts in tron retrievers
rodrigopavezi Feb 10, 2026
b380791
refactor(payment-detection): remove TronInfoRetriever and update Hasu…
rodrigopavezi Feb 10, 2026
5bb4414
feat(payment-detection): add TRON support to fee proxy and enhance Ha…
rodrigopavezi Feb 10, 2026
3828a8b
fix(payment-detection): update import path for TronTheGraphInfoRetriever
rodrigopavezi Feb 10, 2026
ed9413e
feat(payment-network): add support for TRON Base58 addresses in addre…
rodrigopavezi Feb 10, 2026
513eba9
feat(payment-network): enhance TRON address validation and support in…
rodrigopavezi Feb 10, 2026
9143032
test(payment-detection): enhance tests for TronERC20FeeProxyPaymentDe…
rodrigopavezi Feb 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/advanced-logic/src/advanced-logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,16 @@ export default class AdvancedLogic implements AdvancedLogicTypes.IAdvancedLogic
}

public getFeeProxyContractErc20ForNetwork(network?: string): FeeProxyContractErc20 {
return NearChains.isChainSupported(network)
? new FeeProxyContractErc20(this.currencyManager, undefined, undefined, network)
: this.extensions.feeProxyContractErc20;
if (NearChains.isChainSupported(network) || this.isTronNetwork(network)) {
return new FeeProxyContractErc20(this.currencyManager, undefined, undefined, network);
}
return this.extensions.feeProxyContractErc20;
}

private isTronNetwork(network?: string): boolean {
if (!network) return false;
const normalized = network.toLowerCase();
return normalized === 'tron' || normalized === 'nile';
}

protected getNetwork(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ export default abstract class AddressBasedPaymentNetwork<
}
}

/**
* Check if an address is a valid TRON Base58 address (starts with 'T', 34 chars).
* Use this in subclasses that override isValidAddress for TRON-specific networks.
*/
public static isTronAddress(address: string): boolean {
return /^T[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{33}$/.test(address);
}

protected isValidAddressForSymbolAndNetwork(
address: string,
symbol: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CurrencyTypes, ExtensionTypes, RequestLogicTypes } from '@requestnetwork/types';
import { NearChains, isSameChain } from '@requestnetwork/currency';
import { UnsupportedNetworkError } from '../address-based';
import AddressBasedPaymentNetwork, { UnsupportedNetworkError } from '../address-based';
import { FeeReferenceBasedPaymentNetwork } from '../fee-reference-based';

const EVM_CURRENT_VERSION = '0.2.0';
Expand Down Expand Up @@ -57,8 +57,16 @@ export default class Erc20FeeProxyPaymentNetwork<
} else {
return this.isValidAddressForSymbolAndNetwork(address, 'NEAR', 'near');
}
} else if (Erc20FeeProxyPaymentNetwork.isTronNetwork(this.network)) {
return AddressBasedPaymentNetwork.isTronAddress(address);
} else {
return super.isValidAddress(address);
}
}

private static isTronNetwork(network?: string): boolean {
if (!network) return false;
const normalized = network.toLowerCase();
return normalized === 'tron' || normalized === 'nile';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,43 @@ describe('extensions/payment-network/address-based', () => {
testAddressBasedPaymentNetwork.testIsValidAddress();
}).toThrowError(new UnsupportedCurrencyError({ value: 'test', network: 'mainnet' }));
});

it('base isValidAddress should reject TRON addresses for ERC20 currency type', () => {
class TestAddressBasedPaymentNetwork extends AddressBasedPaymentNetwork {
public constructor() {
super(
CurrencyManager.getDefault(),
ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_ADDRESS_BASED,
'test',
RequestLogicTypes.CURRENCY.ERC20,
);
}
public testIsValidAddress(address: string) {
return this.isValidAddress(address);
}
}
const pn = new TestAddressBasedPaymentNetwork();
// A valid TRON Base58 address should NOT be accepted by the base class
// because the base class has no network context to know this is a TRON request
expect(pn.testIsValidAddress('TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW')).toBe(false);
// A valid Ethereum address should still be accepted
expect(pn.testIsValidAddress('0x0000000000000000000000000000000000000000')).toBe(true);
});

it('static isTronAddress should correctly validate TRON addresses', () => {
expect(AddressBasedPaymentNetwork.isTronAddress('TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW')).toBe(
true,
);
expect(AddressBasedPaymentNetwork.isTronAddress('T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb')).toBe(
true,
);
// Invalid: too short
expect(AddressBasedPaymentNetwork.isTronAddress('TJCnKsPa7y5okkXvQAid')).toBe(false);
// Invalid: doesn't start with T
expect(
AddressBasedPaymentNetwork.isTronAddress('0x0000000000000000000000000000000000000000'),
).toBe(false);
// Invalid: empty
expect(AddressBasedPaymentNetwork.isTronAddress('')).toBe(false);
});
});
10 changes: 0 additions & 10 deletions packages/currency/src/chains/declarative/data/nile.ts

This file was deleted.

20 changes: 0 additions & 20 deletions packages/currency/src/chains/declarative/data/tron.ts

This file was deleted.

93 changes: 93 additions & 0 deletions packages/payment-detection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,99 @@ The code generation is included in the pre-build script and can be run manually:
yarn codegen
```

## TRON Payment Detection (Hasura-based)

TRON payment detection uses a Hasura GraphQL API backed by a PostgreSQL database that is populated by a Substreams-based indexer. This approach was chosen because The Graph does not support subgraphs for native TRON (only TRON EVM).

### Architecture

```
TRON Blockchain → Substreams → PostgreSQL → Hasura GraphQL → SDK
```

The payment data flows through:

1. **Substreams**: Indexes ERC20FeeProxy payment events from the TRON blockchain
2. **PostgreSQL**: Stores payment data via `substreams-sink-sql`
3. **Hasura**: Exposes the PostgreSQL data as a GraphQL API
4. **SDK**: Queries Hasura via `TronInfoRetriever` and `HasuraClient`

### Components

- **`TronFeeProxyPaymentDetector`**: Payment detector for TRON ERC20 Fee Proxy payments
- **`TronInfoRetriever`**: Retrieves payment events from Hasura, implements `ITheGraphBaseInfoRetriever`
- **`HasuraClient`**: GraphQL client for querying the Hasura endpoint

### Usage

The `TronFeeProxyPaymentDetector` is automatically registered in the `PaymentNetworkFactory` for TRON networks (`tron` and `nile`).

```typescript
import { PaymentNetworkFactory } from '@requestnetwork/payment-detection';

// The factory automatically uses TronFeeProxyPaymentDetector for TRON
const paymentNetwork = PaymentNetworkFactory.getPaymentNetworkFromRequest({
request,
advancedLogic,
});

const balance = await paymentNetwork.getBalance(request);
```

### Custom Hasura Endpoint

By default, the `HasuraClient` connects to the production Hasura endpoint. To use a custom endpoint:

```typescript
import {
HasuraClient,
TronInfoRetriever,
TronFeeProxyPaymentDetector,
} from '@requestnetwork/payment-detection';

// Create a custom Hasura client
const customClient = new HasuraClient({
baseUrl: 'https://your-hasura-instance.com/v1/graphql',
});

// Use it with TronInfoRetriever
const retriever = new TronInfoRetriever(customClient);

// Or use getHasuraClient with custom options
import { getHasuraClient } from '@requestnetwork/payment-detection';

const client = getHasuraClient('tron', {
baseUrl: 'https://your-hasura-instance.com/v1/graphql',
});
```

### TRON-specific Event Fields

TRON payment events include additional fields specific to the TRON blockchain:

```typescript
interface TronPaymentEvent {
txHash: string;
feeAmount: string;
block: number;
to: string;
from: string;
feeAddress?: string;
tokenAddress?: string;
// TRON-specific resource consumption
energyUsed?: string; // Total energy consumed
energyFee?: string; // Energy fee in SUN
netFee?: string; // Network/bandwidth fee in SUN
}
```

### Supported Networks

| Network | Chain Identifier | Description |
| ------- | ---------------- | ----------------- |
| `tron` | `tron` | TRON Mainnet |
| `nile` | `tron-nile` | TRON Nile Testnet |

# Test

```sh
Expand Down
2 changes: 1 addition & 1 deletion packages/payment-detection/codegen.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
overwrite: true
schema: 'https://api.studio.thegraph.com/query/67444/request-payments-sepolia/version/latest'
schema: 'https://api.studio.thegraph.com/query/67444/request-payments-base/version/latest'
documents: src/thegraph/queries/*.graphql
generates:
src/thegraph/generated/graphql.ts:
Expand Down
6 changes: 3 additions & 3 deletions packages/payment-detection/src/erc20/fee-proxy-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { makeGetDeploymentInformation } from '../utils';
import { TheGraphClient, TheGraphInfoRetriever } from '../thegraph';
import { ReferenceBasedDetectorOptions, TGetSubGraphClient } from '../types';
import { NearInfoRetriever } from '../near';
import { TronInfoRetriever } from '../tron/tron-info-retriever';
import { TronTheGraphInfoRetriever } from '../tron/retrievers/tron-thegraph-info-retriever';
import { NetworkNotSupported } from '../balance-error';

const PROXY_CONTRACT_ADDRESS_MAP = {
Expand Down Expand Up @@ -166,13 +166,13 @@ export class ERC20FeeProxyPaymentDetector<
| TheGraphClient
| TheGraphClient<CurrencyTypes.NearChainName>
| TheGraphClient<CurrencyTypes.TronChainName>,
): TheGraphInfoRetriever | NearInfoRetriever | TronInfoRetriever {
): TheGraphInfoRetriever | NearInfoRetriever | TronTheGraphInfoRetriever {
const graphInfoRetriever = EvmChains.isChainSupported(paymentChain)
? new TheGraphInfoRetriever(subgraphClient as TheGraphClient, this.currencyManager)
: NearChains.isChainSupported(paymentChain) && this.network
? new NearInfoRetriever(subgraphClient as TheGraphClient<CurrencyTypes.NearChainName>)
: TronChains.isChainSupported(paymentChain)
? new TronInfoRetriever(subgraphClient as TheGraphClient<CurrencyTypes.TronChainName>)
? new TronTheGraphInfoRetriever(subgraphClient as TheGraphClient<CurrencyTypes.TronChainName>)
: undefined;
if (!graphInfoRetriever) {
throw new Error(
Expand Down
13 changes: 11 additions & 2 deletions packages/payment-detection/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,13 @@ import {
unpadAmountFromChainlink,
} from './utils';
import { NearConversionNativeTokenPaymentDetector, NearNativeTokenPaymentDetector } from './near';
import { TronERC20FeeProxyPaymentDetector, TronInfoRetriever } from './tron';
import {
TronFeeProxyPaymentDetector,
getHasuraClient,
HasuraClient,
TronInfoRetriever,
} from './tron';
export type { TronPaymentEvent } from './tron/retrievers/tron-info-retriever';
import { FeeReferenceBasedDetector } from './fee-reference-based-detector';
import { SuperFluidPaymentDetector } from './erc777/superfluid-detector';
import { EscrowERC20InfoRetriever } from './erc20/escrow-info-retriever';
Expand All @@ -57,8 +63,11 @@ export {
SuperFluidPaymentDetector,
NearNativeTokenPaymentDetector,
NearConversionNativeTokenPaymentDetector,
TronERC20FeeProxyPaymentDetector,
// TRON
TronFeeProxyPaymentDetector,
TronInfoRetriever,
getHasuraClient,
HasuraClient,
EscrowERC20InfoRetriever,
SuperFluidInfoRetriever,
MetaDetector,
Expand Down
8 changes: 4 additions & 4 deletions packages/payment-detection/src/payment-network-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { SuperFluidPaymentDetector } from './erc777/superfluid-detector';
import { EthFeeProxyPaymentDetector, EthInputDataPaymentDetector } from './eth';
import { AnyToERC20PaymentDetector, AnyToEthFeeProxyPaymentDetector } from './any';
import { NearConversionNativeTokenPaymentDetector, NearNativeTokenPaymentDetector } from './near';
import { TronERC20FeeProxyPaymentDetector } from './tron';
import { TronFeeProxyPaymentDetector } from './tron';
import { getPaymentNetworkExtension } from './utils';
import { getTheGraphClient } from './thegraph';
import { getDefaultProvider } from 'ethers';
Expand Down Expand Up @@ -56,12 +56,12 @@ const supportedPaymentNetwork: ISupportedPaymentNetworkByCurrency = {
'near-testnet': {
[PN_ID.ERC20_FEE_PROXY_CONTRACT]: ERC20FeeProxyPaymentDetector<CurrencyTypes.NearChainName>,
},
// TRON chains
// TRON networks
tron: {
[PN_ID.ERC20_FEE_PROXY_CONTRACT]: TronERC20FeeProxyPaymentDetector,
[PN_ID.ERC20_FEE_PROXY_CONTRACT]: TronFeeProxyPaymentDetector,
},
nile: {
[PN_ID.ERC20_FEE_PROXY_CONTRACT]: TronERC20FeeProxyPaymentDetector,
[PN_ID.ERC20_FEE_PROXY_CONTRACT]: TronFeeProxyPaymentDetector,
},

'*': {
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
schema: https://api.studio.thegraph.com/query/67444/request-payments-sepolia/version/latest
schema: https://api.studio.thegraph.com/query/67444/request-payments-base/version/latest
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# Using local schema until the subgraph is deployed to The Graph Studio
schema: ../../../../../substreams-tron/schema.graphql
# Local schema for TRON payment queries
schema: ./schema.graphql
9 changes: 6 additions & 3 deletions packages/payment-detection/src/tron/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export { TronERC20FeeProxyPaymentDetector } from './tron-fee-proxy-detector';
export { TronInfoRetriever } from './tron-info-retriever';
export type { TronPaymentEvent } from './tron-info-retriever';
export { TronERC20FeeProxyPaymentDetector as TronFeeProxyPaymentDetector } from './tron-fee-proxy-detector';
export { TronInfoRetriever } from './retrievers/tron-info-retriever';
export type { TronPaymentEvent } from './retrievers/tron-info-retriever';
export { TronTheGraphInfoRetriever } from './retrievers/tron-thegraph-info-retriever';
export type { TronTheGraphPaymentEvent } from './retrievers/tron-thegraph-info-retriever';
export { HasuraClient, getHasuraClient } from './retrievers/hasura-client';
Loading