Truder Wallet Info

Hold your favorite assets in one place. Full support for Binance Smart Chain (BSC), Base, Ethereum, Solana, and Tron with zero limits.

Truder Wallet 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.

64.68
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

"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/07
Revision date 2026/09/09

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:

  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

This statement covers a re-audit. The TruderRouter contract was reviewed once before, twenty-seven items were reported, and the project has since rebuilt the contract and redeployed it at three new addresses on Ethereum, Base and BNB Smart Chain. This review examines those new deployments, checks each previous item to see whether the fix works rather than merely exists, and looks for anything the rebuild introduced.

The contract is still not a token. It is a payment routing contract: it accepts native currency or a third-party token from a sender, forwards the bulk of it to a recipient, forwards a fee to the project treasury, and records what it did as an event. It has no supply of its own, no balances, no allowances and no transfer function, which means the usual token questions - can it mint, can it burn, can it blacklist an address, can it lock holdings - do not apply here, because the corresponding code does not exist. Anyone assessing this contract should read it as a payment rail rather than as an asset.

The contract is 147 lines of application code resting on a widely used and extensively reviewed open-source contract library. Nothing about the project's own description was taken on trust. All sixteen source files are identical across the three networks, the published interfaces are identical, the deployed code is identical, and a local rebuild reproduces the deployed code byte for byte on each chain, including the trailing metadata. Every one of the fifteen bundled library files was compared against the official public release and found identical, which matters because the correctness of one of the fixes depends entirely on the exact behaviour of two of those files. Real payment calls were also simulated against the live code on each network to confirm the deployed contract behaves as its published source says, and those simulations independently prove that a newer processor feature the rebuild now depends on is genuinely available on all three chains.

The engineering work is good, and most of it is genuinely fixed. Of the twenty-seven previous items, nineteen are fully resolved and three are partially resolved. Every cost optimisation was carried out. Every code quality item bar two was carried out. Three of the four moderate issues were addressed in whole or in part, and the fixes hold up under testing rather than only on paper: each one was re-run against the exploit that originally demonstrated the problem. Where the previous report insisted that a particular fix be proven by measurement rather than argument, it was measured - the gas amplification attack that a hostile recipient could previously mount has been reduced from more than fourteen million gas to within a third of one percent of the theoretical minimum.

No critical and no high severity issues were identified. One moderate issue is nonetheless the most important item in this statement. It is not a coding error. The rebuild correctly split the administrative role away from the fee treasury, which is exactly what was asked for, but the administrative role was then handed to a single ordinary wallet address rather than to the multisig. The previous review identified the two-of-three multisig as the single strongest control in the whole deployment. That control has been taken off the administrative path: the multisig now receives the fees but has authority over nothing. One leaked key is now enough to redirect every future fee, halt the service, sweep whatever the contract is holding to any destination, or permanently destroy all administrative control. Previously two of three keys were required, and the only place funds could be sent was the multisig itself.

To be precise about what is not at risk: user funds remain safe. The contract still has no code path that can move tokens from any account other than the one calling it, so users who grant unlimited spending permission cannot be drained by a compromised administrator, and the recipient of a payment is chosen by the sender and cannot be altered. Both properties survived the rebuild intact and were re-confirmed by testing. What is exposed is fee revenue, service availability, and anything the contract happens to be holding at the time.

The good news is that this needs no new deployment and no code change. The mechanism to fix it is already built into the contract and already tested: ownership can be handed to the existing multisig through a two-step transfer, and because the transfer only completes when the multisig accepts it, the process cannot go wrong. Doing so would also exercise the multisig for the first time, which closes a separate long-standing concern at no extra cost. At the time of review the contracts had not processed a single payment - the Ethereum deployment has exactly one transaction, its own creation - so this is the least costly possible moment to act.

Ownership Privileges

Ownership is no longer permanent, which is a substantial improvement over the previous deployment. The owner can now be changed, and the fee destination can now be changed. The owner is currently a single ordinary wallet address rather than a multisig, and that address holds every privilege the contract has. The owner can:

  • Redirect all future fee revenue to any address, in a single transaction, taking effect immediately with no delay and no second signature. Because payments are visible before they are processed, this can also be aimed at one specific large payment.
  • Halt all payments on a given network. This stops the service but puts no funds at risk, because the contract is a pass-through that holds nothing.
  • Sweep the contract's balance of native currency or of any single token to any destination of their choosing. In the previous contract funds could only ever be sent to the multisig, so a compromised administrator could relocate but not steal; that is no longer the case. The change was necessary - without it a token issuer could freeze the treasury and strand funds permanently - but it widens the consequences of a key compromise.
  • Permanently destroy all administrative control, in one transaction, with no confirmation step and no way back. The standard renounce function was inherited from the library and left enabled. Should it ever be called, the treasury could never be changed again, nothing could ever be recovered, the emergency stop could never be used, and no replacement owner could be appointed. Payments would continue working normally throughout, so nothing about the contract's behaviour would signal that all recovery capability had been lost. In effect this would restore the exact permanence the rebuild was meant to remove, but with no remedy.
  • Hand ownership to a new address through a safe two-step process, where the recipient must accept before the transfer takes effect. A mistyped destination therefore cannot strand the contract. This was verified by testing.
  • The owner still cannot touch user funds or user approvals. This is the most important limitation on the role and it survived the rebuild. The contract always draws funds from the account calling it, so spending permission granted to this contract remains under the sole control of the account that granted it.
  • The owner cannot alter where a payment goes. The recipient is chosen by the sender. The principal always reaches its intended destination; only the fee portion is under the owner's influence.
  • The owner cannot upgrade, replace or alter the contract's logic. There is no proxy, no delegated call and no initializer. This was confirmed by automated checking and independently by the exact match between the deployed code and a direct build of the published source. One point of caution for readers: a comment in the published source incorrectly states that the owner can upgrade the contract. That comment is wrong and is reported as an item to correct.
  • The owner cannot mint, burn, blacklist, freeze balances, or set or raise a fee rate. None of that code exists. The fee ceiling is a fixed value written into the code with no setter and no upgrade path, and it was mathematically proven that no accepted payment can ever exceed one tenth of its gross amount.

A separate operational concern applies to the same address. The owner wallet holds very little native currency - enough to pay for an emergency stop only while network fees stay below roughly four gwei on Ethereum and roughly a quarter of a gwei on BNB Smart Chain. Both are ordinary rather than extreme levels. Since network congestion and on-chain incidents tend to coincide, the emergency stop may be unaffordable precisely when it is most needed. The multisig, meanwhile, has still never executed a transaction on any network, so the required quorum has never actually been assembled.

Security Features

The rebuilt contract implements a materially stronger set of security features than its predecessor:

  • The fee destination is now separate from the administrative role, and both can be changed. The previous contract fixed the fee destination permanently at deployment, which was reported as a moderate issue and is now closed.
  • An emergency stop was added. Both payment functions can be halted, while the recovery function deliberately remains available during a stop, so a halted contract does not also become an unrecoverable one. Both behaviours were mathematically proven rather than only tested.
  • Ownership transfer is two-step, so ownership cannot be lost to a mistyped address, and a pending transfer can be cancelled.
  • The recovery function now takes an explicit destination and no longer fails when there is nothing to recover. Combined with the changeable treasury, this closes the scenario in which a hostile token issuer could freeze the treasury and permanently trap funds - demonstrated closed by recovering funds and resuming payments after a simulated freeze.
  • Recovery still operates on one token per call rather than looping over a list. This is the right choice and was preserved: a loop would allow a single frozen asset to block the recovery of every other asset.
  • The reentrancy guard was modernised to use a newer, cheaper mechanism, and it was confirmed working on all three live networks rather than assumed from documentation. Hostile recipients, a hostile treasury and hostile token callbacks were all used to attempt re-entry into every entry point, and every attempt was rejected. The tests fail loudly if the attempts ever stop actually occurring, rather than passing vacuously.
  • No function anywhere writes to the contract's memory after making an external call, so the ordering mistake that causes most reentrancy vulnerabilities is absent by construction, and there is no way for an outside observer to read a partially updated state.
  • The gas amplification weakness reported previously is fixed, and the fix was verified by direct measurement across four payload sizes rather than by reading the code.
  • Input validation was tightened throughout. Zero amounts, zero addresses, payments to the contract itself and payments to the token contract are all now rejected with clear, specific errors instead of failing silently or obscurely.
  • The token path still measures the contract's own real balance change instead of trusting the requested amount, which keeps it correct for tokens that tax their own transfers or rebase, and neutralises manipulation based on inflating the contract's balance beforehand.
  • The contract still cannot pull a third party's spending permission under any circumstances.
  • All arithmetic remains protected by the compiler's built-in overflow checks. The contract contains no blocks that opt out of those checks, no loops, no inline assembly, no delegated calls, no self-destruct, and no dependence on the transaction originator or on block timestamps. It also has no function for receiving plain transfers, so currency sent to it by mistake is rejected rather than silently absorbed.
  • The compiler optimiser is now enabled, and the compiler target is now stated explicitly rather than left to a default. Payments cost meaningfully less than before: about twenty-one percent less on the native path with a fee, twenty-seven percent less without one, and the deployed code is twenty-nine percent smaller.
  • Every privileged action emits an event, including the initial setup, which makes the contract straightforward to monitor.
  • Correctness was established three independent ways and none produced a failure: fourteen mathematical proofs across the full range of possible inputs, eight behavioural properties each checked over 32,768 randomised call sequences, and sixty-four tests including working demonstrations of every issue reported here.

Points Requiring Attention

No critical and no high severity issues remain open. Five moderate issues, five minor ones, five code quality observations and one cost note do. The full report sets each out with a reproduction and a recommended fix. In order of importance:

  • Hand ownership to the existing multisig on all three networks. This is the single highest-value action available, it requires no new deployment and no code change, and it uses a mechanism already built into the contract and already tested. It also proves the multisig quorum works for the first time. Do it before any value flows.
  • Fund the owner wallet and every multisig signer with enough gas on every network to act during a fee spike, not just at today's prices. An emergency stop that cannot be paid for is not an emergency stop.
  • Disable the renounce function in any future version, and in the meantime treat it as a transaction that must never be approved. One call would permanently destroy all recovery capability while leaving payments running normally, so the failure would be silent.
  • Add a published delay to fee redirection. This can be done without redeploying, by placing the owner behind a standard delay contract controlled by the multisig. The emergency stop should stay outside the delay.
  • Determine, in writing, whether any backend or accounting system credits balances, settles invoices or triggers payouts on this contract's payment records. The project has chosen not to restrict which tokens may be routed, which is a legitimate decision for a permissionless product and is respected here. It does mean, however, that anyone can still cause the contract to emit a complete, correctly formed payment record for value that never moved. Nothing is lost on-chain when this happens. The risk sits entirely at the boundary with the project's own systems, and if those systems act on these records then independent verification becomes mandatory rather than advisable. This question was asked in the first review and has still not been answered; it is the one item whose answer changes a severity rating.
  • Report the amount the recipient actually received rather than the amount dispatched. For tokens that tax their own transfers these differ, measured here at 855 units claimed against 812.25 delivered. The project declined a related change on the grounds that it would break compatibility with the mobile applications, which is a fair objection to that change - but it does not apply to this one, which needs no interface change at all and so forces no app release.
  • Note that the compatibility claim holds for the two payment functions but not for the contract as a whole. The payment functions are unchanged, so no app release is required and that goal was met. An administrative lookup and the old recovery function were removed, however, and the recovery event changed shape. Any dashboard, indexer or back-office script reading those will fail silently against the new deployments.
  • Correct the published documentation. Two comments in the verified source are factually wrong - one states the contract can be upgraded, which it cannot, and one describes a ten percent fee where the reality is a caller-chosen fee with a ten percent ceiling and no minimum. These sit in the source that three block explorers display, so they are the version most third parties will read.
  • Decide the fee model explicitly. The fee is still supplied by whoever calls the contract, and the code enforces only a ceiling. Anyone constructing the transaction themselves routes value at no cost. Fee income is therefore not enforced by the contract and depends on users choosing the official client. Either compute the fee inside the contract from a stored rate, or accept that it is voluntary and stop describing it as a fee.
  • Retire the three superseded deployments from every reference, and monitor them. They remain live, unpaused and unpausable, with none of the new protections, so any stale link or saved address keeps working.

The contract as deployed is materially healthier than its predecessor and the code-level work is sound. If the operational recommendations above are carried out - the ownership handover, the funding and the monitoring - no further code review is required for the code now running, and this report covers it. A future deployment adding a delay, disabling renunciation and correcting the reported amount would warrant a focused review of those three areas rather than a full audit.

Note - This Audit report consists of a security analysis of the TruderRouter smart contract. This analysis did not include economic analysis of the contract's tokenomics. Moreover, we only audited the main contract for the TruderRouter team. Other contracts associated with the project were not audited by our team. In particular, the off-chain systems that consume this contract's events - the backend, the interface and any indexing infrastructure - were outside the scope of this review, and one of the findings above cannot be given a final severity rating without knowing how those systems treat the contract's payment records. A security review reduces risk but cannot prove the absence of every vulnerability, and the conclusions here apply to the code as deployed at the addresses stated in the full report: any change to the source, the compiler settings, the ownership or treasury configuration or the surrounding systems invalidates them. This statement is not an endorsement of the project, its operators or its commercial prospects, and it is not financial advice. We recommend investors do their own research before investing.

Files and details

Findings and Audit result

medium Issues | 7 findings

Acknowledged

#1 medium Issue
Unconstrained token argument allows fully fabricated transfer events
TruderRouter.sol
L92-122
Description

The token routing function accepts any address as the token and derives everything it reports from that address's own answers. It reads its balance, pulls the funds, reads its balance again, and treats the difference as the amount genuinely received. Measuring the actual balance change is the right technique for real tokens and is what makes this contract correct for fee-on-transfer assets, but it also means a purpose-built contract that reports a fabricated balance change and returns success from transfers that move nothing will cause the router to emit a complete, correctly formed transfer record for value that never moved - including a treasury fee that was never paid. The safe-transfer helper does not help here: it verifies that the call did not revert and that any returned value was true, but it cannot verify that value actually moved. This was demonstrated with a working exploit in which the router emitted an event describing a 900,000 unit transfer with a 100,000 unit fee while moving nothing at all. On-chain nothing is lost, because the fake token belongs to the attacker and no real asset changes hands. The damage sits entirely at the boundary with off-chain systems. These three events are the only record the contract produces, and for a payment router linked to a central treasury they are the natural basis for reconciliation and crediting. If a backend merely displays history, this is log pollution. If it credits a user's balance, marks an invoice as paid, releases goods or triggers a payout when it sees one of these events, then an attacker can mint unlimited fake payment confirmations for the price of gas, which is a direct theft route requiring no flaw whatsoever in the contract's handling of money.

Pending

#2 medium Issue
Permanently fixed administrator with no rotation, pause, timelock or upgrade path
TruderRouter.sol
L15
L38
L46
L50
L54
L131
Description

The treasury address is fixed at construction and cannot be changed for the lifetime of the contract. The entire external surface consists of the two routing functions, the emergency withdrawal, and a getter - there is no function to set or transfer the administrator, no two-step handover, no way to renounce, no pause, no timelock, and the contract is not upgradeable. This was confirmed structurally rather than only by reading the code: calls to plausible ownership-transfer, administrator-setting and pause signatures all fail, because there is no fallback function to absorb them. There are three consequences. If the treasury multisig ever becomes unusable, for instance because two of the three keys are lost, then all future fee revenue is permanently directed at an address nobody controls and the emergency withdrawal becomes permanently unreachable; if instead the multisig is compromised, the attacker's control over that function is equally permanent. If a flaw is found in production, routing cannot be halted, so every user who granted an approval must individually revoke it, which in practice many will not do. And the emergency withdrawal executes immediately with no announcement window, leaving no period in which users could react. The mitigating factor deserves to be stated plainly: the administrator is a two-of-three multisig with no modules rather than a single key, which removes the most common failure mode and is the right choice for this role.

Resolved

#3 medium Issue
A token that freezes the treasury disables the fee path and traps balances permanently
TruderRouter.sol
L68
L81
L87
Description

The treasury is the only possible destination both for the fee leg of a token transfer and for the emergency withdrawal, and the recovery function takes no destination parameter. Major stablecoins can freeze an address unilaterally - both of the largest dollar-pegged tokens expose exactly this capability to their issuers. If the treasury is ever frozen for a given token, then for that token every fee-bearing transfer reverts, and any balance the router happens to be holding becomes permanently unrecoverable, because the destination is hard-wired and the treasury address cannot be changed. This was reproduced end to end: routing worked before the freeze, fee-bearing routing reverted afterwards, fee-free routing continued to work because only the treasury leg is affected, and a balance sitting in the router afterwards could not be swept at all. The result is degraded service plus unrecoverable funds, scoped to the affected token. Users can still route that token with a zero fee, so the payment rail does not die completely, but fee collection for it stops entirely. What elevates the concern is permanence: combined with the fixed administrator, no configuration change can restore access, only a redeployment, and that does not help funds already stranded in the old contract. The likelihood of an issuer freezing the treasury is low and would normally require legal process, but the consequence is total and irreversible for the affected asset.

Pending

#4 medium Issue
The treasury fee is supplied by the caller and has no enforced minimum
TruderRouter.sol
L67
L72
L92
L99
Description

On both routing functions the fee is a plain function argument chosen by the caller, and the contract enforces only an upper bound of ten percent of the gross amount. There is no minimum, no stored rate, and no check that the fee bears any relationship to a rate the treasury expects. The advertised ten percent fee is therefore not a protocol fee at all - it is a ceiling on a fee the caller volunteers. Anyone who builds the transaction data themselves, rather than going through the official front-end, routes value at zero cost, and because both functions are open to everyone and the interface is published on three block explorers this requires no special skill. This was confirmed by test on both the native and the token path: a caller passing a zero fee moved the full amount to the recipient and the treasury received nothing. If fee income is the commercial purpose of these contracts, that income is unenforceable at the contract level and depends entirely on every user voluntarily using the official client. One benign side effect is worth noting: because the fee is chosen per call rather than read from storage, there is no fee-change transaction and therefore no opportunity to front-run a fee update.

Pending

#5 medium Issue
The sole privileged role is held by a single externally owned account rather than the multisig
TruderRouter.sol
Ldeployment configuration; 38
L46
L50
L54
L131
Description

The refactor correctly separated the administrative owner from the fee treasury. The owner was then set to 0xae98C8204fAd33CF242d6D3382D96cA6584a1aD0, which was read live from all three deployments and is a plain externally owned account with no code and no delegation. That account is the deployer and is one of the three signers of the treasury multisig, acting alone. The multisig was kept as the treasury, so it now receives fees but holds no authority over anything. The first review identified the two-of-three multisig as the single strongest control in the deployment; it has been removed from the administrative path. A single key compromise is now sufficient to redirect every future fee in the next block with no second signature and no delay, to halt all routing, to sweep whatever the router holds to an arbitrary destination, and to destroy administrative control permanently. In the previous contract the equivalent sweep could only ever send funds to the multisig, so even a compromised admin could relocate but not steal. User principal is not at risk: the router still has no code path that moves tokens from any account other than the caller, so unlimited approvals cannot be drained by a compromised owner, and the recipient address is caller-specified and cannot be altered by the owner. Both properties were re-verified by test. The exposure is fee revenue, service availability and anything transiently held. The severity reflects a change in likelihood as well as impact: a single hot key is the most commonly realised failure mode in this space, and the previous design had specifically eliminated it. Access control itself is sound - owner-only enforcement was proven symbolically over symbolic callers, selectors and payloads of up to 1024 bytes, and confirmed by eight stateful invariants over 32768 randomised calls each. It is the choice of holder that is the problem.

Pending

#6 medium Issue
Ownership renunciation is live and permanently bricks administration in one transaction
TruderRouter.sol
L15 (inherited
Lnot overridden)
Description

The contract inherits the two-step ownership module, which overrides the transfer function to require a handover but leaves the renounce function exactly as the base module defines it: a single owner-only call that sets the owner to the zero address immediately, with no confirmation step and no way back. The contract does not override it, so it is part of the live interface on all three chains. After one such call the treasury can never be changed, which is precisely the immutable-treasury problem the refactor was built to remove; nothing can ever be swept, so any balance is stranded permanently; the contract can never be paused or unpaused, so the emergency stop that was just added becomes unusable; and no new owner can be appointed, because accepting ownership requires a pending owner and only the owner can set one. The failure is silent, which makes it worse than the original immutability: routing keeps working, users keep paying, and fees keep flowing to a treasury nobody can change, with nothing in the contract's outward behaviour indicating that all recovery capability is gone. Demonstrated by two passing tests. The risk compounds with the single-account owner: an attacker who takes the key and wants maximum damage rather than maximum profit can redirect the treasury and then renounce, locking the redirection in permanently.

Pending

#7 medium Issue
Treasury rotation has no timelock, so fee redirection is immediate and front-runnable
TruderRouter.sol
L54-58
Description

The first review asked for treasury changes to be routed through a timelock. The setter that shipped applies the change immediately. The zero-address check and the event carrying both the old and the new value are both correct and were asked for; what is missing is any delay between announcement and effect. Three consequences follow. Users and integrators learn that the fee destination changed at the same moment it changes, so there is no window in which an unexpected rotation could be noticed and routed around. Because the change lands in the next block and payments are visible in the mempool, the owner - or anyone holding the owner's key - can watch for a large token payment and insert a rotation immediately ahead of it, capturing the fee on that specific payment; the ceiling bounds this at 10 percent of the payment but there is no bound on how targeted it can be. And it removes the main compensating control for the single-account owner, which would be considerably less dangerous if the most valuable thing that key can do had a delay attached to it.

low Issues | 9 findings

Resolved

#1 low Issue
Hostile recipient can multiply the sender's gas cost through an oversized return payload
TruderRouter.sol
L37
L41
L81
Description

The native transfer legs use the common pattern of discarding the second element of the call return tuple. It is widely assumed that this makes the compiler skip copying the returned data, but that is not the case - discarding the element only drops the local variable, and the caller still pays for copying and for the memory expansion. This was measured rather than assumed, by comparing three call shapes against the same recipient. The router's shape cost 10,232,312 gas against a 19,811 gas baseline, and an equivalent call that captures the data explicitly cost 10,232,826 gas, which is within 514 gas of the router's shape and confirms the copy is happening. A raw call with a zero-length output window cost only 5,048,051 gas, isolating roughly 5,184,261 gas of caller-side copying. Driven through the router, a recipient returning 1.9 MB pushed a single payment to 14,623,260 gas, an amplification of well over three hundred times. A hostile recipient can therefore force the payer's transaction over the block gas limit and make payments to that address impossible. No funds are at risk, because the transaction either completes or reverts atomically and value still arrives when it completes, so the practical harm is griefing of the payer plus wasted gas. The concern becomes materially more serious if a relayer, batching or meta-transaction layer is ever added, since one hostile recipient could then exhaust a shared gas budget or fail an entire batch.

Resolved

#2 low Issue
Missing zero-value check on the native path allows anyone to write arbitrary event entries
TruderRouter.sol
L30-44
Description

The token routing function requires the gross amount to be greater than zero, but the native routing function has no equivalent check. Calling the native function with no value attached and a zero fee therefore succeeds - both transfers are zero-value no-ops - and the contract still emits a NativeSent record naming any recipient the caller chooses. Anyone can consequently write arbitrary entries into the router's event log for nothing more than the cost of gas. This pollutes indexers and any reconciliation or analytics built on those events, can be used to inflate apparent activity, and can fill a third party's transfer history with entries that appear to come from the router. It also means the log contains records that correspond to no economic event at all, which complicates reconciliation. The inconsistency with the token path, which does guard this case, suggests the omission was an oversight rather than a deliberate choice.

Resolved

#3 low Issue
Recipient validation rejects only the zero address, so funds can be routed into the contract itself
TruderRouter.sol
L31
L49
Description

Both routing functions check that the recipient is not the zero address and perform no other validation. In particular the recipient is not checked against the contract's own address or against the token address. On the token path, passing the contract's own address as recipient succeeds and quietly converts the caller's funds into a router balance that only the administrator can retrieve, and only to the treasury. Passing the token's own address sends the tokens to the token contract, where they are usually unrecoverable. The two paths also behave differently for the same mistake, which makes the behaviour hard to reason about: on the native path the identical call reverts, because the contract has no function for receiving plain transfers. So the same user error loses funds with tokens but is safely rejected with native currency. The consequence is a loss of user funds through a plausible integration mistake or a malformed front-end request, with recovery entirely at the treasury's discretion, bounded to the amount of a single call.

Acknowledged

#4 low Issue
Reported transfer amount overstates delivery for deflationary tokens and there is no minimum-received guard
TruderRouter.sol
L104-121
Description

The router measures how much it received on the way in, but never how much the recipient received on the way out. With a deflationary or fee-on-transfer token the outbound leg is taxed as well, so the recipient ends up with less than the amount the event reports. Against a token charging five percent on every transfer, the event claimed 855 units while the recipient actually received 812.25 units, a discrepancy confirmed by test. Neither routing function accepts a minimum-received parameter, so a caller cannot express a tolerance and is exposed to whatever tax the token applies, including a rate the token raises between the moment a transaction is submitted and the moment it is included in a block. It is worth being clear that this is a reporting and slippage-protection gap rather than a fund-safety gap: the router itself still ends every call flat, with no dust left stranded, which was verified.

Pending

#5 low Issue
Fee rounding consistently favours the caller rather than the treasury
TruderRouter.sol
L72
L99
L110
Description

The proportional fee is computed with integer division, which floors the result, and the remainder is then handed to the recipient rather than to the treasury. Rounding therefore always runs against the protocol. The multiplication correctly precedes the division, so there is no precision-loss defect here - the only issue is that the rounding direction is the opposite of the usual convention, which is to round protocol-side amounts up and user-claimable amounts down. The practical size of the effect is at most one unit of the token per call, and exactly zero whenever the token is well behaved and the amount received matches the amount requested. A test against a deflationary token showed the treasury receiving 999 units where the nominal fee was 1,000, with the shortfall going to the recipient and never to the router. This is economically negligible and is recorded because the bias is systematic rather than random.

Resolved

#6 low Issue
The token argument is never validated and non-token addresses fail without a reason
TruderRouter.sol
L55-57
Description

The native path explicitly validates its address argument, but the token path performs no validation of the token argument at all and goes straight into an external call to read a balance. When the token argument is the zero address, or any account without code, that balance call succeeds at the virtual machine level with empty return data and the transaction then fails inside the decoder with no reason string attached. Callers and front-ends therefore see an unexplained failure rather than a clear rejection, which makes integration problems needlessly hard to diagnose. There is no direct security consequence, since the transaction does revert and no value moves. It is recorded here partly for the diagnostics and partly because it shares its root cause - a completely unconstrained token argument - with the more serious event-forgery issue reported at medium severity.

Pending

#7 low Issue
The multisig quorum and the only recovery path have never been exercised
TruderRouter.sol
Ldeployment configuration
Description

The treasury is correctly configured as a two-of-three multisig wallet on version 1.4.1 with no modules enabled, and the configuration is identical on all three networks. On-chain state shows, however, that it has never been used. The wallet's transaction counter is zero on Ethereum, Base and BNB Smart Chain, meaning it has never executed a transaction on any network and the emergency withdrawal function - the only privileged function and the only recovery mechanism the contract has - has never been exercised anywhere. One of the three owners has also never signed a transaction on Ethereum and currently holds no native currency, so that key cannot pay for gas at all. A second owner holds roughly 0.000256 in native currency, which was enough to deploy the router but leaves a thin operational margin. The organisation therefore cannot presently demonstrate that it is able to assemble the two signatures needed to move treasury funds or to invoke recovery. Because the treasury address is fixed permanently in the contract, this untested quorum is a genuine operational risk rather than a paperwork matter.

Pending

#8 low Issue
The emergency stop may not be affordable at the moment it is needed
TruderRouter.sol
Ldeployment configuration
Description

The pause is the main operational capability the refactor added, and the only account that can use it is the owner. Its balances are thin. A pause call estimated against the live contract costs 47218 gas, which gives a break-even gas price above which the owner cannot afford to send the transaction at all: roughly 4.14 gwei on Ethereum against a balance of 0.000195 ETH, 8.13 gwei on Base against 0.000384 ETH, and 0.28 gwei on BNB Smart Chain against 0.0000131 BNB. At the prices observed during this review the owner can pause on every chain, but 4.14 gwei is an ordinary Ethereum gas price rather than an extreme one, and 0.28 gwei is below levels BNB Smart Chain has sustained for long periods. The failure mode is particularly unfortunate because congestion and on-chain incidents correlate: the moment an emergency stop is most likely to be needed is also the moment it is most likely to be unaffordable. This is not a code defect, and it is recorded because the pause was added specifically to answer the first review's observation that there was no way to halt the contract.

Pending

#9 low Issue
The recovery sweep now accepts an arbitrary destination
TruderRouter.sol
L131-147
Description

The previous recovery function always sent to the immutable admin. The new one sends wherever the owner names. The change was necessary and was recommended in the first review, and it is the reason the frozen-treasury finding is now closed, but it does change the worst case: previously a compromised admin could only relocate funds to the multisig, and now a compromised owner can take them. The exposure is bounded and small in normal operation, because the router is an atomic pass-through that holds nothing; the realistic loss is limited to funds that arrived outside the routing functions, such as mistaken direct transfers, value sent with the payable constructor, or tokens delivered by a third party. It becomes material only in combination with the single-account owner.

optimization Issues | 6 findings

Resolved

#1 optimization Issue
The deployed build has the optimizer disabled
TruderRouter.sol
L1-91
Description

The verified compiler settings published on all three explorers report the optimizer as disabled, so the code currently running on Ethereum, Base and BNB Smart Chain is unoptimised. This is by far the largest single cost improvement available, and it affects both the one-off deployment cost and the cost of every payment the contract ever processes. For a contract intended to serve high-frequency payments, shipping an unoptimised build means every user pays more gas than necessary on every transaction.

Resolved

#2 optimization Issue
Revert strings instead of custom errors
TruderRouter.sol
L20
L25
L31
L32
L38
L42
L49
L52
L53
L80
L82
L86
Description

All eleven failure paths use string reason messages, which are stored in full in the deployed bytecode. One of them is thirty-eight bytes long and therefore spans two machine words, costing more than the rest. Custom errors reduce the deployed contract size, cut the gas consumed on the revert path, and allow structured parameters to be attached so that callers can tell precisely what went wrong.

Resolved

#3 optimization Issue
The fee ceiling arithmetic can be reduced to a single division
TruderRouter.sol
L32
L53
Description

The fee ceiling is written as a multiplication by ten followed by a division by one hundred. Because integer division floors, this produces exactly the same result as a single division by ten for every possible input, so the multiplication is redundant. Removing it replaces two arithmetic operations with one and also removes the compiler's overflow check on the multiplication. That has a useful side benefit: it eliminates the only theoretical overflow path in the contract, which a static analysis tool reported and which had to be dismissed after separate work proved it could not lead to an incorrect fee.

Resolved

#4 optimization Issue
Constructor is not marked payable
TruderRouter.sol
L24
Description

A constructor that is not marked payable causes the compiler to emit a check rejecting any value sent alongside the deployment transaction. Marking it payable omits that check and saves a small amount of gas at deployment time.

Resolved

#5 optimization Issue
Reentrancy protection can use transient storage
TruderRouter.sol
L10
Description

The reentrancy guard currently in use writes to contract storage before and after every protected call. All three target networks now support transient storage, which is discarded automatically at the end of the transaction and is considerably cheaper to write. Moving to the transient variant saves roughly two thousand to five thousand gas on every payment the contract processes. The library authors have additionally marked the storage-based guard as deprecated and scheduled for removal in the next major version, so this change is also future-proofing.

Pending

#6 optimization Issue
Making the treasury mutable costs a cold storage read on every routing call
TruderRouter.sol
L77
L114
L118
Description

The previous contract held the fee destination in an immutable, which is free to read. The current one reads a storage slot, at 2100 gas cold. This is the correct trade: the first review asked for a rotatable treasury, and that capability is worth far more than 2100 gas. It is recorded only so the number is on file, and it is already included in the measured net savings, which remain substantial on every path.

informational Issues | 14 findings

Pending

#1 informational Issue
No documentation comments anywhere in the contract
TruderRouter.sol
L22-32
L46-58
Description

Not a single function, event, parameter or return value carries a documentation comment. For a contract that third parties are expected to integrate against, and whose amount and fee parameters mean different things on the native path than on the token path, this is the single most valuable documentation improvement available. The absence is what turns the differing parameter semantics from a minor inconsistency into a genuine integration hazard.

Resolved

#2 informational Issue
Fee ceiling written as inline magic numbers
TruderRouter.sol
L32
L53
Description

The ten percent fee ceiling is written inline as a multiplication by ten and a division by one hundred, at two separate places, with no named constant. The policy is therefore invisible to anyone reading the contract on a block explorer, and the two sites can drift apart if one is edited and the other is not.

Resolved

#3 informational Issue
Floating compiler version pragma
TruderRouter.sol
L2
Description

The version pragma permits any compiler release from 0.8.20 upwards. The deployed artefacts were built with exactly 0.8.20, which was confirmed against the on-chain bytecode, but a future redeployment from the same source could silently pick up a different compiler with different code generation and therefore different behaviour or gas characteristics.

Resolved

#4 informational Issue
Default compiler target emits an opcode not available on every chain
TruderRouter.sol
L2
Description

The compiler version in use defaults to a target that includes an opcode introduced by a relatively recent network upgrade, and the verified metadata confirms the default was used. All three current networks support it, so this has no effect today and the deployed bytecode verifies correctly on each of them. It becomes a real hazard only if the same source is redeployed to a network that has not adopted that upgrade, where the contract would either fail to deploy or behave incorrectly.

Resolved

#5 informational Issue
Reentrancy modifier is not applied first
TruderRouter.sol
L77
Description

The emergency withdrawal function lists its access-control modifier before the reentrancy guard, so the access check runs first. The recommended ordering puts the reentrancy guard first, so that no other modifier can execute during a reentrant call. In this specific contract the ordering is harmless, because the access-control modifier is a single comparison against an immutable value and makes no external call, so there is nothing reentrant for it to do.

Resolved

#6 informational Issue
Access-control modifier is used only once
TruderRouter.sol
L19-22
Description

The access-control modifier is applied to exactly one function. It therefore adds a layer of indirection for a single call site and, in an unoptimised build such as the one currently deployed, results in duplicated inline code without any corresponding benefit.

Resolved

#7 informational Issue
Constructor emits no event for the administrator assignment
TruderRouter.sol
L24-27
Description

The administrator is assigned silently in the constructor, so there is no log entry establishing the treasury at deployment time. Off-chain systems must either decode the constructor arguments from the deployment transaction or call the getter, neither of which fits naturally into an event-driven indexing pipeline. Every other state-changing operation in the contract does emit an event, so this is the one gap in otherwise complete event coverage.

Resolved

#8 informational Issue
The reentrancy guard in use is deprecated upstream
TruderRouter.sol
L10
Description

The library authors have marked the storage-based reentrancy guard as deprecated in favour of a transient-storage variant, and have stated it will be removed in the next major version. All three target networks now support transient storage. Continuing with the deprecated version means a future library upgrade will require a code change, and it forgoes a straightforward gas saving in the meantime.

Resolved

#9 informational Issue
Recovery function reverts instead of doing nothing when the balance is zero
TruderRouter.sol
L80
L86
Description

The recovery function rejects the call when the relevant balance is zero, for both native currency and tokens. This turns a harmless no-operation into a failure, which means an operational script that sweeps several assets in sequence will abort partway through the first time it encounters an empty balance.

Pending

#10 informational Issue
Inconsistent parameter meanings between the native and token paths
TruderRouter.sol
L67
L92
Description

On the native path the caller supplies the gross value and the recipient receives that value minus the fee. On the token path the caller supplies the net figure destined for the recipient and the contract pulls that figure plus the fee. The same nominal fee therefore means something different on each path, and an integrator who reasons by analogy from one to the other will get the amounts wrong. Given the complete absence of documentation comments, this is a real hazard rather than a stylistic quibble.

Resolved

#11 informational Issue
Low-level calls used for native transfers
TruderRouter.sol
L37
L41
L81
Description

Native transfers are performed with raw low-level calls rather than a higher-level helper, which static analysis flags by default. In this contract the pattern is appropriate and necessary: a raw call is the only way to pay arbitrary recipients including contract wallets, and the success flag is checked on every one of the three call sites, so the usual concern about silently ignored failures does not apply. This entry records the observation and its resolution so that the finding is not simply dropped. The separate and genuine consequence of this same idiom, namely that returned data is still copied at the caller's expense, is reported at low severity.

Pending

#12 informational Issue
The published documentation is factually wrong in two places
TruderRouter.sol
L11-14
L34-37
Description

The constructor comment states that the initial owner is the address that can pause and upgrade the contract. The contract is not upgradeable: there is no proxy, no delegated call and no initializer. This was confirmed by the upgradeability checker, which reports no initialization schema, independently by reproducing the deployed runtime bytecode exactly from a direct build of the source, and by a test in which a call to an upgrade selector fails because no such function and no fallback exists. Separately, the contract-level comment describes a permissionless payment router with a 10 percent maximum fee. There is no 10 percent fee: there is a caller-supplied fee with a 10 percent ceiling and no floor, and anyone calling the contract directly pays nothing at all. Both texts are embedded in the verified source on three block explorers, so they are the version of the documentation most third parties will actually read. One overstates the owner's powers; the other invites integrators to assume the protocol collects a fee it does not enforce.

Pending

#13 informational Issue
The ABI backward-compatibility claim holds only for the two routing functions
TruderRouter.sol
LABI
Description

The claim is accurate and important for the mobile applications: both routing function signatures are unchanged, so no forced app release is required, and that was the client's stated goal. It is not true of the contract as a whole. The admin getter and the single-argument recovery function were removed, and the recovery event gained an indexed destination parameter, which changes its topic hash. Any indexer, dashboard, back-office script or monitoring rule that reads the admin getter or decodes the old event will fail silently against the new deployments.

Pending

#14 informational Issue
The superseded deployments are still live and still callable
TruderRouter.sol
Ldeployment
Description

The three previous routers remain deployed, unpaused and fully functional, and they have no pause and no way to rotate their immutable admin. Any user, integration, QR code, saved address or documentation page still pointing at them will keep working, and will keep sending fees to the old immutable admin with none of the new protections. Because the old contracts cannot be paused, no on-chain mitigation is available.