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 pro-rata redemption contract. Holders present USDR, which is burned, and receive a share of the USDC that has been funded, independent of redemption order. The audit covered the USDRRedemption implementation.
Document | |
|---|---|
| Name | Smart Contract Code Review and Security Analysis Report for Tangible – USDR |
| Audited By | Kerem Solmaz |
| Approved By | Olesia Bilenka |
| Website | https://www.tangible.store/→ |
| Changelog | 08/09/2026 - Preliminary Report |
| 09/09/2026 - Final Report | |
| Platform | Polygon |
| Language | Solidity |
| Tags | Fungible Token, Decentralized Finance (DeFi), Centralization |
| 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
- Olesia Bilenka
- Website
- https://www.tangible.store/→
- Changelog
- 08/09/2026 - Preliminary Report
- 09/09/2026 - Final Report
- Platform
- Polygon
- Language
- Solidity
- Tags
- Fungible Token, Decentralized Finance (DeFi), Centralization
Review Scope | |
|---|---|
| Repository | https://github.com/TangibleTNFT/usdr-redemption-v2→ |
| Initial Commit | e6f27d4 |
| Final Commit | 62d30b0 |
Review Scope
- Initial Commit
- e6f27d4
- Final Commit
- 62d30b0
Audit Summary
The system users should acknowledge all the risks summed up in the risks section of the report
Documentation quality
The repository includes a detailed README that states the pro-rata model, unit conventions, deploy parameters, and wind-down rules.
Public functions on USDRRedemption and IUSDRRedemption carry extensive NatSpec.
Documentation and implementation agree on share registration at redeem time, funding-based payouts, the 180-day post-full-funding sweep, the one-dollar rate ceiling, and that the sweep countdown arms only when remaining funding reaches zero, including the configured-supply slice that cannot be presented on Polygon.
Some operational assumptions, including that the owner is a Gnosis Safe, are documented rather than enforced on-chain.
Code quality
The implementation is a single non-upgradeable Solidity 0.8.30 contract that uses OpenZeppelin Contracts v5.6.1 for two-step ownership, transient reentrancy protection, SafeERC20 transfers, and mulDiv arithmetic.
Test coverage
Line, statement, branch, and function coverage on USDRRedemption stands at 100% as measured by the Foundry coverage tool on the unit, fuzz, and invariant suites. The suite covers primary redemption, funding, rate, sweep, and rescue flows, plus fuzzed rounding properties for path, tranche, and order independence, and an invariant suite for solvency and the pro-rata ledger. Negative cases cover a rate above the on-chain ceiling, including a mistyped rate, and a USDC recipient equal to the redemption contract. The invariant suite asserts the rate ceiling. Polygon fork tests against the live USDR token exist but require an archive RPC and are excluded from continuous integration. Continuous integration runs format, build, and non-fork tests.
System Overview
USDRRedemption is an immutable Polygon contract that converts USDR (ERC-20, 9 decimals) into native USDC (ERC-20, 6 decimals) on a pro-rata basis. The design replaces a first-come-first-served swap with a share ledger so that the order of redemptions does not change a holder's claim on funded USDC. The system consists of USDRRedemption, the IUSDRRedemption public interface, and the IUSDR burn surface used to destroy presented USDR. The USDR and USDC addresses and the redeemable population totalUSDRSupply are constructor immutables. That population is not read from the token total supply. The constructor requires USDR to report 9 decimals and USDC to report 6 decimals. The target rate is stored as USDC raw units per one whole USDR. It may only increase and cannot exceed 1,000,000 raw units, which is 1.00 USDC per whole USDR.
USDC enters the pro-rata ledger only when the owner funds the contract or recognizes an unaccounted balance already held. Funding cannot exceed the ceiling of supply times rate, rounded up, divided by 1e9. A holder who calls redeem burns USDR from themselves through IUSDR, registers matching shares, and is paid the current floor of shares times funded USDC divided by the immutable supply. Later funding is collected with claim. The rate does not appear in the payout formula. A packed position stores shares and USDC already paid. Raw USDC transfers are not funding unless the owner recognizes them. Redeem and claim reject the redemption contract itself as the USDC recipient.
When funded USDC reaches the ceiling, a timestamp is stored. After 180 days the owner may sweep the entire USDC balance, which permanently closes the contract. Holders who never presented USDR, and holders who presented it but never claimed the remainder, forfeit what is left. Full funding includes the configured-supply slice that may never be presented. That slice sits idle until the terminal sweep. If remaining funding never reaches zero, sweep stays locked. A rate increase clears the sweep clock unless the new ceiling is already met. While closed, redeem, claim, fund, and rate changes revert, and entitlement views return zero. Sweep remains callable so USDC received after closure can still be recovered. Stray non-USDC tokens can be rescued. USDC cannot.
Privileged Roles
Owner (USDRRedemption, inherited from OpenZeppelin Ownable2Step): Funds USDC, recognizes unaccounted USDC, raises the rate up to the on-chain ceiling, sweeps after the delay, rescues non-USDC tokens, and starts a two-step ownership transfer. The constructor accepts any non-zero address. Production comments expect a Gnosis Safe, but the contract does not enforce one. Compromise or misuse of this role allows an immediate rate increase that can clear the sweep clock, funding recognition, token rescue, and a sweep of remaining USDC once the delay has elapsed. Loss of the key before handover can freeze further funding and keep sweep locked if full funding is never reached. Ownership renounce is disabled.
Pending owner (USDRRedemption, inherited from OpenZeppelin Ownable2Step): Completes ownership handover through
acceptOwnership(). Until that call succeeds, the current owner retains every privileged path.Redeem and claim, including the overloads that choose a USDC receiver, may be called by any account.
Potential Risks
Scope Definition and Security Guarantees: The audit covers USDRRedemption, IUSDRRedemption, and IUSDR. Tests, mocks, the Polygon deploy script, and the live USDR and native USDC token implementations are outside the review. Flaws in those out-of-scope components may still affect the audited contract because redemption, funding, and payouts call those tokens.
Dependency on External Logic for Implemented Logic: Redemption burns the caller's USDR through the custom allowance-based burn on the out-of-scope USDR token. USDRRedemption never holds USDR and is not granted a burner role. If that burn reverts, spends allowance differently than IUSDR describes, or is blocked while the token is paused, new shares cannot be registered. Already registered positions can still be settled because claim does not call USDR.
System Reliance on External Contracts: Redeem, claim, fund, sweep, stray-token recovery, and available-balance reads depend on the immutable USDR and USDC addresses set in USDRRedemption. Both tokens are upgradeable proxies. A proxy upgrade, a pause, or a change in transfer or burn behaviour can block burns or USDC movement while registered positions remain unpaid. The pro-rata denominator is an off-chain accessible-supply figure supplied at construction and is not read from the USDR token total supply. If that figure is below the USDR that can actually be presented, later holders cannot register shares. If it is above the presentable population, unused capacity leaves funded USDC unclaimable until a terminal sweep, or indefinitely if full funding never occurs.
Dependency on Unaudited External Libraries: The project depends on OpenZeppelin Contracts v5.6.1 for two-step ownership, transient reentrancy protection, SafeERC20 transfers, and mulDiv arithmetic. While OpenZeppelin is widely used and has undergone prior audits, the specific version integrated has not been independently verified as part of this engagement. Any defect in those base contracts would affect USDRRedemption.
Owner's Unrestricted State Modification: The owner may raise the rate on USDRRedemption at any time while the contract is open. The rate must be strictly greater than the current value and must not exceed 1,000,000 raw USDC units per whole USDR. A raise increases the funding target and clears the sweep clock unless the new ceiling is already met. There is no on-chain path to lower the rate. A raise to the ceiling after a full fund at the deploy rate leaves a remaining top-up on the same scale as the original funding target. Residual USDC still cannot leave through the stray-token recovery path. Existing positions remain entitled to a share of already recognized funding.
Coarse-grained Authorization Model Risks: A single owner on USDRRedemption controls funding, rate increases, the terminal sweep, recovery of tokens other than USDC, and two-step ownership transfer. No separate roles isolate those powers. Compromise of that one address grants every privileged path at once.
Absence of Time-lock Mechanisms for Critical Operations: Funding, rate increases, and non-USDC rescues on USDRRedemption execute in the calling transaction. The only delayed owner action is the terminal sweep, which waits 180 days after recognized funding equals the supply-times-rate ceiling.
Insufficient Multi-signature Controls for Critical Functions: Ownership is a single address assigned in the USDRRedemption constructor. Production comments expect a Gnosis Safe, but the contract does not require one. Rate changes, funding, sweep, and stray-token recovery do not require a second on-chain signature.
Single Points of Failure and Control: Funding progress, the rate, terminal closure, and stray-token recovery all require the current owner of USDRRedemption. Ownership cannot be renounced. If the key is lost before a two-step handover completes, further funding and rate increases stop, and the sweep may stay locked if full funding is never reached.
Administrative Key Control Risks: After the sweep delay the owner of USDRRedemption sends the entire USDC balance to a chosen recipient and permanently closes the contract. Holders who never presented USDR, and holders who presented it but never claimed the remainder, forfeit what is left. Non-USDC tokens can be redirected immediately. Payouts follow the funded ledger, not the raw USDC balance, so holders who burn USDR before funding arrives receive a zero or partial payout and must wait on later claims. Unrecognized raw USDC transfers are not owed to anyone.
Findings
Code ― | Title | Status | Severity | |
|---|---|---|---|---|
| F-2026-1926 | Uncapped Rate Increase Clears The Sweep Clock And Risks Locking Residual USDC | fixed | Low | |
| F-2026-1926 | Missing Self-Address Check In Redeem And Claim | fixed | Observation | |
| F-2026-1926 | Funding Target Uses Configured Supply And Leaves Surplus Idle Until Sweep | 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→ |
| Initial Commit | e6f27d4fe2fae3ee2325a8f966f31eeccea70ca2 |
| Final Commit | 62d30b0fa52793947171fecee1a7f0a48a3f0214 |
| Whitepaper | - |
| Requirements | README.md |
| Technical Requirements | README.md |
Scope Details
- Initial Commit
- e6f27d4fe2fae3ee2325a8f966f31eeccea70ca2
- Final Commit
- 62d30b0fa52793947171fecee1a7f0a48a3f0214
- 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.