Skip to content

feat: tron batch payments contracts and tests#1724

Open
LeoSlrRf wants to merge 6 commits into
masterfrom
feat/batch-tron
Open

feat: tron batch payments contracts and tests#1724
LeoSlrRf wants to merge 6 commits into
masterfrom
feat/batch-tron

Conversation

@LeoSlrRf
Copy link
Copy Markdown
Contributor

@LeoSlrRf LeoSlrRf commented May 20, 2026

Description of the changes

Adds Tron-specific batch payment contracts and test infrastructure to the smart-contracts package.

This PR introduces the existing EVM BatchPayment contract on Tron, and introduces ERC20BatchPayments, a version for better performance and increased security on Tron

Two Tron-targeted test suites are added:

  • ERC20BatchPayments.test.js covers ERC20BatchPayments, including happy-path single-token and multi-token payments, zero-amount payments, zero-fee payments, BadTRC20 token handling, and error cases such as insufficient funds, missing allowance, and mismatched input arrays.
  • BatchPayments.test.js covers the main BatchPayments contract on Tron, adding batch fee computation and verification, dynamic batch fee updates, admin access control for proxy and fee setters, and a check that batchEthPaymentsWithReference reverts when no EthFeeProxy is configured.

A shared helpers.js module provides utilities used by both test files, including token deployment, approval helpers, balance diffing, batch fee computation, and revert/no-balance-change assertion helpers.

The root package.json workspace configuration is updated to prevent hoisting of @openzeppelin dependencies for the smart-contracts package, ensuring the Tron build resolves its own copy of those contracts.

Contracts Scope

BatchPayments.sol

Same contract as the one on EVM.
We don't have the EthFeeProxy on Tron, so the associated address is defined as the zero address. Calls to the associated method fail as expected.

ERC20BatchPayment.sol

This is the same contract as above, with existing BatchPayments without:

  • The dedicated batch fees
  • The EthFeeProxy methods
  • The ownership

Security Consideration

The BatchPayment contract provides methods for managing the underlying proxies and fees. And exposes an unused method related to native payment.

On the other hand, the ERC20BatchPayment contract benefits from not exposing unused methods, thereby reducing the attack surface. Additionally, since we don't need an owner to manage the fees, the ERC20FeeProxy contract is now hardcoded, which allows us to remove the contract owner.

Gas Consideration

ERC20BatchPayment has a lower gas footprint because it does not include batch-fee-related logic.

Initially, I created a third version in which the feeAmounts and feeRecipients were removed from the interface and were always set to zero within the contract. This didn't reduce the gas cost compared to a call where we would pass the zero values directly.

Copy link
Copy Markdown
Contributor Author

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

@LeoSlrRf LeoSlrRf marked this pull request as ready for review May 20, 2026 11:22
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 20, 2026

Greptile Summary

This PR ports the existing EVM BatchPayments contract to Tron and introduces a new simplified ERC20BatchPayments contract (no batch fee, no ETH proxy) that reduces attack surface and gas cost. Two Tron test suites are added alongside a shared helpers.js module, and the root package.json workspace config is updated to prevent hoisting of @openzeppelin dependencies for smart-contracts.

  • BatchPayments.sol is a direct Tron port of the EVM contract; the ETH proxy is intentionally set to the zero address and the receive() guard prevents accidental TRX deposits.
  • ERC20BatchPayments.sol is a slimmer Tron-only variant: no batch fee, no ownership, and a clean _transferToContractAndApproveProxy helper that consistently pre-checks allowance and balance before pulling tokens into the contract.
  • The BatchPayments.test.js suite is thorough and consistently uses waitForConfirmation after each batch call, while ERC20BatchPayments.test.js omits it in several happy-path tests.

Confidence Score: 5/5

The contracts are safe to merge; both findings are test quality and error-message clarity issues, not fund-loss or logic bugs.

Both contracts behave correctly under all tested scenarios. The allowance check omission leads to a less helpful revert message rather than wrong behaviour, and the missing confirmation waits affect test reliability but not contract correctness.

packages/smart-contracts/test/tron/ERC20BatchPayments.test.js and packages/smart-contracts/tron/contracts/BatchPayments.sol

Important Files Changed

Filename Overview
packages/smart-contracts/tron/contracts/BatchPayments.sol EVM BatchPayments contract ported to Tron; batchERC20PaymentsWithReference allowance check excludes the batch fee unlike the multi-token sibling, producing a misleading revert message
packages/smart-contracts/tron/contracts/ERC20BatchPayments.sol New simplified Tron-only batch ERC20 contract; no batch fee, no ETH proxy, uses SafeERC20 library; logic is correct and consistent across single- and multi-token paths
packages/smart-contracts/test/tron/ERC20BatchPayments.test.js Test suite for ERC20BatchPayments; several happy-path tests assert balances immediately after batch calls without awaiting Tron block confirmation, making them potentially flaky on live networks
packages/smart-contracts/test/tron/BatchPayments.test.js Comprehensive test suite for BatchPayments on Tron; consistently uses waitForConfirmation, covers happy paths, error cases, batch fee updates, and admin access control
packages/smart-contracts/test/tron/helpers.js Shared test utilities; all helpers look correct and the previously-undeclared threw variable issue has been resolved
packages/smart-contracts/tron/contracts/lib/SafeERC20.sol Custom SafeERC20 library for Tron handling non-standard tokens via low-level calls
packages/smart-contracts/tron/contracts/interfaces/ERC20FeeProxy.sol Minimal IERC20FeeProxy interface; straightforward and correct
packages/smart-contracts/tron/contracts/interfaces/EthereumFeeProxy.sol IEthereumFeeProxy interface; intentionally set to zero address on Tron
package.json Root workspaces nohoist config added to prevent @OpenZeppelin hoisting out of smart-contracts

Reviews (5): Last reviewed commit: "chore: remove contract ownership" | Re-trigger Greptile

Comment thread packages/smart-contracts/test/tron/ERC20BatchPayments.test.js Outdated
Comment thread packages/smart-contracts/test/tron/ERC20BatchPayments.test.js Outdated
Comment thread packages/smart-contracts/test/tron/ERC20BatchPayments.test.js Outdated
Comment on lines +84 to +93
const expectRevertOrNoBalanceChange = async (fn, getBalances) => {
const before = await getBalances();
try {
await fn();
} catch (_error) {}
await waitForConfirmation(2000);
const after = await getBalances();
const unchanged = before.every((value, index) => value === after[index]);
return { unchanged };
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Silent error swallowing can mask real bugs

expectRevertOrNoBalanceChange catches errors without asserting that a revert actually occurred. If the transaction unexpectedly succeeds and the balance check happens to track the wrong account, a funds leak would be silently accepted. The expectNonOwnerReverts helper in the same file correctly asserts threw === true; applying the same pattern here would make failure detection explicit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

On Tron, when a revert occurs the transaction call does not fail.

See this transaction for example.

Comment thread packages/smart-contracts/test/tron/helpers.js Outdated
Copy link
Copy Markdown
Member

MantisClone commented May 21, 2026

@LeoSlrRf is this still true? If not, please update the PR description.

> Currently, both contracts provide methods for managing the underlying proxies.

@LeoSlrRf
Copy link
Copy Markdown
Contributor Author

@MantisClone It wasn't the case. I just pushed the ownership removal and updated the PR description

uint256[] calldata _amounts,
bytes[] calldata _paymentReferences,
uint256[] calldata _feeAmounts,
address _feeAddress
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I noticed that all batch payment contracts enforce a single feeAddress for all payments. I don't see why that would be the case.

Given that we won't use the fee parameters, I'm wondering if it's worth updating to an array for Tron only.

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.

2 participants