TradeREA Info

TradeREA brings liquidity to real estate by enabling fractional ownership and secondary market trading, giving investors greater flexibility and access than traditional real estate investing.

TradeREA Logo

TrustNet Score

The TrustNet Score evaluates crypto projects based on audit results, security, KYC verification, and social media presence. This score offers a quick, transparent view of a project's credibility, helping users make informed decisions in the Web3 space.

71.03
Poor Excellent

Real-Time Threat Detection

Real-time threat detection, powered by Cyvers.io, is currently not activated for this project.

This advanced feature provides continuous monitoring and instant alerts to safeguard your assets from potential security threats. Real-time detection enhances your project's security by proactively identifying and mitigating risks. For more information, click here.

Security Assessments

Select the audit
"Static Analysis Dynamic Analysis Symbolic Execution SWC Check Manual Review"
Contract address
N/A
Network N/A
License N/A
Compiler N/A
Type N/A
Language Solidity
Onboard date 2026/09/18
Revision date 2026/10/06

Summary and Final Words

No crucial issues found

The contract does not contain issues of high or medium criticality. This means that no known vulnerabilities were found in the source code.

Contract owner cannot mint

It is not possible to mint new tokens.

Contract owner can blacklist addresses

It is possible to lock user funds by blacklisting addresses.

Contract owner cannot set high fees

The fees, if applicable, can be a maximum of 25% or lower. The contract can therefore not be locked. Please take a look in the comment section for more details.

Token transfer can be locked

Owner can lock user funds with owner functions.

Token cannot be burned

There is no burning within the contract without any allowances

Ownership is not renounced

The owner retains significant control, which could potentially be used to modify key contract parameters.

Contract is upgradeable

The contract uses a proxy pattern or similar mechanism, enabling future upgrades. This can introduce risks if the upgrade mechanism is not securely managed.

Scope of Work

This audit encompasses the evaluation of the files listed below, each verified with a SHA-1 Hash. The team referenced above has provided the necessary files for assessment.

The auditing process consists of the following systematic steps:

  1. Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
  2. Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
  3. Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
  4. Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
  5. Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
  6. Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
  7. Actionable Recommendations: Provide detailed, specific, and actionable steps to secure and optimize the smart contracts.

A file with a different Hash has been intentionally or otherwise modified after the security review. A different Hash may indicate a changed condition or potential vulnerability that was not within the scope of this review.

Final Words

The following provides a concise summary of the audit report, accompanied by insightful comments from the auditor. This overview captures the key findings and observations, offering valuable context and clarity.


Smart Contract Analysis Statement

Contract Analysis

The TradeREA Utility Token (TRU) contract implements an ERC20 token with a fixed supply of 300,000,000 tokens minted once at initialization, upgradeability through the UUPS proxy pattern with a mandatory two day on-chain announcement before any upgrade, signature-based approvals, holder burns, a global pause and an account freeze list. This statement covers a re-audit of a substantially reworked version. The team removed three features that nothing relied on, namely the advisory tier registry, on-chain voting checkpoints and the anti-whale transfer ceilings, and kept their storage reserved so that an existing proxy can be upgraded without a single slot moving. We compared the storage layout with the previously audited version slot by slot and exercised the upgrade from both earlier versions, confirming that balances, the freeze list, ownership, roles and the signature domain all carry over unchanged.

Every issue raised in the previous review has been addressed. The initializer can now only be called by the account that deployed the implementation, so a deployment that initializes in a separate transaction can no longer be taken over. An ownership handoff only passes on administrator rights when the outgoing owner actually holds them, upgrades must be announced for a specific implementation two days before they can be executed, a thirty day recovery path exists if every upgrader key is lost, an approved spender can no longer destroy a frozen holder's tokens, and issuance is now capped cumulatively so that burned tokens can never be issued again. Two smaller observations come out of this round. A single upgrader can remove its co-holders and then complete a scheduled upgrade, because only upgraders can cancel one, so the announcement period gives holders notice but no veto. On a proxy upgraded from an earlier version, the cumulative issuance cap only takes effect once the version three migration has run, so that migration should always be part of the upgrade transaction.

Ownership Privileges

The contract has not been deployed at the time of review, so ownership is whatever the deployment establishes. At initialization ownership and the administrator role go to a named administrator, the operator role to a separate operator, upgrade authority to one or more named upgraders and the entire supply to a named recipient. The role holders retain the following privileges:

  • Replacing the contract logic through an upgrade, reserved for the upgrader role and only possible two days after the exact new implementation has been announced on chain. The upgrader role administers itself, so the administrator cannot grant it directly.
  • Freezing any account, which blocks it from sending, receiving, spending allowances and using signature-based approvals, reserved for the administrator role. Freezing never seizes or reduces a balance.
  • Halting every token movement globally, including transfers and burns, for an unlimited period, reserved for the operator role.
  • Proposing a replacement upgrader if every upgrader key is lost, reserved for the administrator role. The grant can only be executed after thirty days, and any current upgrader can cancel it in the meantime.
  • There is no function that mints tokens, and every mint is checked against the total ever issued, so supply can never exceed 300,000,000 tokens and burned tokens cannot be issued again.
  • There is no fee logic of any kind, so no transfer fee can be levied or raised to any level.
  • No privileged function can move, confiscate or reduce another account's balance.
  • Ownership transfer is two-step, renouncing ownership is permanently disabled, and the last holder of the administrator and of the upgrader role can neither renounce nor be removed.

Security Features

The contract implements several positive security features:

  • Upgrades are announced on chain for a specific implementation and can only be executed after a two day delay, and anyone can read both the pending and the current implementation, so holders see every change before it happens.
  • Initialization is restricted to the implementation's deployer, the implementation disables its own initializers, and both the fresh-deployment initializer and the version three migration can run only once, so a proxy cannot be initialized by a third party or a second time.
  • The freeze list covers sending, receiving, allowance spending and signature-based approvals, while a frozen holder keeps the ability to burn its own tokens and nobody can burn them on its behalf. A rejected signed approval does not consume a nonce, and the signature domain is unchanged across the upgrade, so existing signatures remain valid.
  • Issuance is tracked cumulatively, role holder listings are paginated, every state change emits an event and every check uses a descriptive custom error. We confirmed through property-based testing against a deliberately hostile administrator, operator and upgrader that supply conservation, the issuance cap, the role guards, the freeze semantics and the ownership handoff rules all hold.

Note - This Audit report consists of a security analysis of the TradeREA Utility Token (TRU) smart contract. This analysis did not include economic analysis of the contract's tokenomics. Moreover, we only audited the main contract for the TradeREA Utility Token (TRU) team. Other contracts associated with the project were not audited by our team. The contract was reviewed as source code only, as it was not deployed at the time of review, so no on-chain verification of ownership, bytecode or role custody was possible. We recommend investors do their own research before investing.

Files and details

Findings and Audit result

high Issues | 1 findings

Resolved

#1 high Issue
Initializer is callable by anyone and can be front-run on a non-atomic deployment
TradeREAUtilityToken.sol
L225
Description

The initialize function is external and has no caller restriction. Its only protection is the combination of onlyFreshDeployment and reinitializer(2), which stops it running twice but does not restrict who runs it the first time. If the proxy is ever deployed with empty initialization data and initialized in a separate transaction, anyone watching the mempool can call initialize first with their own address and receive ownership, all four privileged roles and the entire 300,000,000 token supply, after which the legitimate deployer is permanently locked out. The repository contains no deployment script at all, so nothing currently enforces the atomic deployment that this design depends on.

medium Issues | 2 findings

Resolved

#1 medium Issue
Ownership handoff grants the admin role without any admin approving it
TradeREAUtilityToken.sol
L495
L587
Description

When a queued handoff is applied inside acceptOwnership, the new owner is granted DEFAULT_ADMIN_ROLE, OPERATOR_ROLE and TIER_REGISTRY_ROLE. The queue is created by transferOwnershipAndRoles, which only checks that the caller is the owner. Ownership and the admin role can legitimately drift apart, for example when admin authority is moved to a multisig and the deployer steps down from the role but stays the owner because renouncing ownership is blocked. In that state the owner cannot call grantRole directly, yet it can still cause DEFAULT_ADMIN_ROLE to be handed to an arbitrary address through the ownership flow, with no involvement from the party that actually holds the role. The sibling function transferOwnershipAndRolesAndRenounce does check that the owner holds the admin role, so the same condition is enforced in one variant and ignored in the other.

Acknowledged

#2 medium Issue
Upgrade authority is unilateral and takes effect immediately
TradeREAUtilityToken.sol
L769
Description

The upgrade gate only checks that the caller holds UPGRADER_ROLE and that the new implementation is not the zero address. There is no timelock, no multi-party approval and no on-chain announcement period, so a single holder of that role can replace the entire contract logic in one transaction. Because the role administers itself, the deliberate consequence is that the admin role cannot reach upgrade authority, which is a genuine improvement, but it also means the security of every token holder rests entirely on how that one role is custodied. Nothing in the code constrains who holds it, and a fresh deployment grants it to the same single address that receives everything else.

low Issues | 6 findings

Resolved

#1 low Issue
A lost upgrader key freezes upgradeability permanently with no recovery route
TradeREAUtilityToken.sol
L281
L657
Description

The version 2 migration makes UPGRADER_ROLE its own administrator, so only a current upgrader can grant it. The last-holder guard correctly prevents the final holder from being removed, but it cannot protect against a key that is simply lost or a holder contract that has no way to call grantRole. In that situation the holder set can never grow again, the proxy can never be upgraded, and the admin role has no route to fix it because the only function that re-points the role administrator is private and runs solely inside the migration. Recovering would require an upgrade, which is exactly what is no longer possible.

Resolved

#2 low Issue
An approved third party can destroy a frozen holder's tokens
TradeREAUtilityToken.sol
L320
L753
Description

Burns deliberately bypass the blacklist so that a frozen holder retains an escape hatch, which is implemented by skipping the sender check whenever the destination is the zero address. The allowance guard only rejects a blacklisted spender, not a blacklisted token owner, so a spender who was approved before the freeze and is not itself blacklisted can still call burnFrom and destroy the frozen holder's entire balance. The holder cannot revoke the approval either, because that path is unaffected by the freeze but the holder has no incentive to notice. The result is that freezing an account exposes it to unilateral value destruction by any earlier counterparty.

Resolved

#3 low Issue
Voting power delegated to a frozen account is not removed
TradeREAUtilityToken.sol
L452
Description

Blacklisting an account moves that account's own voting units away from its delegate and emits a dedicated event, and the delegation hook then refuses any new delegation by or to a frozen address. Weight that other holders delegated to the account before it was frozen is left untouched, so the frozen account continues to report that voting power and would still be able to influence any governor that reads it. Clearing the position depends entirely on each individual delegator choosing to re-delegate, and the protocol has no way to compel them. The contract's own documentation acknowledges this, but the stated goal of preventing a sanctioned holder from influencing governance is only partly met.

Resolved

#4 low Issue
Initialization writes configuration state without emitting the matching events
TradeREAUtilityToken.sol
L246
Description

The initializer sets the limits flag, both anti-whale ceilings and the deployer's limit exemption, but emits none of the three events that the contract declares for exactly those state changes. Every later change to the same variables does emit an event, so any off-chain consumer that reconstructs configuration from logs starts with a blank or default view and will believe no exemption exists and no ceilings are configured until one of the setters is called for an unrelated reason.

Resolved

#5 low Issue
Role holder listing iterates an unbounded set
TradeREAUtilityToken.sol
L635
Description

The helper that lists role holders other than the current owner reads the full member array and loops over it with no upper bound. The number of holders of any role is limited only by how many times an administrator has granted it, so the cost of this call grows without limit. It is a read-only function, so there is no risk to the token's own state, but a sufficiently large holder set makes it unusable from another contract and eventually too expensive even for node calls, which quietly removes the privilege-drift visibility the function was added to provide.

Pending

#6 low Issue
A single upgrader can remove its co-holders and then upgrade unopposed
TradeREAUtilityToken.sol
L466
L696
Description

The upgrader role administers itself and the last-holder guard only protects the final holder, so any one upgrader can revoke every other upgrader in a single transaction and then schedule an upgrade. Cancelling a scheduled upgrade is reserved for upgraders, and the administrator's only counter, the recovery path, takes thirty days and can be cancelled by that same upgrader. The two day delay therefore gives holders notice but no on-chain veto, and keeping several independent upgraders protects against a lost key but not against a compromised one. We reproduced the whole sequence, ending in a completed upgrade that neither the removed upgrader nor the administrator could stop.

optimization Issues | 4 findings

Resolved

#1 optimization Issue
Array length is re-read on every iteration of the batch loop
TradeREAUtilityToken.sol
L376
Description

The batch tier update evaluates the accounts array length in the loop condition, so the length is loaded again on every pass instead of once. The role holder listing helper has the same pattern on a memory array. Neither is expensive individually, but the batch function is explicitly designed to process up to five hundred entries in one call, which is where the repeated loads become measurable.

Resolved

#2 optimization Issue
A balance is read twice on the same execution path
TradeREAUtilityToken.sol
L343
Description

In the wallet ceiling check the recipient balance is fetched once to evaluate the condition and a second time to build the revert argument. On the failing path that is two storage reads for the same value, and because the read goes through the library's namespaced storage accessor it is slightly more expensive than a direct load.

Resolved

#3 optimization Issue
Single-use modifier increases deployed code size
TradeREAUtilityToken.sol
L211
Description

The freshness guard is declared as a modifier but applied to one function only. Modifier code is inlined at each use site, so declaring it as a modifier rather than a plain internal call brings no reuse benefit here and only adds to the deployed size of a contract that already inherits a large number of parents.

Acknowledged

#4 optimization Issue
Reserved storage slots cannot be declared constant
TradeREAUtilityToken.sol
L195
Description

Automated analysis reports the reserved storage array as an unused state variable and suggests making it constant. Both suggestions are inapplicable. The array is intentionally never read, because its only purpose is to occupy storage slots that a future version can consume, and the compiler does not support constant fixed-size arrays in any case. The layout was verified independently and matches what the code documents, with the array beginning at slot seven and the two queue variables packed together in slot six.

informational Issues | 12 findings

Resolved

#1 informational Issue
Pausing blocks signature-based approvals but not ordinary ones
TradeREAUtilityToken.sol
L732
Description

The permit override rejects calls while the contract is paused, but the plain approve function never reaches the transfer hook and therefore keeps working normally during a pause. A user can still set an allowance by sending a transaction, yet the equivalent signed message is refused. Beyond the inconsistency there is a small practical cost, because a signed approval carrying a deadline can expire during a pause and the signature is then wasted even though nothing about it was unsafe.

Resolved

#2 informational Issue
The supply constant is a ceiling on current supply, not a limit on total issuance
TradeREAUtilityToken.sol
L327
Description

The mint check compares current total supply plus the new amount against the declared constant. Because burning reduces current supply, the headroom created by a burn stays available forever. The present implementation exposes no mint function at all, so the supply is genuinely fixed today, but the contract is upgradeable and a later version that adds minting would be able to reissue tokens holders had destroyed, up to the original ceiling. The contract's own comments flag this, and it is noted here so it is not lost between versions.

Acknowledged

#3 informational Issue
Native currency cannot be recovered if it ever reaches the contract
TradeREAUtilityToken.sol
L70
Description

The inherited upgrade entry point is payable, which automated analysis reports as a contract that can hold native currency without any way to withdraw it. Testing every reachable path shows the concern does not apply to this implementation. An upgrade with no migration payload rejects attached value outright, an upgrade that does carry a payload forwards the value into a function that is not payable and therefore reverts, and the token declares neither a receive nor a fallback function. Value could still arrive through a forced transfer or if a future implementation introduces a payable function, and in those cases it would be stuck because no withdrawal function exists.

Resolved

#4 informational Issue
Two error types are raised for conditions they do not describe
TradeREAUtilityToken.sol
L372
L403
Description

The batch tier update rejects an empty array by raising the error that signals a batch that is too large, reporting a size of zero against a maximum of five hundred, which reads as a contradiction. Separately, the limits setter raises the error that signals a value below the permitted floor when the real problem is that the per-transaction ceiling was set above the wallet ceiling, a completely different condition that happens to be rejected by the same function.

Resolved

#5 informational Issue
One setter lacks the redundant-write guard that every comparable setter has
TradeREAUtilityToken.sol
L358
Description

The limits toggle, the exemption setter and the blacklist setter all reject a call that would write the value already stored, so their events always correspond to a real change. The single tier snapshot setter has no such guard and will happily write the same tier again and emit another event, which means an indexer counting tier changes can see movement that did not happen.

Resolved

#6 informational Issue
Compiler version and build settings are not pinned anywhere in the project
TradeREAUtilityToken.sol
L2
Description

Both source files use an open-ended version requirement rather than a fixed compiler version, and the build configuration shipped with the project contains no compiler version, no optimizer settings and no target virtual machine version. A floating requirement allows the contract to be compiled with any future compiler in the series, including one released after the code was reviewed, and the absence of committed build settings means the produced bytecode is not reproducible. The settings used for this review were chosen by the reviewer and are recorded in the report.

Resolved

#7 informational Issue
Anti-whale ceilings are not applied to issuance
TradeREAUtilityToken.sol
L332
Description

The transfer hook applies the wallet and per-transaction ceilings only when both parties are non-zero addresses, which deliberately exempts minting and burning. That is harmless today because the contract exposes no mint function, but it means the ceilings constrain only peer transfers and any issuance added in a later version would bypass them by default.

Acknowledged

#8 informational Issue
A frozen account can still reduce the circulating supply
TradeREAUtilityToken.sol
L320
Description

Because burns are exempt from the blacklist, an account that has been frozen for compliance reasons retains the ability to destroy its own tokens and thereby lower total supply. This is a documented design decision rather than an oversight, but it is worth stating plainly, since a freeze that still permits a state-changing action is not a complete freeze and the supply reduction is irreversible.

Resolved

#9 informational Issue
Imports pull in entire files and one storage variable breaks the naming convention
TradeREAUtilityToken.sol
L10
Description

All thirteen imports are written as plain file imports rather than named imports, so every symbol in each imported file enters the namespace and a reader cannot tell from the import line which types the contract actually depends on. Separately, the reserved storage array uses a double leading underscore, which does not match the naming convention applied to the other private variables in the contract.

Resolved

#10 informational Issue
The source does not compile against current versions of its main dependency
TradeREAUtilityToken.sol
L10
Description

The project ships no dependency manifest, no lock file and no import remappings, so the library version the contract was written against is not recorded anywhere. Resolving it from the code shows a narrow window. One helper the initializer calls was removed from the library at version 5.5.0, so anything from that release onward fails to compile, while a role enumeration helper the contract relies on only appeared in 5.1.0. The effective compatible range is therefore 5.1.0 through 5.4.0, and the review was carried out against 5.4.0 as the newest version that builds.

Acknowledged

#11 informational Issue
A defensive branch added by an earlier review round is unreachable
TradeREAUtilityToken.sol
L611
Description

The ownership acceptance function carries a branch that clears a queued role handoff when the incoming owner turns out not to be the queued recipient. That state cannot be constructed under the current control flow. The only way to nominate an owner is either the plain transfer function, which was overridden by an earlier review round to clear the queue, or the two combined handoff functions, which always set the queue and the nominee to the same address. The two values are therefore always in lockstep or the queue is empty, so the branch never executes. Two fixes from different review rounds overlap here, and the later one made the earlier one redundant.

Pending

#12 informational Issue
The issuance cap depends on the version three migration having run
TradeREAUtilityToken.sol
L374
L543
Description

On a proxy upgraded from an earlier version, the cumulative issuance counter is only set by the version three migration. If the upgrade is executed without the migration call, the proxy runs the new code with the counter at zero, and nothing stops it from moving on to a later version in that state. A later version that adds a mint path would then treat the full supply as fresh headroom, so up to another 300,000,000 tokens could be issued despite the documented cap. We reproduced this with a test implementation. The current code has no mint path, the state is visible because the reported version stays below the implementation version, and an upgrader can still run the migration later as long as no later initializer has run first.