Skip to content

Commit 5d88414

Browse files
committed
fix TRON hex address
1 parent 0324fff commit 5d88414

40 files changed

Lines changed: 57064 additions & 6 deletions
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
name: Tron Smart Contracts Tests
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- master
7+
paths:
8+
- 'packages/smart-contracts/tron/**'
9+
- 'packages/smart-contracts/tronbox-config.js'
10+
- '.github/workflows/tron-smart-contracts.yml'
11+
push:
12+
branches:
13+
- master
14+
paths:
15+
- 'packages/smart-contracts/tron/**'
16+
- 'packages/smart-contracts/tronbox-config.js'
17+
workflow_dispatch:
18+
19+
jobs:
20+
tron-compile-check:
21+
name: Tron Contract Compilation Check
22+
runs-on: ubuntu-latest
23+
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@v4
27+
28+
- name: Setup Node.js
29+
uses: actions/setup-node@v4
30+
with:
31+
node-version: '22'
32+
cache: 'yarn'
33+
34+
- name: Install TronBox globally
35+
run: npm install -g tronbox
36+
37+
- name: Install dependencies
38+
run: yarn install --frozen-lockfile
39+
40+
- name: Compile Tron contracts
41+
working-directory: packages/smart-contracts
42+
run: yarn tron:compile
43+
44+
- name: Verify build artifacts exist
45+
working-directory: packages/smart-contracts
46+
run: |
47+
echo "Checking build artifacts..."
48+
ls -la tron-build/
49+
50+
# Verify key contracts were compiled
51+
if [ ! -f "tron-build/ERC20FeeProxy.json" ]; then
52+
echo "ERROR: ERC20FeeProxy.json not found!"
53+
exit 1
54+
fi
55+
56+
if [ ! -f "tron-build/TestTRC20.json" ]; then
57+
echo "ERROR: TestTRC20.json not found!"
58+
exit 1
59+
fi
60+
61+
echo "✅ All required artifacts present"
62+
63+
- name: Verify contract ABI structure
64+
working-directory: packages/smart-contracts
65+
run: |
66+
echo "Verifying ERC20FeeProxy ABI..."
67+
# Check that the compiled contract has the expected function
68+
if ! grep -q "transferFromWithReferenceAndFee" tron-build/ERC20FeeProxy.json; then
69+
echo "ERROR: ERC20FeeProxy missing transferFromWithReferenceAndFee function!"
70+
exit 1
71+
fi
72+
echo "✅ Contract ABI structure verified"
73+
74+
# Note: Integration tests are skipped in CI due to QEMU ARM64 emulation being too slow
75+
# for Tron blockchain confirmations. Run integration tests locally with:
76+
# docker run -d --name tron-tre -p 9090:9090 tronbox/tre
77+
# yarn tron:test
78+
# Or run against Nile testnet:
79+
# TRON_PRIVATE_KEY=your_key yarn tron:test:nile
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const chainId = 'nile';
2+
3+
// Nile is Tron's test network
4+
export const testnet = true;
5+
6+
// Test tokens on Nile testnet
7+
// Note: These are testnet token addresses, not mainnet
8+
export const currencies = {
9+
// Add testnet token addresses as needed
10+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,20 @@
11
export const chainId = 'tron';
2+
3+
// Tron mainnet configuration
4+
export const testnet = false;
5+
6+
// Common TRC20 tokens on Tron
7+
export const currencies = {
8+
// USDT-TRC20 - the most widely used stablecoin on Tron
9+
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t: {
10+
name: 'Tether USD',
11+
symbol: 'USDT',
12+
decimals: 6,
13+
},
14+
// USDC on Tron
15+
TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8: {
16+
name: 'USD Coin',
17+
symbol: 'USDC',
18+
decimals: 6,
19+
},
20+
};

packages/currency/src/chains/declarative/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CurrencyTypes } from '@requestnetwork/types';
22

33
import * as TronDefinition from './data/tron';
4+
import * as NileDefinition from './data/nile';
45
import * as SolanaDefinition from './data/solana';
56
import * as StarknetDefinition from './data/starknet';
67
import * as TonDefinition from './data/ton';
@@ -11,6 +12,7 @@ export type DeclarativeChain = CurrencyTypes.Chain;
1112

1213
export const chains: Record<CurrencyTypes.DeclarativeChainName, DeclarativeChain> = {
1314
tron: TronDefinition,
15+
nile: NileDefinition,
1416
solana: SolanaDefinition,
1517
starknet: StarknetDefinition,
1618
ton: TonDefinition,
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import BtcChains from './btc/BtcChains';
22
import EvmChains from './evm/EvmChains';
33
import NearChains from './near/NearChains';
4+
import TronChains from './tron/TronChains';
45
import DeclarativeChains from './declarative/DeclarativeChains';
56
import { isSameChain } from './utils';
67

7-
export { BtcChains, EvmChains, NearChains, DeclarativeChains, isSameChain };
8+
export { BtcChains, EvmChains, NearChains, TronChains, DeclarativeChains, isSameChain };
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { ChainsAbstract } from '../ChainsAbstract';
2+
import { CurrencyTypes, RequestLogicTypes } from '@requestnetwork/types';
3+
import { TronChain, chains } from './index';
4+
5+
class TronChains extends ChainsAbstract<CurrencyTypes.TronChainName, TronChain, string> {}
6+
export default new TronChains(chains, RequestLogicTypes.CURRENCY.ETH);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { CurrencyTypes } from '@requestnetwork/types';
2+
3+
import * as TronDefinition from '../declarative/data/tron';
4+
import * as NileDefinition from '../declarative/data/nile';
5+
6+
export type TronChain = CurrencyTypes.Chain;
7+
8+
export const chains: Record<CurrencyTypes.TronChainName, TronChain> = {
9+
tron: TronDefinition,
10+
nile: NileDefinition,
11+
};

packages/payment-processor/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ export * from './payment/encoder-approval';
2828
export * as Escrow from './payment/erc20-escrow-payment';
2929
export * from './payment/prepared-transaction';
3030
export * from './payment/utils-near';
31+
export * from './payment/utils-tron';
32+
export * from './payment/tron-fee-proxy';
3133
export * from './payment/single-request-forwarder';
3234
export * from './payment/erc20-recurring-payment-proxy';
3335
export * from './payment/erc20-commerce-escrow-wrapper';
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import { BigNumber, BigNumberish } from 'ethers';
2+
import { ClientTypes, ExtensionTypes } from '@requestnetwork/types';
3+
import { TronChains } from '@requestnetwork/currency';
4+
5+
import { getAmountToPay, getRequestPaymentValues, validateRequest } from './utils';
6+
import {
7+
TronWeb,
8+
ITronTransactionCallback,
9+
processTronFeeProxyPayment,
10+
approveTrc20,
11+
getTronAllowance,
12+
isTronAccountSolvent,
13+
isValidTronAddress,
14+
getERC20FeeProxyAddress,
15+
} from './utils-tron';
16+
import { validatePaymentReference } from '../utils/validation';
17+
18+
/**
19+
* Checks if the TronWeb instance has sufficient allowance for the payment
20+
*/
21+
export async function hasSufficientTronAllowance(
22+
request: ClientTypes.IRequestData,
23+
tronWeb: TronWeb,
24+
amount?: BigNumberish,
25+
): Promise<boolean> {
26+
const network = request.currencyInfo.network;
27+
if (!network || !TronChains.isChainSupported(network)) {
28+
throw new Error('Request currency network is not a supported Tron network');
29+
}
30+
TronChains.assertChainSupported(network);
31+
32+
const tokenAddress = request.currencyInfo.value;
33+
const { feeAmount } = getRequestPaymentValues(request);
34+
const amountToPay = getAmountToPay(request, amount);
35+
const totalAmount = BigNumber.from(amountToPay).add(feeAmount || 0);
36+
37+
const allowance = await getTronAllowance(tronWeb, tokenAddress, network);
38+
return allowance.gte(totalAmount);
39+
}
40+
41+
/**
42+
* Checks if the payer has sufficient TRC20 token balance
43+
*/
44+
export async function hasSufficientTronBalance(
45+
request: ClientTypes.IRequestData,
46+
tronWeb: TronWeb,
47+
amount?: BigNumberish,
48+
): Promise<boolean> {
49+
const tokenAddress = request.currencyInfo.value;
50+
const { feeAmount } = getRequestPaymentValues(request);
51+
const amountToPay = getAmountToPay(request, amount);
52+
const totalAmount = BigNumber.from(amountToPay).add(feeAmount || 0);
53+
54+
return isTronAccountSolvent(tronWeb, tokenAddress, totalAmount);
55+
}
56+
57+
/**
58+
* Approves the ERC20FeeProxy contract to spend TRC20 tokens for a request payment
59+
*/
60+
export async function approveTronFeeProxyRequest(
61+
request: ClientTypes.IRequestData,
62+
tronWeb: TronWeb,
63+
amount?: BigNumberish,
64+
callback?: ITronTransactionCallback,
65+
): Promise<string> {
66+
const network = request.currencyInfo.network;
67+
if (!network || !TronChains.isChainSupported(network)) {
68+
throw new Error('Request currency network is not a supported Tron network');
69+
}
70+
TronChains.assertChainSupported(network);
71+
72+
validateRequest(request, ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT);
73+
74+
const tokenAddress = request.currencyInfo.value;
75+
const { feeAmount } = getRequestPaymentValues(request);
76+
const amountToPay = getAmountToPay(request, amount);
77+
const totalAmount = BigNumber.from(amountToPay).add(feeAmount || 0);
78+
79+
return approveTrc20(tronWeb, tokenAddress, network, totalAmount, callback);
80+
}
81+
82+
/**
83+
* Processes a TRC20 fee proxy payment for a Request.
84+
*
85+
* @param request The request to pay
86+
* @param tronWeb The TronWeb instance connected to the payer's wallet
87+
* @param amount Optionally, the amount to pay. Defaults to remaining amount of the request.
88+
* @param feeAmount Optionally, the fee amount to pay. Defaults to the fee amount from the request.
89+
* @param callback Optional callbacks for transaction events
90+
* @returns The transaction hash
91+
*/
92+
export async function payTronFeeProxyRequest(
93+
request: ClientTypes.IRequestData,
94+
tronWeb: TronWeb,
95+
amount?: BigNumberish,
96+
feeAmount?: BigNumberish,
97+
callback?: ITronTransactionCallback,
98+
): Promise<string> {
99+
const network = request.currencyInfo.network;
100+
if (!network || !TronChains.isChainSupported(network)) {
101+
throw new Error('Request currency network is not a supported Tron network');
102+
}
103+
TronChains.assertChainSupported(network);
104+
105+
validateRequest(request, ExtensionTypes.PAYMENT_NETWORK_ID.ERC20_FEE_PROXY_CONTRACT);
106+
107+
const {
108+
paymentReference,
109+
paymentAddress,
110+
feeAddress,
111+
feeAmount: requestFeeAmount,
112+
} = getRequestPaymentValues(request);
113+
114+
validatePaymentReference(paymentReference);
115+
116+
if (!isValidTronAddress(paymentAddress)) {
117+
throw new Error(`Invalid Tron payment address: ${paymentAddress}`);
118+
}
119+
120+
const tokenAddress = request.currencyInfo.value;
121+
const amountToPay = getAmountToPay(request, amount);
122+
const feeToPay = feeAmount ?? requestFeeAmount ?? '0';
123+
124+
// Check allowance
125+
const totalAmount = BigNumber.from(amountToPay).add(feeToPay);
126+
const allowance = await getTronAllowance(tronWeb, tokenAddress, network);
127+
128+
if (allowance.lt(totalAmount)) {
129+
throw new Error(
130+
`Insufficient TRC20 allowance. Required: ${totalAmount.toString()}, Available: ${allowance.toString()}. ` +
131+
`Please call approveTronFeeProxyRequest first.`,
132+
);
133+
}
134+
135+
// Check balance
136+
const hasSufficientBalance = await isTronAccountSolvent(tronWeb, tokenAddress, totalAmount);
137+
if (!hasSufficientBalance) {
138+
throw new Error('Insufficient TRC20 token balance for payment');
139+
}
140+
141+
return processTronFeeProxyPayment(
142+
tronWeb,
143+
network,
144+
tokenAddress,
145+
paymentAddress,
146+
amountToPay,
147+
paymentReference,
148+
feeToPay,
149+
feeAddress || tronWeb.defaultAddress.base58,
150+
callback,
151+
);
152+
}
153+
154+
/**
155+
* Gets information needed to pay a Tron request
156+
*/
157+
export function getTronPaymentInfo(
158+
request: ClientTypes.IRequestData,
159+
amount?: BigNumberish,
160+
): {
161+
proxyAddress: string;
162+
tokenAddress: string;
163+
paymentAddress: string;
164+
amount: string;
165+
paymentReference: string;
166+
feeAmount: string;
167+
feeAddress: string;
168+
} {
169+
const network = request.currencyInfo.network;
170+
if (!network || !TronChains.isChainSupported(network)) {
171+
throw new Error('Request currency network is not a supported Tron network');
172+
}
173+
TronChains.assertChainSupported(network);
174+
175+
const { paymentReference, paymentAddress, feeAddress, feeAmount } =
176+
getRequestPaymentValues(request);
177+
178+
const tokenAddress = request.currencyInfo.value;
179+
const amountToPay = getAmountToPay(request, amount);
180+
const proxyAddress = getERC20FeeProxyAddress(network);
181+
182+
return {
183+
proxyAddress,
184+
tokenAddress,
185+
paymentAddress,
186+
amount: amountToPay.toString(),
187+
paymentReference: paymentReference ?? '',
188+
feeAmount: (feeAmount || '0').toString(),
189+
feeAddress: feeAddress ?? '',
190+
};
191+
}

0 commit comments

Comments
 (0)