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.
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.
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
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 cannot blacklist addresses.
It is not 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.
Contract cannot be locked
Owner cannot lock any user funds.
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 not upgradeable
The contract does not use proxy patterns or other mechanisms to allow future upgrades. Its behavior is locked in its current state.
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:
- Specification Review: Analyze the provided specifications, source code, and instructions to fully understand the smart contract's size, scope, and functionality.
- Manual Code Examination: Conduct a thorough line-by-line review of the source code to identify potential vulnerabilities and areas for improvement.
- Specification Alignment: Ensure that the code accurately implements the provided specifications and intended functionalities.
- Test Coverage Assessment: Evaluate the extent and effectiveness of test cases in covering the codebase, identifying any gaps in testing.
- Symbolic Execution: Analyze the smart contract to determine how various inputs affect execution paths, identifying potential edge cases and vulnerabilities.
- Best Practices Evaluation: Assess the smart contracts against established industry and academic best practices to enhance efficiency, maintainability, and security.
- 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 TokenSale contract is a multi-phase sale of the TRU token in which buyers pay with the network's native currency, priced through a Chainlink feed, or with administrator-approved dollar stablecoins, and receive their tokens in the same transaction. Each phase has its own price, caps, minimum purchase and time window. This statement covers a re-audit of a substantially reworked version. The team removed vesting, the referral bonus and the Merkle whitelist entirely, which eliminates the accounting defects that made the previous version unsafe. A later purchase can no longer freeze a buyer's position, the same inventory can no longer be sold twice, no bonus can be paid outside the caps, and emergency recovery can no longer reach tokens that belong to buyers, because once a purchase completes the contract owes nothing to anyone.
All other previously reported issues have been fixed or reduced to documented operational choices. Stablecoin decimals are read from the token itself, purchases carry a minimum output and a deadline, the oracle staleness window is bounded and announced, fee-charging assets are credited only with what actually arrived, the price feed's decimals are checked at deployment, native proceeds are pulled to the treasury instead of being forwarded inside each purchase, and the administrator role can no longer be emptied. Three smaller observations come out of this round. The staleness window, one hour by default and up to a day, is wide compared with a feed that updates roughly every thirty seconds. Every accepted stablecoin is valued at exactly one dollar, so the sale inherits each asset's peg. The operator role granted to the first administrator does not move with an administrator handover. Price and phase changes still take effect immediately, so buyers rely on their slippage protection and on the administrator being a multisig or a timelock.
Ownership Privileges
The contract has not been deployed at the time of review, so ownership is whatever the deployment establishes. At construction a single administrator receives both the administrator and the operator role, and the administrator title can only be handed over in two steps after a delay. The role holders retain the following privileges:
- Creating phases and changing the price, caps, minimum purchase and time window of a phase with immediate effect, and choosing which phase is active. Caps can never be lowered below what was already sold, and a buyer's minimum output protects a pending purchase from a repricing.
- Recovering unsold sale tokens and any foreign tokens to the funds wallet. While a phase is live the tokens it can still sell are held back, and the contract never holds anything a buyer has already paid for.
- Changing the funds wallet that receives all proceeds, which takes a two day delay and must be accepted by the new wallet itself, and withdrawing accumulated native proceeds to that wallet.
- Adding and removing accepted stablecoins, adjusting the global hard cap, setting the oracle staleness window between five minutes and one day, and pausing and resuming purchases.
- No privileged function can mint tokens. The contract only distributes what has been deposited into it.
- There is no fee of any kind and no referral bonus, so nothing can be skimmed from a purchase.
- There is no blacklist or whitelist in the sale, so no buyer can be individually blocked from buying, although an account frozen on the token itself cannot receive a purchase.
- The administrator role can neither be renounced nor revoked, so administration can never be left empty, and handing it over always takes two steps.
Security Features
The contract implements several positive security features:
- Tokens are delivered in the same transaction as the payment, so there are no outstanding obligations to track or misreport. We confirmed through property-based testing that the tokens held by buyers always equal the recorded total sold and that the deposited inventory is always fully accounted for.
- Both purchase paths take a minimum output and a deadline, are protected against reentrancy and by the pause, and fail with a descriptive error for every invalid input or configuration.
- The oracle read validates the round, the timestamp and the answer before any arithmetic, the feed's decimals are checked at deployment, and stablecoin decimals are read from the token and bounded, so a misconfiguration fails loudly instead of mispricing silently.
- Proceeds handling is robust: stablecoins go straight to the treasury and are credited on the amount received, native proceeds are pulled rather than pushed, the funds wallet change is delayed and must be accepted by the new wallet, and every configuration change emits an event.
Note - This Audit report consists of a security analysis of the TokenSale smart contract. This analysis did not include economic analysis of the contract's tokenomics. Moreover, we only audited the main contract for the TradeREA 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 administrator custody was possible. We recommend investors do their own research before investing.
Files and details
Findings and Audit result
high Issues | 5 findings
Resolved
#1 high Issue
A second purchase resets the vesting clock and makes claiming revert
Each purchase overwrites the buyer's purchase timestamp while the vested amount accumulates across purchases, so the elapsed time used for the linear release restarts from zero for the whole position every time the buyer adds to it. The amount already withdrawn is not reset, so the final subtraction of the released amount from the freshly recomputed vested amount underflows and reverts. A buyer who bought early, waited and claimed part of their allocation, then bought again, can no longer claim anything, and the read-only helper that reports the claimable amount reverts as well, which breaks any front end or integrator that calls it. The position stays frozen until enough time passes for the recomputed linear amount to catch up with what was already withdrawn, which in a typical configuration is months. This was reproduced deterministically and was also found independently by coverage-guided fuzzing and by symbolic execution.
Resolved
#2 high Issue
Tokens owed to vesting buyers are counted as available inventory and sold twice
Before accepting a purchase the contract compares the tokens needed against its own token balance. In a vesting phase the purchased tokens stay in the contract and are only recorded against the buyer, so the balance does not fall and the very same inventory passes the identical check for the next buyer, and the next. The phase cap and the global hard cap are independent numbers set by an administrator and have no relationship to how much was actually deposited, so if either exceeds the deposited amount the contract will sell more than it can ever deliver. Whoever claims first is paid in full and later claimants are left with a claim that reverts for insufficient balance. The public availability helper reports the raw balance and therefore hides the shortfall entirely. A solvency invariant failed within seconds of fuzzing, reaching an obligation of roughly 847,700 tokens against a balance of 822,500.
Resolved
#3 high Issue
The referral bonus is self-awardable, repeatable and exempt from every cap
The referrer is an arbitrary address supplied by the caller. The only checks are that it is not the zero address and not the buyer, both of which are satisfied by a second wallet the buyer controls, so any purchaser can award themselves the full bonus, up to ten percent of every purchase. The bonus is paid on every purchase rather than only the first, because the stored referrer field is written once but the transfer uses the parameter each time, which makes the stored field effectively decorative. The bonus is not added to the phase total, the global sold counter or the hard cap check, so the tokens that genuinely leave the contract can exceed the published hard cap by the full bonus rate. In a vesting phase the bonus is also released immediately while the purchase itself stays locked, giving a self-referring buyer instant liquidity the schedule was meant to prevent.
Resolved
#4 high Issue
Payment token decimals are supplied by an administrator instead of read from the token
Registering an accepted payment token takes the decimal count as a function argument and never compares it with the token's own reported value. The purchase path then uses that number to normalise the paid amount into a six decimal dollar figure. If the value is understated, buyers are credited vastly more than they paid. Registering an eighteen decimal asset as a six decimal one lets an attacker pay one millionth of a millionth of a token and receive credit for a full dollar, which was reproduced as ten tokens handed over for an economically worthless payment. If the value is overstated the opposite happens and genuine payments round down to zero, so a real one thousand dollar purchase is rejected outright and the sale silently stops accepting that asset. Neither direction produces any warning.
Resolved
#5 high Issue
Emergency recovery can withdraw the tokens backing outstanding vesting claims
Emergency recovery transfers an arbitrary amount of an arbitrary token to the funds wallet with no restriction, and nothing in the contract distinguishes the sale token held on behalf of vesting buyers from spare inventory. The administrator can therefore withdraw the exact tokens that buyers have already paid for and that the contract has recorded as owed to them, after which every claim reverts for insufficient balance. The destination wallet is itself changeable by the same role in a single transaction with no delay, so the funds need not go anywhere previously disclosed. There is no timelock on either function.
medium Issues | 5 findings
Resolved
#1 medium Issue
No slippage floor or deadline on either purchase path
Neither purchase function accepts a minimum output or an expiry. The number of tokens a buyer receives depends on the phase price, which an administrator can change at any moment, and on the oracle price for the native currency path, which moves independently. A buyer therefore has no way to express the fill they are willing to accept. A repricing that lands before a pending purchase silently changes the outcome, which was reproduced as a buyer receiving one tenth of the expected tokens with no revert, and an oracle move between submission and inclusion has the same effect. There is also no protection against an administrator observing a large pending purchase and repricing ahead of it.
Resolved
#2 medium Issue
The oracle staleness window is unbounded and changes without an event
The staleness window is a plain storage value that the operator role can set to anything, with no bounds and no event. Setting it to the maximum unsigned value disables the staleness check completely, after which the contract will accept a price of any age. This was reproduced by accepting a price stamped at the beginning of unix time together with an absurd value, resulting in a thousand dollars of purchase credit for one unit of native currency. Setting it to zero has the opposite effect and makes every native currency purchase revert, since any non-zero age then fails the comparison. Because no event is emitted, neither change is visible to monitoring that relies on logs.
Resolved
#3 medium Issue
Buyers are credited for the requested payment amount, not the amount received
The stablecoin path pulls the payment directly from the buyer to the treasury and then credits the buyer based on the amount requested, without checking how much actually arrived. Any asset that deducts a fee on transfer, rebases, or otherwise delivers less than the stated amount therefore results in the buyer being credited for value the treasury never received. This was reproduced with a ten percent transfer fee, where the buyer received the full allocation for a payment that arrived ten percent short, and the recorded revenue figure was overstated by the same margin. Assets with a dormant fee switch are a realistic concern here because the accepted list is administrator-controlled and the fee can be enabled after registration.
Resolved
#4 medium Issue
The price feed is assumed to report eight decimals and this is never checked
The native currency conversion divides by a fixed constant that only produces the right answer when the feed reports eight decimals. The constructor accepts any feed address without reading its decimals, and the reference is immutable so it cannot be changed afterwards. Pointing the sale at an eighteen decimal feed was accepted without complaint and inflated every fill by ten orders of magnitude in testing. Most major dollar-denominated feeds do use eight decimals, which is what makes this easy to overlook, but nothing in the contract enforces it and the consequence of getting it wrong is total.
Acknowledged
#5 medium Issue
No timelock on privileged functions and phase terms can be changed retroactively
Every privileged function takes effect in the transaction that calls it. The phase update function rewrites the price, both caps, the whitelist root and the vesting cliff and duration of a phase that already has buyers in it, and the vesting parameters are read at claim time rather than captured at purchase, so changing them retroactively alters the schedule of positions that were already bought. Setting the duration to zero releases every locked position immediately, and lengthening it pushes existing claims backwards. The funds destination and the global hard cap can likewise be changed instantly. None of this is announced in advance and none of it is delayed.
low Issues | 12 findings
Resolved
#1 low Issue
Lowering a cap below what is already sold produces an arithmetic panic
Each of the three cap checks builds its error argument by subtracting the amount already sold from the cap. Because the phase update function and the hard cap setter both allow a cap to be lowered below the recorded total, that subtraction can underflow, and the transaction then fails with a bare arithmetic panic instead of the descriptive error the contract declares for the situation. Buyers and integrators see an opaque failure and cannot tell that a cap was reduced under them.
Resolved
#2 low Issue
The accepted payment token registry accumulates duplicate entries
Disabling a payment asset only clears its enabled flag and leaves it in the list. Because registration appends whenever the asset is not currently enabled, re-enabling a previously disabled asset appends a second entry, and each further disable and re-enable cycle adds another. Five cycles were reproduced leaving five extra copies. The list is returned in full by a public helper, so it grows without bound and becomes progressively more expensive to read, and the reported event on removal states a decimal count of zero while the stored record keeps the real value.
Resolved
#3 low Issue
The active phase helper reverts before any phase has been created
The helper that returns the active phase indexes the phase array directly, so between deployment and the creation of the first phase it fails with an out of bounds panic. The neighbouring helper that reports whether the sale is active handles the same state correctly by checking the array length first, so the inconsistency is clearly unintended.
Resolved
#4 low Issue
A funds wallet that cannot receive native currency blocks all native purchases
Every native currency purchase forwards the payment to the funds wallet with a raw call and reverts the whole purchase if that call fails. The destination is set by an administrator with no check that it can accept native currency, so pointing it at a contract that rejects transfers stops the entire native purchase path for every buyer, which was reproduced. Because the call forwards all remaining gas and is not capped, a destination that consumes gas deliberately can also make purchases fail or become arbitrarily expensive.
Resolved
#5 low Issue
The phase update function does not re-validate the time window
Phase creation rejects a window whose end is not after its start, but the update function writes both timestamps with no such check. An administrator can therefore update a phase into an inverted window, which the creation function refuses, and the phase then becomes permanently unbuyable because the purchase validation can never be satisfied. This was reproduced by confirming that the creation call reverts and the equivalent update call succeeds.
Resolved
#6 low Issue
The vesting cliff is ignored or degenerates depending on how it is configured
Whether a purchase is locked at all is decided solely by the vesting duration, so a phase configured with a long cliff and a zero duration pays out in full immediately and the cliff is silently discarded. This was reproduced with a one year cliff that had no effect whatsoever. In the opposite configuration, where the cliff is longer than the duration, the elapsed time has already passed the duration by the moment the cliff expires, so the entire position unlocks in a single step and no linear release ever occurs. Neither combination is rejected at configuration time.
Resolved
#7 low Issue
Three configuration setters change state without emitting an event
The setters for the oracle staleness window, the referral bonus rate and the global hard cap all write storage and return without emitting anything. Every other configuration change in the contract does emit an event, so these three are gaps rather than a consistent choice, and two of them alter parameters that directly affect pricing and cap enforcement.
Resolved
#8 low Issue
Purchase and claim entry points do not validate phase existence or index
Four externally reachable functions index the phase array without checking it first. Both purchase paths reach it through the shared validation helper, which reads the active phase before anything else, so while no phase has been created yet a buyer gets a raw out of bounds panic instead of a descriptive error. That window is narrow, because it closes permanently once the first phase exists and the active index can never be pushed past the array bound afterwards, but it is open from the moment the contract is deployed. The two claim functions take a phase index straight from the caller and index the array with it, so any out of range value fails the same opaque way. The neighbouring helper that reports whether the sale is active does guard the empty case, which shows the omission is not deliberate.
Resolved
#9 low Issue
Oracle round metadata is discarded and a future timestamp underflows
The price read keeps only the answer and its timestamp and discards the round identifier and the round in which the answer was computed, and it has no sequencer uptime check. It also subtracts the reported timestamp from the current block time without checking the order of the two, so a feed reporting a timestamp in the future makes the subtraction underflow and the whole purchase fails with a raw arithmetic panic. A conforming feed cannot trigger that, because it stamps each round with the block that recorded it and so can never be ahead of the consuming block, and the stablecoin path does not read the oracle at all, so the sale does not stop entirely. Reaching it requires a non conforming feed, and since the feed reference is immutable that can only originate at deployment.
Resolved
#10 low Issue
The administrator role can be emptied, which permanently disables administration
The contract uses the plain access control base with no protection against the final administrator revoking or renouncing its own role. If that happens, every function gated on the administrator role becomes permanently unreachable, which means phases can no longer be created or updated, payment assets can no longer be managed, the funds destination is frozen and emergency recovery is gone, while buyers may still hold unclaimed vesting positions. The non-enumerable base also means the number of holders cannot be read on chain, so the condition cannot be checked before it is too late. The companion token contract implements exactly this guard, so the omission is inconsistent within the same project.
Resolved
#11 low Issue
The referral ledger records payments already made rather than a claimable balance
The mapping named as if it held rewards owed to a referrer is in fact a running total of bonuses that have already been transferred, because the bonus is sent immediately in the same call that records it. Anyone reading the contract or its public getters would reasonably conclude there is a pending balance to be claimed, and there is no claim function, so the naming is actively misleading to integrators and to anyone auditing the token flows.
Pending
#12 low Issue
The oracle staleness window is wide compared with how often the feed updates
The price read accepts an answer up to one hour old by default, and the operator can widen that to a full day. The Chainlink MATIC / USD feed on Polygon that the sale is designed for refreshes roughly every thirty-three seconds, so a healthy answer is never more than about a minute old, and anything much older means the feed has stalled. Inside the window the contract keeps pricing native currency purchases at the frozen answer, so if the market moves while the feed is stuck, buyers paying in native currency receive tokens at a price that no longer reflects what their payment is worth, by up to an hour of price movement by default and up to a day at the maximum setting. The stablecoin path is not affected.
optimization Issues | 6 findings
Resolved
#1 optimization Issue
The purchase execution function is large and branch-heavy
The internal purchase execution function has a cyclomatic complexity of twelve, carrying four cap checks, the referral calculation, the first-purchase bookkeeping, the vesting branch and the bonus transfer in one body. High branch density in the most security-sensitive function of the contract raises both the gas cost of the common path and the chance that a future change misses an interaction between branches.
Resolved
#2 optimization Issue
The phase structure wastes a storage slot through field ordering
In the phase structure the two timestamps are adjacent and pack together, but the whitelist root then forces a new slot, the two vesting fields pack into another, the running total takes a third and the active flag occupies a fourth almost entirely empty slot. Moving all four small time fields and the boolean next to each other lets them share a single slot, since together they need thirty three bytes and the boolean can follow the reordered pair.
Resolved
#3 optimization Issue
The phase name is stored as a dynamic string
Each phase stores a human-readable name as a dynamic string, which costs a separate storage slot plus the string data and is copied in full whenever the phase structure is returned by the public helper. The name is used only for display and in one event, so nothing requires it to be dynamically sized.
Resolved
#4 optimization Issue
Counter updates and increments can be cheaper
The buyer counter uses a post-increment where the return value is discarded, which is marginally more expensive than a pre-increment. The three cumulative counters for phase total, global total and raised amount are each incremented under the default overflow checks even though the cap comparisons immediately above them already constrain the result, so the checks are redundant work on the hot purchase path.
Resolved
#5 optimization Issue
The active phase index is resolved twice per purchase
The validation function reads the active phase index to fetch the phase reference, and the execution function reads the same storage variable again to key the buyer's purchase record. The two reads always agree today, but the index is fetched from storage twice on every purchase and the relationship between the two lookups is implicit rather than passed explicitly.
Acknowledged
#6 optimization Issue
Constructor is reported as non-payable
Automated analysis notes that marking the constructor payable would remove the compiler's value check and save a small amount of deployment gas. The same note is raised for the token contract. The saving is negligible and applies once, while the check it removes is a genuine, if minor, guard against a mistaken deployment transaction carrying value.
informational Issues | 13 findings
Resolved
#1 informational Issue
Claims remain possible while the sale is paused
The purchase functions are both gated on the contract not being paused, but the claim function is not, so vesting releases continue during a pause. This was confirmed by pausing the sale and successfully claiming a matured position. Whether that is correct depends on what the pause is for, and the contract documentation describes it only as a circuit breaker without saying which flows it is meant to interrupt.
Resolved
#2 informational Issue
The availability helper overstates how many tokens are actually available
The helper that reports remaining tokens returns the contract's raw balance. In a vesting phase that balance still includes everything owed to buyers who have not yet claimed, so the figure presented as available inventory can be entirely committed already. This is the public face of the over-selling problem and it means the shortfall is not visible to anyone reading the contract from outside.
Resolved
#3 informational Issue
Revert strings are used in five places despite a full set of custom errors
The contract declares sixteen custom errors and uses them consistently in the purchase and validation paths, but five checks still use string-based requires, covering both phase index validations, the native currency forwarding check, the referral bonus bound and the emergency withdrawal check. The mixture means some failures carry a matchable selector and others only a string.
Resolved
#4 informational Issue
Whitelist leaves are hashed once rather than twice
The whitelist leaf is a single hash of the packed buyer address. With a fixed twenty byte input there is no practical way to pass an internal node off as a leaf, so this is not exploitable as written, but single-hashed leaves are fragile against changes in how the tree is generated and diverge from the recommended construction. The related concern about packing collisions does not apply here because only one value is packed.
Resolved
#5 informational Issue
Native currency can be sent to the contract with no accounting or event
The contract declares an empty receive function, so anyone can send native currency to it directly. Nothing is recorded and no event is emitted, and the only way the balance can leave is through emergency recovery to the funds wallet. A buyer who sends currency directly rather than calling the purchase function receives nothing and has no on-chain evidence of the payment beyond the raw transfer.
Resolved
#6 informational Issue
Phase creation accepts incoherent parameter combinations
Phase creation checks only that the end time is after the start time. A price of zero is accepted and makes every purchase in that phase fail on division. A per-wallet cap larger than the phase cap is accepted and is simply dead configuration. A minimum purchase larger than the per-wallet cap is accepted and makes the phase unbuyable by anyone. None of these produce a warning at configuration time.
Resolved
#7 informational Issue
Emergency withdrawal emits its event after the external transfer
The native currency emergency withdrawal forwards the balance and only then emits its event, which automated analysis reports as a reentrancy-adjacent ordering issue. There is no exploitable path, because the function is restricted to the administrator role, reads no state after the call and changes no state at all, so the only consequence is event ordering if the destination reenters.
Resolved
#8 informational Issue
One declared error is never used
The error declared for a position that is still inside its cliff is never raised anywhere in the contract. The calculation it appears to belong to returns zero instead when the cliff has not expired, so the error is dead code and its presence suggests a check that was intended but not wired up.
Acknowledged
#9 informational Issue
Naming does not follow the usual conventions
Two public functions carry a fully capitalised currency name, which reads as a constant rather than a function. Four administrative setters prefix their parameters with an underscore, a convention normally reserved for storage variables. Two immutable state variables use mixed case where the convention for immutables is upper case with underscores.
Resolved
#10 informational Issue
Dollar value conversion truncates, always in the protocol's favour
Both purchase paths reduce the payment to a six decimal dollar figure with integer division before computing the token amount, so up to one millionth of a dollar per transaction is discarded. Automated analysis flags this as division before multiplication. The residue is economically negligible and always rounds against the buyer rather than the protocol, and a payment small enough to round to zero is rejected by the zero amount check rather than taken for nothing.
Resolved
#11 informational Issue
Compiler version and build settings are not pinned anywhere in the project
The file uses an open-ended compiler version requirement, and the project's build configuration contains no compiler version, optimizer settings, target virtual machine version, dependency manifest or import remappings. The contract therefore cannot be compiled from a fresh checkout without the reviewer supplying all of that, and two independent builds can legitimately produce different bytecode.
Pending
#12 informational Issue
Every accepted stablecoin is valued at exactly one dollar
Accepted stablecoins are credited at one dollar per whole token with no price check, and the registration path cannot tell whether an asset really is a dollar stablecoin. The source documents this as a design decision, but it means the sale inherits the peg of every accepted asset. During a depeg, such as the brief one a major dollar stablecoin went through in 2023, buyers could pay with the depegged asset and receive tokens at a discount equal to the depeg until the administrator removes the asset or pauses the sale.
Pending
#13 informational Issue
The operator role does not follow an administrator handover
The constructor grants the operator role to the first administrator, but the two-step administrator transfer only moves the administrator title. After a handover the previous administrator therefore keeps the operator role and can still pause and unpause the sale, switch the active phase and change the staleness window within its bounds. Switching phases matters most, because another phase can carry a different price. The new administrator can revoke the role, but nothing in the transfer prompts it to, and the token contract handles the same situation differently by moving the operator role together with administrator rights.