- September 17, 2026
OpenZeppelin Security
Summary
Type: LibraryTimeline: 2026-07-02 → 2026-07-22Languages: Solidity
FindingsTotal issues: 11 (11 resolved)Critical: 0 (0 resolved) · High: 2 (2 resolved) · Medium: 1 (1 resolved) · Low: 3 (3 resolved)
Notes & Additional Information5 notes raised (5 resolved)
Client Reported Issues0 reported issues (0 resolved)
Table of Contents
- Table of Contents
- Summary
- Scope
- System Overview
- Security Model and Trust Assumptions Privileged Roles Trust Assumptions Additional Considerations
- Privileged Roles
- Trust Assumptions
- Additional Considerations
- High Severity TRC4626 And VestingWallet Cannot Release USDT Due To False-On-Success Transfers Audited Contracts Inherit Issues From Upstream OpenZeppelin Contracts
- TRC4626 And VestingWallet Cannot Release USDT Due To False-On-Success Transfers
- Audited Contracts Inherit Issues From Upstream OpenZeppelin Contracts
- Medium Severity InputSettlerEscrowTron Uses Ethereum Permit2 Address, Breaking openFor on TRON
- InputSettlerEscrowTron Uses Ethereum Permit2 Address, Breaking openFor on TRON
- Low Severity TRC20FlashMint Rejects TIP-3156-Compliant Flash Borrowers Documentation Cites Ethereum ERC/EIP Names for Standards Renamed to TRON Identifiers Missing Documentation for Decoding 21-Byte TRON Addresses
- TRC20FlashMint Rejects TIP-3156-Compliant Flash Borrowers
- Documentation Cites Ethereum ERC/EIP Names for Standards Renamed to TRON Identifiers
- Missing Documentation for Decoding 21-Byte TRON Addresses
- Notes & Additional Information TRON Block Time Compresses Block-Denominated Governance Windows Roughly Fourfold Stale Warnings And Cautions In The Codebase Rename contracts from OpenZeppelin Contracts to Tron Contracts Governance Executors Do Not Support Forwarding or Recovery of TRC-10 Tokens README and Documentation Reference ERC/EIP-Named Artifacts That Only Exist Under TRC Names
- TRON Block Time Compresses Block-Denominated Governance Windows Roughly Fourfold
- Stale Warnings And Cautions In The Codebase
- Rename contracts from OpenZeppelin Contracts to Tron Contracts
- Governance Executors Do Not Support Forwarding or Recovery of TRC-10 Tokens
- README and Documentation Reference ERC/EIP-Named Artifacts That Only Exist Under TRC Names
- Conclusion
- Appendix Issue Classification
- Issue Classification
Scope
OpenZeppelin audited three repositories. The first two make up the TRON port of the OpenZeppelin Contracts library, and the third is the TRON adaptation of the Open Intents Framework settlement contracts:
- the OpenZeppelin/tron-contracts repository at commit 06d69bc,
- the OpenZeppelin/tron-contracts-upgradeable repository at commit f66f953
- the openintentsframework/oif-contracts repository at commit 39df288.
For the two OpenZeppelin libraries, in scope were all Solidity sources under the contracts/ directory, excluding the mock and test helpers under contracts/mocks/. The two libraries share the same module layout; the upgradeable repository provides the upgradeable variants of the same standards and therefore omits the proxy and interface modules that only the non-upgradeable library ships. For the Open Intents Framework repository, in scope were the TRON-specific input-settlement contracts. The complete in-scope file lists are given below.
The in-scope files of tron-contracts (216 files) were the following:
The in-scope files of tron-contracts-upgradeable (84 files) were the following:
The in-scope files of oif-contracts (TRON settlement adaptation) were the following:
Update: The fixes for the findings highlighted in this report have all been merged at commit d260982 for tron-contracts and at commit 8e38b9b for oif-contracts.
System Overview
The two repositories in scope are a port of OpenZeppelin Contracts v5.6.1 to the TRON Virtual Machine (TVM). They are general-purpose libraries of reusable building blocks: the fungible (TRC-20 / ERC-20), non-fungible (TRC-721 / ERC-721), multi-token (TRC-1155 / ERC-1155), and minimal multi-token (ERC-6909) standards and their extensions, access control, governance, cryptography and signature utilities, proxies, meta-transactions (TRC-2771 / ERC-2771), crosschain bridges, and finance helpers, rather than a deployed protocol. Downstream projects inherit and compose these contracts, so the security-relevant surface is the correctness of each component together with the points at which TVM execution differs from the EVM.
The tron-contracts repository is the standard library. The tron-contracts-upgradeable repository is the upgradeable variant of the same sources, generated for use behind proxies: its contracts use the initializer pattern in place of constructors and store state in namespaced storage locations (TIP-7201 / ERC-7201) to remain upgrade-safe. Both libraries rename the standards that TRON publishes under its own TIP identifiers (for example ERC20 to TRC20, EIP712 to TIP712), and dual-cite the TRON (TIP / TRC) and Ethereum (EIP / ERC) specifications where both exist. Several standards map to a ratified TIP, including the permit extension (TIP-2612 / ERC-2612), the payable token (TIP-1363 / ERC-1363), the tokenized vault (TIP-4626 / ERC-4626), flash loans (TIP-3156 / ERC-3156), interface detection (TIP-165 / ERC-165), contract signature validation (TIP-1271 / ERC-1271), proxy storage slots (TIP-1967 / ERC-1967), and the minimal proxy (TIP-1167 / ERC-1167). Others have no TIP yet, such as ERC-6909, the ERC-7913 signature verifiers, and the ERC-7786 and ERC-7802 crosschain interfaces.
The port adapts the parts of the library where the TVM behaves differently from the EVM. The most security-relevant adaptations are the CREATE2 address derivation, which uses TRON's 0x41 prefix (TIP-26) instead of the EVM 0xff (EIP-1014); the typed-data (TIP-712 / EIP-712) domain separator, which binds to the four-byte chain identifier that TRON exposes through eth_chainId (TIP-474); the signed-message prefix in MessageHashUtils, which uses the TRON string (TIP-191 / TIP-104) rather than the ERC-191 one; the TRC-721 receiver callback, which TRON specifies with a distinct magic value; the secp256r1 (P256) verification (TIP-7951 / EIP-7951), which is performed in pure Solidity because no native precompile is active on the TVM; and a dedicated SafeTRC20 transfer helper for TRON USDT, whose transfer returns false even on a successful transfer. The EIP-7702 account-abstraction module present upstream is not included in either repository, since TRON provides multi-signature and permission features at the account level (TIP-16 / TIP-105).
The oif-contracts repository is the Open Intents Framework, a cross-chain intent settlement system in which a user locks input tokens in an escrow on a source chain so that a filler can satisfy the intent on a destination chain. In scope is its TRON adaptation, InputSettlerEscrowTron, which extends the framework's InputSettlerEscrow to run on the TVM and vendors the tron-contracts library as a dependency. The adaptation routes token payouts through SafeTRC20 so that settlement works with TRON USDT. It also relies on the Permit2 contract for the sponsored openFor collection path. Because TRON derives CREATE2 addresses with the 0x41 prefix, Permit2 is deployed at a different address on TRON than on Ethereum, so the framework's Permit2 address hook must resolve to the TRON deployment.
Security Model and Trust Assumptions
Because the repositories in scope are libraries rather than a deployed system, their security depends on how integrators configure and compose them and on the execution semantics of the target TRON network. This section records the trust assumptions relied upon during the review. Any finding whose impact depends on one of these assumptions being violated is out of scope unless an integrator or unprivileged actor could plausibly breach it.
Privileged Roles
- The libraries define no global privileged role of their own. They provide the access-control and governance primitives (Ownable, Ownable2Step, AccessControl, AccessManager, Governor, and TimelockController) that integrators configure for their own deployments. The trust placed in owners, administrators, proposers, and executors is determined entirely by that configuration.
The libraries define no global privileged role of their own.
- They provide the access-control and governance primitives (Ownable, Ownable2Step, AccessControl, AccessManager, Governor, and TimelockController) that integrators configure for their own deployments. The trust placed in owners, administrators, proposers, and executors is determined entirely by that configuration.
- The upgrade authority of an upgradeable deployment is fully trusted. For contracts deployed behind a proxy, the proxy administrator (for transparent and beacon proxies) or the account authorized by _authorizeUpgrade (for UUPS proxies) can replace the implementation and therefore the entire behavior of the contract.
The upgrade authority of an upgradeable deployment is fully trusted.
- For contracts deployed behind a proxy, the proxy administrator (for transparent and beacon proxies) or the account authorized by _authorizeUpgrade (for UUPS proxies) can replace the implementation and therefore the entire behavior of the contract.
Trust Assumptions
- The codebase is treated as a direct port of OpenZeppelin Contracts v5.6.1. No TRON-specific functionality, such as native voting or staking opcodes, is expected beyond the adaptations required to run on the TVM, and the majority of the difference from upstream is renaming rather than logic changes. Where behavior is unchanged from upstream, the corresponding upstream security properties are relied upon and are not re-derived.
The codebase is treated as a direct port of OpenZeppelin Contracts v5.6.1.
- No TRON-specific functionality, such as native voting or staking opcodes, is expected beyond the adaptations required to run on the TVM, and the majority of the difference from upstream is renaming rather than logic changes. Where behavior is unchanged from upstream, the corresponding upstream security properties are relied upon and are not re-derived.
- A TRC name is not taken to imply a ratified TRON standard. Where a renamed standard maps to an existing TIP, its implementation is expected to conform to that TIP, and any deviation is reported. Where no TIP exists, mirroring the Ethereum standard is considered acceptable and is not treated as a deviation. A TRC-named artifact is therefore not taken to correspond to a published TRON standard.
A TRC name is not taken to imply a ratified TRON standard.
- Where a renamed standard maps to an existing TIP, its implementation is expected to conform to that TIP, and any deviation is reported. Where no TIP exists, mirroring the Ethereum standard is considered acceptable and is not treated as a deviation. A TRC-named artifact is therefore not taken to correspond to a published TRON standard.
- TRC-10 tokens are treated as unsupported. The contracts implement no TRC-10 handling, and TRC-10 assets are not expected to be routed through them. The review still considers whether a TRC-10 balance held by an in-scope contract could be used to create an unfavorable scenario, for example through an arbitrary call executed by a governance or timelock contract.
TRC-10 tokens are treated as unsupported.
- The contracts implement no TRC-10 handling, and TRC-10 assets are not expected to be routed through them. The review still considers whether a TRC-10 balance held by an in-scope contract could be used to create an unfavorable scenario, for example through an arbitrary call executed by a governance or timelock contract.
- TRC-20 token decimals default to 18. The default decimals() for the TRC-20 implementation is 18 rather than TRON's native six, and is expected to be overridden where a different precision is required. Integrations pairing these tokens with six-decimal assets are expected to account for the difference.
TRC-20 token decimals default to 18.
- The default decimals() for the TRC-20 implementation is 18 rather than TRON's native six, and is expected to be overridden where a different precision is required. Integrations pairing these tokens with six-decimal assets are expected to account for the difference.
- Tokens that return false on a successful transfer are handled only by the dedicated helper. TRON USDT returns false from transfer even when the transfer succeeds. Only SafeTRC20.safeTransferUSDT, which verifies success via balance change rather than the return value, is relied upon for such tokens; flows that use the ordinary safeTransfer are not expected to be configured with them. Fee-on-transfer and rebasing tokens are treated as unsupported, as in the upstream library.
Tokens that return false on a successful transfer are handled only by the dedicated helper.
- TRON USDT returns false from transfer even when the transfer succeeds. Only SafeTRC20.safeTransferUSDT, which verifies success via balance change rather than the return value, is relied upon for such tokens; flows that use the ordinary safeTransfer are not expected to be configured with them. Fee-on-transfer and rebasing tokens are treated as unsupported, as in the upstream library.
- secp256r1 (P256) verification is performed in Solidity, without a precompile. No P256 precompile (TIP-7951 / EIP-7951) is active on the TVM, so verification is routed through Solidity while the function signatures are kept unchanged. The computation is more expensive than a precompile and is bounded by TRON's per-transaction execution limits.
secp256r1 (P256) verification is performed in Solidity, without a precompile.
- No P256 precompile (TIP-7951 / EIP-7951) is active on the TVM, so verification is routed through Solidity while the function signatures are kept unchanged. The computation is more expensive than a precompile and is bounded by TRON's per-transaction execution limits.
- The energy model differs from the EVM gas model. Gas-forwarding components, in particular the TRC2771Forwarder and the authority-based access paths, depend on gas-forwarding behavior that differs on TRON, where execution is metered as energy and the gas-limit and gas-price semantics are not identical to the EVM. Integrations relying on gas-limited sub-calls are relied upon to validate the behavior on TRON.
The energy model differs from the EVM gas model.
- Gas-forwarding components, in particular the TRC2771Forwarder and the authority-based access paths, depend on gas-forwarding behavior that differs on TRON, where execution is metered as energy and the gas-limit and gas-price semantics are not identical to the EVM. Integrations relying on gas-limited sub-calls are relied upon to validate the behavior on TRON.
- Crosschain message delivery is delegated to a trusted external gateway. The crosschain bridges delegate message delivery and replay protection to an external gateway, which is trusted to deliver each message at most once and to attribute the source chain and counterpart correctly.
Crosschain message delivery is delegated to a trusted external gateway.
- The crosschain bridges delegate message delivery and replay protection to an external gateway, which is trusted to deliver each message at most once and to attribute the source chain and counterpart correctly.
- The Open Intents Framework settlement relies on an external Permit2 deployment and off-chain actors. The TRON settlement contracts depend on an external Permit2 deployment for the sponsored input-collection path, expected to be configured with the correct TRON Permit2 address, and on off-chain fillers and solvers to satisfy intents. The on-chain guarantees concern only the custody and release of escrowed inputs.
The Open Intents Framework settlement relies on an external Permit2 deployment and off-chain actors.
- The TRON settlement contracts depend on an external Permit2 deployment for the sponsored input-collection path, expected to be configured with the correct TRON Permit2 address, and on off-chain fillers and solvers to satisfy intents. The on-chain guarantees concern only the custody and release of escrowed inputs.
Additional Considerations
- Deterministic deployment uses TRON's address derivation. The Create2, Clones, and relayer helpers derive addresses with TRON's 0x41 CREATE2 prefix (TIP-26) rather than the EVM 0xff (EIP-1014). Off-chain tooling and any counterfactual-address computation must use the same derivation to obtain correct addresses.
Deterministic deployment uses TRON's address derivation.
- The Create2, Clones, and relayer helpers derive addresses with TRON's 0x41 CREATE2 prefix (TIP-26) rather than the EVM 0xff (EIP-1014). Off-chain tooling and any counterfactual-address computation must use the same derivation to obtain correct addresses.
- The libraries assume the target network's TVM feature gates are active. Behavior that depends on a feature gate, such as transient storage (TIP-650 / EIP-1153) and the optimized chain-identifier return value (TIP-474), must be confirmed against the target network, and deployments must target an EVM version the network accepts.
The libraries assume the target network's TVM feature gates are active.
- Behavior that depends on a feature gate, such as transient storage (TIP-650 / EIP-1153) and the optimized chain-identifier return value (TIP-474), must be confirmed against the target network, and deployments must target an EVM version the network accepts.
High Severity
TRC4626 And VestingWallet Cannot Release USDT Due To False-On-Success Transfers
Both withdraw and redeem in TRC4626 pay the underlying asset out through a single internal function, _transferOut, which calls SafeTRC20.safeTransfer. That helper only accepts a transfer as successful when the token returns true or returns nothing at all.
On TRON, the USDT contract returns false from a transfer that has actually succeeded. safeTransfer reads that false return value, concludes the transfer failed, and reverts with SafeTRC20FailedOperation. Deposits are not affected, since _transferIn pulls assets in with safeTransferFrom and USDT does return true from transferFrom. The port already encountered this behavior and added safeTransferUSDT to deal with it, using it on the bridge release path in _onReceive, but the same change was never carried over to the vault.
This means that when the underlying is USDT, users can deposit and mint, but every withdraw and redeem reverts, so the shares can never be redeemed.
The same oversight is present in VestingWallet. Its only way to pay out a vested TRC-20 balance, release, also calls SafeTRC20.safeTransfer, so a wallet funded with USDT accepts the deposit but can never release it. The contract has no rescue function and is not upgradeable; transferring ownership does not help because the new owner reaches the same reverting call.
Consider paying the underlying out through safeTransferUSDT (or another transfer helper that confirms success from the resulting balance change) on the TRC4626 withdrawal path, matching what BridgeTRC20 already does. Additionally, consider applying the same fix to VestingWallet.
Update: Resolved in pull request #131. The team stated:
TRC4626._transferOut, VestingWallet.release and TRC20Wrapper.withdrawTo now pay out through SafeTRC20.safeTransferChecked, which confirms success from the caller's balance delta rather than the returned boolean. TRC20Wrapper.withdrawTo isn't listed in the finding but has the same defect — its underlying was trapped behind the wrapper. Fixed alongside. We made the checked transfer the default rather than a per-token exception, renaming safeTransferUSDT to safeTransferChecked: these contracts can't know at deployment time which token they hold, so gating on a configured USDT address would leave every other false-on-success token broken. The use of safeTransferUSDT was updated to safeTransferChecked on OIF at the PR: #195
TRC4626._transferOut, VestingWallet.release and TRC20Wrapper.withdrawTo now pay out through SafeTRC20.safeTransferChecked, which confirms success from the caller's balance delta rather than the returned boolean.
- TRC20Wrapper.withdrawTo isn't listed in the finding but has the same defect — its underlying was trapped behind the wrapper. Fixed alongside.
TRC20Wrapper.withdrawTo isn't listed in the finding but has the same defect — its underlying was trapped behind the wrapper. Fixed alongside.
- We made the checked transfer the default rather than a per-token exception, renaming safeTransferUSDT to safeTransferChecked: these contracts can't know at deployment time which token they hold, so gating on a configured USDT address would leave every other false-on-success token broken.
We made the checked transfer the default rather than a per-token exception, renaming safeTransferUSDT to safeTransferChecked: these contracts can't know at deployment time which token they hold, so gating on a configured USDT address would leave every other false-on-success token broken.
- The use of safeTransferUSDT was updated to safeTransferChecked on OIF at the PR: #195
The use of safeTransferUSDT was updated to safeTransferChecked on OIF at the PR: #195
Audited Contracts Inherit Issues From Upstream OpenZeppelin Contracts
The audited contracts are generated from the tron-contracts repository by the OpenZeppelin Upgradeability Transpiler, and tron-contracts is in turn based on OpenZeppelin Contracts v5.6.1. Several in-scope issues do not originate from EVM-to-TVM differences or from the transpilation, but are inherited from the upstream codebase. Most were resolved upstream after the v5.6.1 release and are scheduled for the 5.7 release, so the fixes are absent at the audited commit. In particular:
- The baseDelaySeconds delay of GovernorTimelockAccessUpgradeable can be bypassed by executing a proposal without queuing it. The execute function accepts proposals in both the Succeeded and Queued states, but etaSeconds is recorded only when queue is called. A proposal executed directly from the Succeeded state therefore has a proposalEta of 0, the block.timestamp < etaSeconds check passes trivially, and the post-vote reaction window is skipped for operations that do not require prior scheduling. Resolved upstream in PR #6386, with a follow-up in PR #6582.
- The atomic mode of TRC2771ForwarderUpgradeable does not provide all-or-nothing execution (#30). When refundReceiver is the zero address, executeBatch reverts only on invalid requests. A valid request whose target call reverts does not abort the batch: its nonce is consumed, earlier calls remain committed, and the failed request's value is refunded through Address.sendValue to the zero address, permanently locking the corresponding TRX. Resolved upstream in PR #6391.
- The per-target admin delay of AccessManagerUpgradeable can be bypassed when changing a managed contract's authority, since setAuthority can be scheduled and invoked through the execute path, which does not apply the admin delay enforced on updateAuthority. Resolved upstream in PR #6388.
- The burn and burnBatch functions of TRC1155BurnableUpgradeable perform an inline isApprovedForAll check instead of calling the virtual _checkAuthorized function, so authorization overrides that apply to transfers are silently bypassed for burns. Resolved upstream in PR #6435.
- The NatSpec of the _mintConsecutive function of TRC721ConsecutiveUpgradeable states that a batchSize of 0 returns the number of consecutive IDs minted so far, whereas the function returns the next consecutive token ID, which differs from that count whenever _firstConsecutiveId is overridden to a nonzero value. This documentation error was resolved upstream in PR #6433.
Consider backporting the referenced upstream fixes to the tron-contracts codebase, or updating the fork to version 5.7 of OpenZeppelin Contracts once it is published. Furthermore, consider reporting the late-quorum bypass upstream and addressing it in both codebases by tracking the quorum state separately from the stored extended deadline and reacting to every false-to-true quorum transition.
Update: Resolved at pull request #119, #120, #121, #122, #123, #136, #137 and #141. The team stated:




.png)


-1.png)