Introduction
We express our gratitude to the Tangible – USDR team for the collaborative engagement that enabled the execution of this Smart Contract Security Assessment.
USDR Redemption v2 is a fixed rate redemption contract on Polygon PoS that allows holders of the legacy USDR stablecoin to exchange their tokens for USDC at an immutable rate of $0.532 per whole USDR. Each redemption is atomic and settles on a first come first served basis: the contract burns the caller's USDR through an allowance and pays out USDC from the balance it holds at that moment, reverting when the balance cannot cover the payout in full. The audit covered the redemption contract and the two interface files that define its external API.
Document | |
|---|---|
| Name | Smart Contract Code Review and Security Analysis Report for Tangible – USDR |
| Audited By | Kerem Solmaz |
| Approved By | Kornel Światłowski |
| Website | www.tangible.store |
| Changelog | 29/07/26 - Preliminary Report |
| 04/08/26 - Final Report | |
| Platform | Polygon |
| Language | Solidity |
| Tags | Fungible Token, Real World Assets (RWA), Claims, Centralization, Token Standards |
| Methodology | https://docs.hacken.io/methodologies/smart-contracts→ |
Document
- Name
- Smart Contract Code Review and Security Analysis Report for Tangible – USDR
- Audited By
- Kerem Solmaz
- Approved By
- Kornel Światłowski
- Website
- www.tangible.store
- Changelog
- 29/07/26 - Preliminary Report
- 04/08/26 - Final Report
- Platform
- Polygon
- Language
- Solidity
- Tags
- Fungible Token, Real World Assets (RWA), Claims, Centralization, Token Standards
Review Scope | |
|---|---|
| Repository | https://github.com/TangibleTNFT/usdr-redemption-v2→ |
| Initial Commit | 4428548 |
| Final Commit | f59a0f5 |
Review Scope
- Initial Commit
- 4428548
- Final Commit
- f59a0f5
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the report
Documentation quality
The project provides a README that documents the redemption mechanics, the rate representation, the deployment parameters, and the intended operational flow from the owner multisig. NatSpec coverage is complete across the contract and the interface, and the contract header declares five numbered specification invariants together with design rationale for the reentrancy guard, the owner gated funding path, and the deliberate absence of an upgrade path. No separate formal specification document is present in the repository, and references to an out of tree specification were removed before the audited commit. Several cases were identified in which the documented behaviour is stated more strongly than the implementation enforces, and these were reported.
Code quality
The codebase consists of a single 231 line contract and two interface files, and it follows Solidity and OpenZeppelin conventions throughout. OpenZeppelin Contracts v5.6.1 supplies two step ownership, safe ERC20 transfer handling, and the transient storage reentrancy guard, and the build pins both the compiler version and the Cancun EVM version so the transient storage opcodes match the deployment target. The design keeps a single mutable storage slot and no per user accounting, which removes an entire class of state consistency defects, and the constructor validates the token addresses, the rate, and the decimal assumptions of both tokens so a misconfigured deployment fails at construction. All issues identified during the audit are informational in severity and concern documentation accuracy, input validation consistency, and the inherited ownership surface.
Test coverage
Coverage of the contract in scope is 100 percent across lines, statements, branches, and functions as measured by the Foundry coverage tool, and that figure is reached by the mock based suite alone. The project supplies 72 tests: 51 unit tests against mock tokens, a stateful invariant campaign of 64 runs at depth 128 asserting value conservation, monotonicity of the funding timestamp and the sweep unlock relationship, and 18 integration tests that redeem live USDR on a Polygon fork pinned to block 88250000, parameterised over both native USDC and the bridged variant. Three improvements are recommended:
The invariant configuration enables the option that tolerates reverting calls, so an unexpected revert passes without failing the campaign and targeted revert assertions would close that gap
The invariant handler bounds every fuzzed redemption by the maximum redeemable view, which leaves the band between that bound and the true settlement limit unexercised
The fork suite depends on an archive endpoint, falling back to a public node on which three of its tests do not complete, so provisioning a dedicated endpoint would keep the integration path exercised on every change rather than skipped in continuous integration.
System Overview
USDR Redemption v2 is the wind down mechanism for USDR, a Polygon based stablecoin formerly backed by tokenized real estate that lost its peg. The contract offers holders a single deterministic exit: burn USDR and receive USDC at a rate fixed at deployment. It is a deliberate departure from the first version of the redemption system, which used epochs, pro rata rationing, per user accounting, and identity checks. None of those mechanisms are present in this version.
The system in scope consists of one contract, USDRRedemption, together with the IUSDRRedemption interface that publishes its full external API, its events, and its custom errors, and the IUSDR interface that declares the single USDR token function the contract consumes. The contract is deployed once with no proxy and no upgrade path, and the token addresses and the rate are immutable, so no post deployment configuration can alter the pricing or the assets involved.
A holder grants the contract a USDR allowance and calls the redemption entry point, optionally naming a separate address to receive the USDC. The contract computes the payout, rejects a payout that rounds down to zero, and reverts when the payout exceeds the USDC balance held at that moment. It then burns the USDR directly from the caller using the token's allowance based burn function and transfers the USDC to the receiver in the same transaction. The contract never takes custody of USDR and requires no burner role on the USDR token, because that token's burn function is permissionless and spends the caller's allowance. Settlement is all or nothing with no queue and no deferred claim, which makes access to the reserve a race between holders for the USDC currently on hand.
The reserve is replenished by the owner through a dedicated funding function that pulls USDC from the owner and stamps the sole mutable storage variable, the funding timestamp. Funding is restricted to the owner so that third party dust deposits cannot reset the wind down clock. USDC that reaches the contract through a direct transfer is still redeemable but does not stamp that timestamp. Once 180 days elapse with no funding, the owner may sweep the entire remaining USDC balance to a chosen recipient, and every funding call restarts the 180 day period. The timestamp is initialised to the deployment time so the delay also applies to any USDC received before the first funding call. A separate recovery function returns the full balance of any stray token to an owner chosen address and explicitly rejects USDC so the sweep delay cannot be circumvented.
Four view functions support integration. One previews the USDC payout for a given USDR amount, one reports the live USDC balance available to redemptions, one reports an amount intended to size a redemption against that balance, and one reports the earliest timestamp at which a sweep may occur.
The contract depends on two external tokens, both of which are upgradeable proxies outside the audit scope. Redemption relies on the USDR token's burn function, which executes only while that token is unpaused, so a pause of USDR halts all redemptions until it is lifted. The reentrancy guard uses transient storage under EIP-1153, which the Polygon PoS target supports from the Napoli upgrade onward, and the build pins the Cancun EVM version so no later opcode is emitted.
Privileged roles
Owner: Ownership follows the two step transfer pattern and is assigned in the constructor, with a Gnosis Safe multisig named as the intended holder in the deployment script and the README. The owner is the only party that can supply USDC to the reserve, and therefore controls whether redemption remains possible at all. The owner can also sweep the entire USDC balance to any address once 180 days have passed since the last funding call, recover the full balance of any non USDC token held by the contract, initiate a two step ownership transfer, and renounce ownership in a single step through the inherited function. Compromise of the owner key would allow the reserve to be withdrawn as soon as the sweep window is open and would allow any stray token balance to be redirected immediately. The multisig requirement is an operational convention enforced by the deployment procedure rather than by the contract, because the constructor accepts any non zero address as the owner. No timelock constrains the recovery function or the ownership transitions, and the sweep delay is the only time based restriction in the system.
No other privileged role exists. The redemption entry points are permissionless and callable by any USDR holder, no pause capability is present, no address can alter the rate or the token addresses, and the deploying account holds no privilege after construction because ownership is assigned during deployment.
The pause authority on the USDR token is held by the token issuer and sits outside the audit scope, yet it determines whether redemption functions at all, because the burn call reverts while that token is paused.
Potential Risks
Scope Definition and Security Guarantees: The audit covers the redemption contract and its two interface files at the audited commit. The USDR token, the native USDC token, the deployment script, the mock contracts used for testing, and the off chain procedures that move real estate proceeds into USDC and into the reserve are outside the scope. Vulnerabilities in those components may impact the security of the redemption path even though the audited contract itself is correct, because redemption succeeds only when both external tokens behave as their current implementations do.
System Reliance on External Contracts: Every redemption performs two external calls that the audited contract cannot constrain: a burn against the USDR token and a transfer of native USDC. Both tokens are upgradeable proxies controlled by third parties, so the semantics the redemption logic assumes can change without any change to the audited bytecode. The dependency is documented in the interface: the USDR burn function executes only while that token is unpaused, so a pause halts every redemption until it is lifted, and the contract exposes no alternative exit for holders during that period. A change to the allowance behaviour of the USDR burn function, or to the transfer behaviour of USDC, would break redemption or alter its accounting.
Dependency on Unaudited External Libraries: The contract builds on OpenZeppelin Contracts v5.6.1 for two step ownership, safe ERC20 transfer handling, and the transient storage reentrancy guard. That library is widely used and has been audited previously, but the specific version integrated here was not independently verified as part of this engagement, and a defect in the ownership or transfer handling logic would propagate directly into the funding, sweep, and recovery paths. The reentrancy guard additionally depends on EIP-1153 transient storage being available on the deployment network, which holds for Polygon PoS from the Napoli upgrade onward and is pinned through the Cancun EVM setting in the build configuration.
Single Points of Failure and Control: The redemption reserve is supplied entirely by the owner, and the contract holds no mechanism that obliges the owner to fund it or to keep it funded. Settlement is first come first served against the live balance and reverts when that balance cannot cover a payout in full, so a holder's ability to exit depends on the off chain cadence at which real estate proceeds are converted into USDC and supplied to the contract. Holders that redeem early are paid at the full rate while holders that arrive after the balance is exhausted receive nothing and hold no on chain claim, and if funding stops for 180 days the owner may withdraw the remaining balance and end redemption capacity permanently. No on chain solvency guarantee, deferred claim, or pro rata mechanism mitigates this.
Administrative Key Control Risks: The single owner role concentrates the funding, sweep, and token recovery capabilities in one address. The deployment script and the README designate a Gnosis Safe multisig as the intended owner, but the constructor accepts any non zero address, so the multisig protection is an operational convention rather than an enforced property, and a deployment that supplies an externally owned account would place all three capabilities behind one key with no separation of duties. Compromise of that key exposes the entire USDC reserve from the moment the sweep window opens, and exposes any non USDC token balance immediately.
Absence of Time-lock Mechanisms for Critical Operations: The 180 day sweep delay is the only time based restriction in the contract, and it applies solely to the withdrawal of USDC. The token recovery function executes immediately upon the owner's call with no delay and no announcement, and the inherited ownership renunciation function assigns ownership to the zero address in a single transaction with no confirmation step, in contrast to the two step handover applied to ownership transfer. Neither path offers a window in which a harmful or mistaken action could be observed and reverted, and renunciation is irreversible.
Immutable Rate With No Adjustment Path: The redemption rate, both token addresses, and the sweep delay are fixed at deployment, and the contract is not upgradeable, so no mechanism exists to adjust the rate after launch. Any divergence between the fixed $0.532 rate and the realised value of the underlying real estate portfolio cannot be corrected on chain, and correcting it would require deploying a new contract and migrating holders to it. The deployment script constrains the rate to a sanity band between $0.40 and $0.70 to guard against a mistyped value, which mitigates a configuration error at launch but does not provide any adjustment capability afterwards.
Findings
Code ― | Title | Status | Severity | |
|---|---|---|---|---|
| F-2026-1872 | Documented Sweep Timelock Invariant Is The Protection Applied To Directly Transferred USDC | fixed | Observation | |
| F-2026-1872 | Missing Ownership Renunciation Override Allows Permanent Loss Of Funding And Recovery Capability | fixed | Observation | |
| F-2026-1846 | Documentation On The Maximum Redeemable Amount View Contradicts The Implemented Behavior | fixed | Observation | |
| F-2026-1825 | Floating Pragma In Source Files Can Lead To Inconsistent Compiler Behavior Across Builds | fixed | Observation | |
| F-2026-1824 | Inconsistent Zero Amount Validation In Sweep And Rescue Functions | fixed | Observation |
Appendix 1. Definitions
Severities
When auditing smart contracts, Hacken is using a risk-based approach that considers Likelihood, Impact, Exploitability and Complexity metrics to evaluate findings and score severities.
Reference on how risk scoring is done is available through the repository in our Github organization:
Severity | Description |
|---|---|
Critical | Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation. |
High | High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation. |
Medium | Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category. |
Low | Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution. |
Severity
- Critical
Description
- Critical vulnerabilities are usually straightforward to exploit and can lead to the loss of user funds or contract state manipulation.
Severity
- High
Description
- High vulnerabilities are usually harder to exploit, requiring specific conditions, or have a more limited scope, but can still lead to the loss of user funds or contract state manipulation.
Severity
- Medium
Description
- Medium vulnerabilities are usually limited to state manipulations and, in most cases, cannot lead to asset loss. Contradictions and requirements violations. Major deviations from best practices are also in this category.
Severity
- Low
Description
- Major deviations from best practices or major Gas inefficiency. These issues will not have a significant impact on code execution.
Potential Risks
The "Potential Risks" section identifies issues that are not direct security vulnerabilities but could still affect the project’s performance, reliability, or user trust. These risks arise from design choices, architectural decisions, or operational practices that, while not immediately exploitable, may lead to problems under certain conditions. Additionally, potential risks can impact the quality of the audit itself, as they may involve external factors or components beyond the scope of the audit, leading to incomplete assessments or oversight of key areas. This section aims to provide a broader perspective on factors that could affect the project's long-term security, functionality, and the comprehensiveness of the audit findings.
Appendix 2. Scope
The scope of the project includes the following smart contracts from the provided repository:
Scope Details | |
|---|---|
| Repository | https://github.com/TangibleTNFT/usdr-redemption-v2→ |
| Commit | 4428548c46787eb46b6ca438bba7c180e6b76ee8 |
| f59a0f583e837c484ca6f3c5fd95f44271ace992 | |
| Whitepaper | - |
| Requirements | README.md |
| Technical Requirements | README.md |
Scope Details
- Commit
- 4428548c46787eb46b6ca438bba7c180e6b76ee8
- f59a0f583e837c484ca6f3c5fd95f44271ace992
- Whitepaper
- -
- Requirements
- README.md
- Technical Requirements
- README.md
Assets in Scope
Appendix 3. Additional Valuables
Additional Recommendations
The smart contracts in the scope of this audit could benefit from the introduction of automatic emergency actions for critical activities, such as unauthorized operations like ownership changes or proxy upgrades, as well as unexpected fund manipulations, including large withdrawals or minting events. Adding such mechanisms would enable the protocol to react automatically to unusual activity, ensuring that the contract remains secure and functions as intended.
To improve functionality, these emergency actions could be designed to trigger under specific conditions, such as:
Detecting changes to ownership or critical permissions.
Monitoring large or unexpected transactions and minting events.
Pausing operations when irregularities are identified.
These enhancements would provide an added layer of security, making the contract more robust and better equipped to handle unexpected situations while maintaining smooth operations.
Frameworks and Methodologies
This security assessment was conducted in alignment with recognised penetration testing standards, methodologies and guidelines, including the NIST SP 800-115 – Technical Guide to Information Security Testing and Assessment →, and the Penetration Testing Execution Standard (PTES) →, These assets provide a structured foundation for planning, executing, and documenting technical evaluations such as vulnerability assessments, exploitation activities, and security code reviews. Hacken’s internal penetration testing methodology extends these principles to Web2 and Web3 environments to ensure consistency, repeatability, and verifiable outcomes.