Skip to content

Commit cf070e9

Browse files
committed
feat: add Tron payment detection support
- Add TronFeeProxyDetector for detecting Tron payments - Add TronInfoRetriever for fetching payment data from TheGraph - Add GraphQL queries for Tron payments - Integrate Tron detector into payment network factory - Add unit tests for detector and retriever
1 parent 9005e3a commit cf070e9

File tree

12 files changed

+598
-3
lines changed

12 files changed

+598
-3
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
overwrite: true
2+
# Using local schema until the subgraph is deployed to The Graph Studio
3+
schema: '../substreams-tron/schema.graphql'
4+
documents: src/thegraph/queries/tron/*.graphql
5+
generates:
6+
src/thegraph/generated/graphql-tron.ts:
7+
plugins:
8+
- 'typescript'
9+
- 'typescript-operations'
10+
- 'typescript-graphql-request'
11+
- 'typescript-document-nodes'

packages/payment-detection/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"prepare": "yarn run build",
3939
"test": "jest --runInBand",
4040
"test:watch": "yarn test --watch",
41-
"codegen": "graphql-codegen --config codegen.yml ; graphql-codegen --config codegen-superfluid.yml; graphql-codegen --config codegen-near.yml"
41+
"codegen": "graphql-codegen --config codegen.yml ; graphql-codegen --config codegen-superfluid.yml; graphql-codegen --config codegen-near.yml; graphql-codegen --config codegen-tron.yml"
4242
},
4343
"dependencies": {
4444
"@requestnetwork/currency": "0.30.0",

packages/payment-detection/src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
getTheGraphClientUrl,
1717
getTheGraphEvmClient,
1818
getTheGraphNearClient,
19+
getTheGraphTronClient,
1920
} from './thegraph';
2021
import {
2122
calculateEscrowState,
@@ -30,6 +31,7 @@ import {
3031
unpadAmountFromChainlink,
3132
} from './utils';
3233
import { NearConversionNativeTokenPaymentDetector, NearNativeTokenPaymentDetector } from './near';
34+
import { TronERC20FeeProxyPaymentDetector, TronInfoRetriever } from './tron';
3335
import { FeeReferenceBasedDetector } from './fee-reference-based-detector';
3436
import { SuperFluidPaymentDetector } from './erc777/superfluid-detector';
3537
import { EscrowERC20InfoRetriever } from './erc20/escrow-info-retriever';
@@ -55,6 +57,8 @@ export {
5557
SuperFluidPaymentDetector,
5658
NearNativeTokenPaymentDetector,
5759
NearConversionNativeTokenPaymentDetector,
60+
TronERC20FeeProxyPaymentDetector,
61+
TronInfoRetriever,
5862
EscrowERC20InfoRetriever,
5963
SuperFluidInfoRetriever,
6064
MetaDetector,
@@ -65,6 +69,7 @@ export {
6569
getTheGraphClientUrl,
6670
getTheGraphEvmClient,
6771
getTheGraphNearClient,
72+
getTheGraphTronClient,
6873
parseLogArgs,
6974
padAmountForChainlink,
7075
unpadAmountFromChainlink,

packages/payment-detection/src/payment-network-factory.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { SuperFluidPaymentDetector } from './erc777/superfluid-detector';
2424
import { EthFeeProxyPaymentDetector, EthInputDataPaymentDetector } from './eth';
2525
import { AnyToERC20PaymentDetector, AnyToEthFeeProxyPaymentDetector } from './any';
2626
import { NearConversionNativeTokenPaymentDetector, NearNativeTokenPaymentDetector } from './near';
27+
import { TronERC20FeeProxyPaymentDetector } from './tron';
2728
import { getPaymentNetworkExtension } from './utils';
2829
import { getTheGraphClient } from './thegraph';
2930
import { getDefaultProvider } from 'ethers';
@@ -55,6 +56,13 @@ const supportedPaymentNetwork: ISupportedPaymentNetworkByCurrency = {
5556
'near-testnet': {
5657
[PN_ID.ERC20_FEE_PROXY_CONTRACT]: ERC20FeeProxyPaymentDetector<CurrencyTypes.NearChainName>,
5758
},
59+
// TRON chains
60+
tron: {
61+
[PN_ID.ERC20_FEE_PROXY_CONTRACT]: TronERC20FeeProxyPaymentDetector,
62+
},
63+
nile: {
64+
[PN_ID.ERC20_FEE_PROXY_CONTRACT]: TronERC20FeeProxyPaymentDetector,
65+
},
5866

5967
'*': {
6068
[PN_ID.ERC20_ADDRESS_BASED]: ERC20AddressBasedPaymentDetector,

packages/payment-detection/src/thegraph/client.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
22
import { CurrencyTypes } from '@requestnetwork/types';
3-
import { NearChains } from '@requestnetwork/currency';
3+
import { NearChains, TronChains } from '@requestnetwork/currency';
44
import { GraphQLClient } from 'graphql-request';
55
import { Block_Height, getSdk, Maybe } from './generated/graphql';
66
import { getSdk as getNearSdk } from './generated/graphql-near';
7+
import { getSdk as getTronSdk } from './generated/graphql-tron';
78

89
const THE_GRAPH_STUDIO_URL =
910
'https://api.studio.thegraph.com/query/67444/request-payments-$NETWORK/version/latest';
@@ -20,6 +21,13 @@ const THE_GRAPH_URL_MANTLE =
2021
const THE_GRAPH_URL_CORE =
2122
'https://thegraph.coredao.org/subgraphs/name/requestnetwork/request-payments-core';
2223

24+
// TRON Substreams-powered subgraph URLs
25+
const THE_GRAPH_URL_TRON =
26+
'https://api.studio.thegraph.com/query/67444/request-payments-tron/version/latest';
27+
28+
const THE_GRAPH_URL_TRON_NILE =
29+
'https://api.studio.thegraph.com/query/67444/request-payments-tron-nile/version/latest';
30+
2331
const THE_GRAPH_EXPLORER_SUBGRAPH_ID: Partial<Record<CurrencyTypes.ChainName, string>> = {
2432
['arbitrum-one']: '3MtDdHbzvBVNBpzUTYXGuDDLgTd1b8bPYwoH1Hdssgp9',
2533
avalanche: 'A27V4PeZdKHeyuBkehdBJN8cxNtzVpXvYoqkjHUHRCFp',
@@ -47,11 +55,13 @@ const THE_GRAPH_EXPLORER_SUBGRAPH_ID: Partial<Record<CurrencyTypes.ChainName, st
4755
/**
4856
* A GraphQL client to query Request's subgraph.
4957
*
50-
* @type TGraphClientVariant: null if no variant, 'near' if native token payments detection on Near
58+
* @type TGraphClientVariant: null if no variant, 'near' if native token payments detection on Near, 'tron' for TRON
5159
*/
5260
export type TheGraphClient<TChain extends CurrencyTypes.VMChainName = CurrencyTypes.EvmChainName> =
5361
(TChain extends CurrencyTypes.NearChainName
5462
? ReturnType<typeof getNearSdk>
63+
: TChain extends CurrencyTypes.TronChainName
64+
? ReturnType<typeof getTronSdk>
5565
: ReturnType<typeof getSdk>) & {
5666
options?: TheGraphQueryOptions;
5767
};
@@ -104,6 +114,9 @@ export const getTheGraphClient = (
104114
) => {
105115
const url = getTheGraphClientUrl(network, options);
106116
if (!url) return;
117+
if (TronChains.isChainSupported(network)) {
118+
return getTheGraphTronClient(url, options);
119+
}
107120
return NearChains.isChainSupported(network)
108121
? getTheGraphNearClient(url, options)
109122
: getTheGraphEvmClient(url, options);
@@ -127,6 +140,15 @@ export const getTheGraphNearClient = (url: string, options?: TheGraphClientOptio
127140
return sdk;
128141
};
129142

143+
export const getTheGraphTronClient = (url: string, options?: TheGraphClientOptions) => {
144+
const [clientOptions, queryOptions] = extractClientOptions(url, options);
145+
const sdk: TheGraphClient<CurrencyTypes.TronChainName> = getTronSdk(
146+
new GraphQLClient(url, clientOptions),
147+
);
148+
sdk.options = queryOptions;
149+
return sdk;
150+
};
151+
130152
export const getTheGraphClientUrl = (
131153
network: CurrencyTypes.ChainName,
132154
options?: TheGraphClientOptions,
@@ -155,6 +177,10 @@ export const getTheGraphClientUrl = (
155177
return THE_GRAPH_URL_MANTLE_TESTNET;
156178
case chain === 'core':
157179
return THE_GRAPH_URL_CORE;
180+
case chain === 'tron':
181+
return THE_GRAPH_URL_TRON;
182+
case chain === 'nile':
183+
return THE_GRAPH_URL_TRON_NILE;
158184
default:
159185
return shouldUseTheGraphExplorer ? theGraphExplorerUrl : theGraphStudioUrl;
160186
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Getting TRC20 payments from the ERC20FeeProxy contract on TRON
2+
query GetTronPayments(
3+
$reference: Bytes!
4+
$to: String!
5+
$tokenAddress: String!
6+
$contractAddress: String!
7+
) {
8+
payments(
9+
where: {
10+
reference: $reference
11+
to: $to
12+
tokenAddress: $tokenAddress
13+
contractAddress: $contractAddress
14+
}
15+
orderBy: timestamp
16+
orderDirection: asc
17+
) {
18+
amount
19+
block
20+
txHash
21+
feeAmount
22+
feeAddress
23+
from
24+
timestamp
25+
tokenAddress
26+
}
27+
}
28+
29+
# Getting TRC20 payments without token address filter (for any token)
30+
query GetTronPaymentsAnyToken($reference: Bytes!, $to: String!, $contractAddress: String!) {
31+
payments(
32+
where: { reference: $reference, to: $to, contractAddress: $contractAddress }
33+
orderBy: timestamp
34+
orderDirection: asc
35+
) {
36+
amount
37+
block
38+
txHash
39+
feeAmount
40+
feeAddress
41+
from
42+
timestamp
43+
tokenAddress
44+
}
45+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Using local schema until the subgraph is deployed to The Graph Studio
2+
schema: ../../../../../substreams-tron/schema.graphql
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { TronERC20FeeProxyPaymentDetector } from './tron-fee-proxy-detector';
2+
export { TronInfoRetriever } from './tron-info-retriever';
3+
export type { TronPaymentEvent } from './tron-info-retriever';
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { erc20FeeProxyArtifact } from '@requestnetwork/smart-contracts';
2+
import {
3+
CurrencyTypes,
4+
ExtensionTypes,
5+
PaymentTypes,
6+
RequestLogicTypes,
7+
} from '@requestnetwork/types';
8+
import { TronChains, isSameChain } from '@requestnetwork/currency';
9+
10+
import { ERC20FeeProxyPaymentDetectorBase } from '../erc20/fee-proxy-contract';
11+
import { NetworkNotSupported } from '../balance-error';
12+
import { ReferenceBasedDetectorOptions, TGetSubGraphClient } from '../types';
13+
import { TronInfoRetriever, TronPaymentEvent } from './tron-info-retriever';
14+
import { TheGraphClient } from '../thegraph';
15+
16+
/**
17+
* Handle payment networks with ERC20 fee proxy contract extension on TRON chains
18+
*/
19+
export class TronERC20FeeProxyPaymentDetector extends ERC20FeeProxyPaymentDetectorBase<
20+
ExtensionTypes.PnFeeReferenceBased.IFeeReferenceBased,
21+
TronPaymentEvent
22+
> {
23+
private readonly getSubgraphClient: TGetSubGraphClient<CurrencyTypes.TronChainName>;
24+
protected readonly network: CurrencyTypes.TronChainName | undefined;
25+
26+
constructor({
27+
advancedLogic,
28+
currencyManager,
29+
getSubgraphClient,
30+
network,
31+
}: ReferenceBasedDetectorOptions & {
32+
network?: CurrencyTypes.TronChainName;
33+
getSubgraphClient: TGetSubGraphClient<CurrencyTypes.TronChainName>;
34+
}) {
35+
super(
36+
ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT,
37+
advancedLogic.getFeeProxyContractErc20ForNetwork(network) ??
38+
advancedLogic.extensions.feeProxyContractErc20,
39+
currencyManager,
40+
);
41+
this.getSubgraphClient = getSubgraphClient;
42+
this.network = network;
43+
}
44+
45+
/**
46+
* Gets the deployment information for the ERC20FeeProxy contract on TRON
47+
*/
48+
public static getDeploymentInformation(
49+
network: CurrencyTypes.TronChainName,
50+
_paymentNetworkVersion?: string,
51+
): { address: string; creationBlockNumber: number } {
52+
void _paymentNetworkVersion; // Parameter kept for API compatibility
53+
// For TRON, we use the 'tron' version of the artifact
54+
const address = erc20FeeProxyArtifact.getAddress(network, 'tron');
55+
const creationBlockNumber =
56+
network === 'tron'
57+
? 79216121 // TRON mainnet
58+
: 63208782; // Nile testnet
59+
60+
return { address, creationBlockNumber };
61+
}
62+
63+
/**
64+
* Extracts the payment events of a request on TRON
65+
*/
66+
protected async extractEvents(
67+
eventName: PaymentTypes.EVENTS_NAMES,
68+
toAddress: string | undefined,
69+
paymentReference: string,
70+
requestCurrency: RequestLogicTypes.ICurrency,
71+
paymentChain: CurrencyTypes.TronChainName,
72+
_paymentNetwork: ExtensionTypes.IState,
73+
): Promise<PaymentTypes.AllNetworkEvents<TronPaymentEvent>> {
74+
void _paymentNetwork; // Parameter required by parent class signature
75+
// Validate that the payment chain is a supported TRON chain
76+
if (!TronChains.isChainSupported(paymentChain)) {
77+
throw new NetworkNotSupported(
78+
`Unsupported TRON network '${paymentChain}' for TRON payment detector`,
79+
);
80+
}
81+
82+
if (this.network && !isSameChain(paymentChain, this.network)) {
83+
throw new NetworkNotSupported(
84+
`Unsupported network '${paymentChain}' for payment detector instantiated with '${this.network}'`,
85+
);
86+
}
87+
88+
if (!toAddress) {
89+
return {
90+
paymentEvents: [],
91+
};
92+
}
93+
94+
const { address: proxyContractAddress } =
95+
TronERC20FeeProxyPaymentDetector.getDeploymentInformation(paymentChain);
96+
97+
const subgraphClient = this.getSubgraphClient(
98+
paymentChain,
99+
) as TheGraphClient<CurrencyTypes.TronChainName>;
100+
101+
if (!subgraphClient) {
102+
throw new Error(
103+
`Could not get a TheGraph-based info retriever for TRON chain ${paymentChain}. ` +
104+
`Ensure the TRON Substreams-powered subgraph is deployed and accessible.`,
105+
);
106+
}
107+
108+
const infoRetriever = new TronInfoRetriever(subgraphClient);
109+
110+
return infoRetriever.getTransferEvents({
111+
eventName,
112+
paymentReference,
113+
toAddress,
114+
contractAddress: proxyContractAddress,
115+
paymentChain,
116+
acceptedTokens: [requestCurrency.value],
117+
});
118+
}
119+
}

0 commit comments

Comments
 (0)