Skip to content
$1,000,000 in security audit grants are live now, Apply here →

Security review · December 2025

Protocol Review

for Nashpoint

Nashpoint engaged Guardian to review the security of their Nashpoint Contracts. From the 27th of October to the 13th of November, a team of 5 auditors reviewed the source code in scope.

Published
Review window
October 27 to November 13, 2025
Rounds
Main Review, Remediation Review
Language
Solidity
Chains
Arbitrum
Sector
Real-world assets, Yield and vaults
  • 0 Critical
  • 2 High
  • 14 Medium
  • 30 Low
  • 20 Informational

35 resolved · 1 partially resolved · 30 acknowledged

Scope

Overview

Nashpoint engaged Guardian to review the security of their Nashpoint Contracts. From the 27th of October to the 13th of November, a team of 5 auditors reviewed the source code in scope.

Findings 66

Main Review

60 findings
  1. H-01 High Nodes Cannot Be Used In Defi Protocols Compatibility Resolved
    Location
    Node.sol: 621-638
    Round
    Main Review

    Description

    Proof of concept: PoC

    The transfer, transferFrom, and approve functions in the Node contract are supposed to return boolean values.

    However, these overridden functions do not explicitly return any value and always return false, even though super.transfer/super.transferFrom executes successfully.

    As a result, Nodes can never be used in DeFi protocols that utilize safe libraries like Solmate’s SafeTransferLib or OpenZeppelin’s SafeERC20, which breaks a core feature of Nodes.

    Recommendation

    Return true in these functions if they execute successfully.

    Resolution

    Nashpoint Team: The issue was resolved in commit 48b7373.

  2. H-02 High Locked Tokens In Adapter Due To Duplicate Hash DoS Resolved
    Location
    DigiftEventVerifier.sol: 261-264
    Round
    Main Review

    Description

    Proof of concept: PoC

    The SubRedManagement contract enables multi-token deposits from investors in exchange for security tokens stToken. The Nashpoint protocol handles this via the beacon proxy pattern, deploying different instances of DigiftAdapter contracts.

    Once DigiftAdapter request a deposit or redemption, an admin from the SubRedManagement contract finalizes the process by calling settleSubscriber or settleRedemption. At this point, a settleSubscriber or settleRedemption event is emitted, which the Manager role from Nashpoint verifies through DigiftEventVerifier.verifySettlementEvent function.

    The event verifier prevents double spending by hashing the blockHash, receiptsRoot, transaction index, log index and reverting if the hash was already used.

    The issue is that multiple DigiftAdapter contracts can have the same used hash logged for a valid transaction if they are included in the investorList of the same event. However, since the hash has already been used, this will completely DoS the event verification for the latter adapters, resulting in locked funds.

    Consider the following example:

    Two DigiftAdapter contracts exist: Adapter A and Adapter B. requestDeposit is executed on both adapters for 20,000 USD each. Admin forwards this request forwardRequestsToDigift to SubRedManagement.

    SubRedManagement finalizes this request via settleSubscriber, emitting the SettleSubscriber event which includes both Adapter A and Adapter B investment and share token quantities.

    To finalize this settlement, the Manager starts with calling settleDeposit on Adapter A, providing the offchain and onchain parameters to verify that the SettleSubscriber event was emitted to include Adapter A in the list of investors and share tokens minted.

    The event is then verified, vars.logHash = _hashLog(vars.blockHash, vars.receiptsRoot, fargs.txIndex, i); is calculated, and usedLogs[vars.logHash] = true; is updated.

    Next, the Manager calls settleDeposit on Adapter B. Since Adapter B's subscription event was grouped with Adapter A's subscription event, it will contain the same block hash, receipt root, transaction index, and log index i. This means that it will produce the exact same log hash that was calculated for Adapter A, causing revert LogAlreadyUsed();.

    Thus, the event cannot be verified, and funds are subsequently locked in the SubRedManagement contract.

    Recommendation

    Include the DigiftAdapter addresses in the hash to ensure adapter uniqueness.

    Resolution

    Nashpoint Team: The issue was resolved in commit db926f6.

  3. M-01 Medium Wrong requestRedeem Authorization Per Spec Compatibility Resolved
    Location
    Node.sol: 792-799
    Round
    Main Review

    Description

    The current _validateOwner implementation incorrectly requires operators to have ERC-20 approval and rejects approved spenders who aren't operators when requestRedeem, violating ERC-7540.

    Per the EIP-7540 spec, operators should be able to execute redemptions without allowance restrictions, while approved spenders (non-operators) should be able to request redemptions within their approved share limit.

    Recommendation

    Spend allowance only when the spender is not operator.

    function _validateOwner(address owner, uint256 shares) internal {
    if (owner != msg.sender && !isOperator[owner][msg.sender]) {
    _spendAllowance(owner, msg.sender, shares);
    }
    

    Resolution

    Nashpoint Team: The issue was resolved in commit 7ff2c88.

  4. M-02 Medium Wrong maxClaimableAssets Can DoS Fulfillments DoS Resolved
    Location
    ERC7540Router.sol: 69
    Round
    Main Review

    Description

    In erc7540Router.fulfillRedeemRequest, when no component can fully cover assetsRequested, the router enters the partial path and computes:

    claimableShares = claimableRedeemRequest(0, node)

    maxClaimableAssets = convertToAssets(claimableShares)

    convertToAssets can return an inflated amount (cause it calculates the asset with current shares exchange rate, which will be higher than the value of shares at settlement often) vs the actual withdrawable assets (maxWithdraw(node)), so min(assetsRequested, maxClaimableAssets) will exceed maxWithdraw(node), causing the withdrawal to revert when calling components.withdraw and blocking fulfillment.

    Recommendation

    Use maxWithdraw instead of convertToAssets(claimableShares)

    Resolution

    Nashpoint Team: The issue was resolved in commit 2c458ec.

  5. M-03 Medium Rebalance Deadlock Due To Management Fee DoS Resolved
    Location
    Node.sol: 339
    Round
    Main Review

    Description

    startRebalance always attempts to pay accrued management fees from the Node’s on-hand reserve before opening the rebalance window.

    There isn't a balance or reserve ratio enforcement in Nodes, and some Nodes may have all of their balance invested in other components and have zero reserves.

    If the Node's balance is less than the computed feeForPeriod, _payManagementFees() and startRebalance reverts, preventing the rebalance window from ever opening.

    Since routers can only move funds during the rebalance window, operators cannot free up reserve to pay the fee, causing a deadlock where actions dependent on rebalancing are DoS’d.

    Recommendation

    Consider setting a minimum reserve ratio or buffer amount that cannot be invested in components, ensuring sufficient funds are available to cover fees.

    Alternatively, consider a mechanism that pulls from components to cover fees when the balance is insufficient (e.g. atomically liquidate remainder during startRebalance).

    Resolution

    Nashpoint Team: The issue was resolved in commit 5715448.

  6. M-04 Medium User Can Claim Others’ Queued ERC7540 DoS Acknowledged
    Location
    ERC7540Router.sol: 61-62
    Round
    Main Review

    Description

    Once users request withdrawals from the Node contract, the Rebalancer role can proceed to request a withdrawal from the ERC7540 vault (via ERC7540Router.requestAsyncWithdrawal), if an ERC7540 vault is used for investments by the Node.

    After the request is finalized by the external ERC7540 vault contract (which can take days), the Rebalancer role can proceed to fulfill the redemption request of users via ERC7540Router::fulfillRedeemRequest.

    The problem is that a user can timely execute a withdrawal request just before the Rebalancer role calls ERC7540Router.fulfillRedeemRequest, taking up other users' requested redemption for that rebalancing window, causing another withdrawal delay for users which can be at least another 1-2 days, or more.

    Consider the following example:

    • Bob and Alice both request a withdrawal from the Node contract, each requesting 100 shares to withdraw.
    • Rebalancer role initiates a rebalance via startRebalance, then proceeds to call

    ERC7540Router.requestAsyncWithdrawal with 200 shares specified.

    • The component takes 48 hours to finalize the request.
    • Bob decides instead of withdrawing only 100 shares, he wants to withdraw a full 200 shares. Rather than waiting for

    the Rebalancer to call ERC7540Router.fulfillRedeemRequest followed by another 100 shares withdrawal request, he realizes Alice has already queued 100 shares.

    • Bob takes advantage and timely requests another withdrawal via Node.requestRedeem specifying another 100

    shares just before the next rebalancing window (as the rebalancing time is public).

    • Now when the rebalancing window is initiated and Rebalancer calls ERC7540Router.fulfillRedeemRequest with Bob's

    address, Bob will receive the full 200 shares that was queued for withdrawal, because now his total assets requested is updated.

    • Therefore, Alice will receive nothing.

    Alice now has to wait for another ERC7540Router.requestAsyncWithdrawal call followed by ERC7540Router.fulfillRedeemRequest for her 100 share withdrawal, which can take days or longer.

    Recommendation

    A potential solution could be to separate withdrawal requests into epochs denoted by an epochId, so if any user attempts to request a withdrawal last second, it will reflect the withdrawal request for the next epoch rather than the current one.

    Resolution

    Nashpoint Team: Acknowledged.

  7. M-05 Medium Owner Can Drain All Funds Via rescueTokens Trust Assumptions Resolved
    Location
    Node.sol: 321-326
    Round
    Main Review

    Description

    rescueTokens() allows the owner to recover accidentally sent tokens, blocking only the underlying asset and component share tokens. The implementation assumes that the share address of any component is the component address itself:

    function rescueTokens(address token, address recipient, uint256 amount) external onlyOwner {
    if (token == asset) revert ErrorsLib.InvalidToken();
    >>   if (_isComponent(token)) revert ErrorsLib.InvalidToken();
    IERC20(token).safeTransfer(recipient, amount);
    emit EventsLib.RescueTokens(token, recipient, amount);
    }
    

    However, for ERC7540 components, the share token of a component (obtained via IERC7575(component).share()) can differ from the component address itself, as seen in ERC7540Router._requestRedeem(), so the owner can withdraw shares for any component that has a share token different from its address.

    Example how owner can drain all users funds:

    • Owner whitelists an ERC7540 component where share() != component
    • Sets allocation to 100% and invests all node funds into that component
    • Calls rescueTokens(shareToken, owner, balance) to sweep all share tokens (bypasses the

    _isComponent check)

    • All user funds are drained

    Recommendation

    Add a loop in rescueTokens() to also block IERC7575(components[i]).share() for each component , you may consider doing it via try catch to avoid reverts from components that doesn't have share() function

    Resolution

    Nashpoint Team: The issue was resolved in commit d54c056.

  8. M-06 Medium Whitelist Bypass Via EIP7702 Gaming Resolved
    Location
    GatePolicy.sol: 22
    Round
    Main Review

    Description

    With EIP-7702, a whitelisted user can set their code to allow non-whitelisted users to interact with the protocol, effectively bypassing the GatePolicy.

    Consider this scenario:

    • Bob is whitelisted and Alice is not.
    • Bob sets his account to interact with a permissioned Node like a router.
    • Bob's account takes Alice's assets, deposits to Node, and transfers shares to Alice.
    • Since the msg.sender in this case is Bob, whitelist checks pass and any non-whitelisted user can

    interact with Nodes.

    Note that this scenario does not work with TransferPolicy, as the funds must ultimately be transferred to or from a non-whitelisted user, which the policy prevents.

    Recommendation

    One option is to recommend using TransferPolicy and GatePolicy concurrently for all node owners, as GatePolicy alone does not prevent end-to-end interaction.

    Another option would be to use tx.origin as well along with msg.sender for the whitelist check in GatePolicy. However, this may be overly restrictive or introduce new issues, especially if some node owners want to implement custom routers that are whitelisted for their own nodes.

    Alternatively, consider tracking users off-chain and removing from the whitelist those who allow others via EIP-7702.

    Resolution

    Nashpoint Team: The issue was resolved in commit 37f402f.

  9. M-07 Medium Cannot Support Hybrid Asynchronicity DoS Acknowledged
    Location
    ERC7540Router.sol: 283-294
    Round
    Main Review

    Description

    The _getErc7540Assets function in the ERC7540Router contract assumes that all ERC7540 components are fully asynchronous in both deposits and redemptions, and directly calls the pendingRedeemRequest, claimableRedeemRequest, pendingDepositRequest, and claimableDepositRequest functions.

    However, according to the ERC7540 specifications, implementations can choose whether to include asynchronous flows for deposits, redemptions, or both.

    For example, the Node itself is ERC7540-compatible but does not implement the pendingDepositRequest or claimableDepositRequest functions.

    Directly calling all four of these functions in _getErc7540Assets could cause a DoS when the component is partially asynchronous, as is the case with the Node.

    Recommendation

    Use try/catch when calling ERC7540 components in the ERC7540Router. Alternatively, ensure all components supported by the protocol are fully asynchronous and never include hybrid components.

    Resolution

    Nashpoint Team: Acknowledged.

  10. M-08 Medium Wrong ERC-7540 Claimable Share Valuation Logical Error Resolved
    Location
    ERC7540Router.sol: 285-289
    Round
    Main Review

    Description

    _getErc7540Assets() values claimable redeem shares at the current share price via convertToAssets(), but claimable shares represent a locked-in redemption value from when the request was processed. From erc7540 spec:

    "The assets that will be received on redeem or withdraw MAY NOT be equivalent to the value of convertToAssets(shares) at the time of Request, as the price can change between Pending and Claimed"

    Example:

    • Node requests redeem of 100 shares when share price = 1.0 → locked value = 100 assets
    • Request processed and becomes claimable (100 shares → 100 assets)
    • Component NAV increases → new share price = 1.2
    • _getErc7540Assets computes: convertToAssets(100 claimable shares) = 120 assets
    • Actual claimable via maxWithdraw(): 100 assets
    • Overvaluation: 20 assets (20%)

    This can inflate or reduce the actual totalAsset, leading to incorrect share valuation and erroneous node share calculations.

    Recommendation

    Consider using maxWithdraw() for claimable shares. However, there is a tradeoff here and keep in mind that it may underestimate assets if vault has some withdrawal restrictions (e.g., withdraw cap per transaction, paused vault returns 0).

    Resolution

    Nashpoint Team: The issue was resolved in commit 91db03d.

  11. M-09 Medium Owner Can Drain User Funds Via Swing Pricing Trust Assumptions Resolved
    Location
    Node.sol: 25
    Round
    Main Review

    Description

    Proof of concept: PoC

    Owner can drain all users funds by exploiting the bonus mechanism , and the fact that he can switch swing pricing on and off at will with other important params.

    Attack Steps: 1. Set rebalanceCooldown = 0 and rebalanceWindow = 1 sec (no lower bounds enforced) : this to faster the attack 2. Invest all reserves to vault (0% reserve ratio) 3. Enable max swing pricing (99%) and set extreme reserve target (90%) 4. Owner's secondary address deposits to capture ~49.5% bonus shares (dilutes existing users) 5. Disable swing pricing to exit without penalty 6. Liquidate vault and redeem owner's shares at inflated value 7. Repeat each block until node is drained

    Recommendation

    You may consider queueing activation/deactivation of swing pricing to take effect in future to limit the owner power, so users can opt out and exit the system.

    Additionally, consider having global lower bounds for rebalanceCooldown and rebalanceWindow to decrease the likelihood of such attack by malicious owner.

    Resolution

    Nashpoint Team: The issue was resolved in commit 6259916.

  12. M-10 Medium Users May Be Over-penalized For Withdrawals Logical Error Resolved
    Location
    QuoterV1.sol: 88-110
    Round
    Main Review

    Description

    When swing pricing is enabled, users' withdrawal may face the penalty. According to https://nashpoint.gitbook.io/nashpoint/swing-pricing-calculations, when reserves fall below target, withdrawals receive progressively worse pricing.

    The problem here is that if one withdrawal starts when the reserve ratio is larger than the target reserve ratio, and ends with the reserve ratio is less than the target reserve ratio, the whole withdrawal assets will be punished.

    For example: 1. Current reserve cache ratio is 100%. Target reserve ratio is 10%. 2. Alice wants to request all assets, Then all assets will be punished with the final swing factor. In fact, when Alice requests withdrawal for the first 90% asset, the node should not charge some punish fees.

    When users deposit assets, users may get some bonus when the reserve ratio is below the target reserve ratio. Similar issue in deposit, if one deposit operation will raise the reserve ratio from one ratio below the target reserve ratio to one ratio above the target ratio, the system will return the min of deposit and asset shortfall to avoid overpaying the deposit bonus.

    When users request withdrawal, one similar mechanism should be applied.

    Recommendation

    When one withdrawal request will drop the reserve ratio from the ratio which is above target ratio to the ratio which is below the target ratio, the punish amount should be calculated in the range from target reserve ratio to the actual reserve ratio after the withdrawal.

    Resolution

    Nashpoint Team: The issue was resolved in commit 6259916.

  13. M-11 Medium Split Withdrawals To Reduce Penalty Math Resolved
    Location
    QuoterV1.sol: 88-110
    Round
    Main Review

    Description

    Proof of concept: PoC

    When swing pricing is enabled, users' withdrawal may be punished when the reserves fall below target.

    However, the penalty calculation is non-linear. When users split their withdrawal into several smaller transactions, they end up paying a lower total penalty than expected.

    Recommendation

    Refactor the penalty calculation.

    Resolution

    Nashpoint Team: The issue was resolved in commit 6259916.

  14. M-12 Medium Redeem Asset Impacted Math Resolved
    Location
    Node.sol: 436-459
    Round
    Main Review

    Description

    Proof of concept: PoC

    When swing pricing is enabled, users' redeem request may face some punishment when current reserve ratio is below than the target ratio.

    When the rebalancer finalizes a redemption request, all related shares are burned, and the actual return asset is calculated based on sharesAdjusted. After the redemption is finalized, the share price increases slightly.

    Therefore, the order of redemption finalizations is important. In this case, later redemption finalizations will receive a higher price. Because the order has importance, the rebalancer should finalize redemption requests in the order they were originally submitted.

    The problem is that a single user can submit multiple redemption requests, and the Node contract merges all of them together. When the rebalancer attempts to fulfill redemptions for a controller, it becomes impossible to finalize these requests in their original submission order.

    Recommendation

    Do not merge a single user’s multiple redemption requests. The rebalancer should fulfill redemption requests in the order they were originally submitted.

    Resolution

    Nashpoint Team: The issue was resolved in commit 6259916.

  15. M-13 Medium Incorrect reserveImpact Math Resolved
    Location
    QuoterV1.sol: 105
    Round
    Main Review

    Description

    The reserveImpact is computed using the pre-penalty asset value, but the penalty reduces the assets. Therefore, the actual reserveImpact after the redemption differs from the one used during the penalty calculation. As a result, users are always over-penalized.

    //...
    } else {
    reserveImpact = int256(Math.mulDiv(reserveCash - assets, WAD, totalAssets - assets));
    }
    assets = Math.mulDiv(assets, (WAD - _getSwingFactor(reserveImpact, maxSwingFactor,
    targetReserveRatio)), WAD); //@audit returned asset value is decreased.
    

    For this calculation to be precise, the asset value after the penalty is applied should be the same value used when calculating the reserveImpact.

    Recommendation

    Consider assetsToReturn as the final value. We need to solve the equation where:

    reserveImpact = int256(Math.mulDiv(reserveCash - assetsToReturn, WAD, totalAssets -
    assetsToReturn));
    assetsToReturn = Math.mulDiv(assets, (WAD - _getSwingFactor(reserveImpact, maxSwingFactor,
    targetReserveRatio)), WAD);
    

    Note that this introduces additional complexity, as the reserveImpact needed to compute the final value is itself unknown and circularly dependent on the final value.

    If the additional complexity is undesirable and the current version will be used, this should be documented, as it creates unfair situations for high-value redemption requests.

    Resolution

    Nashpoint Team: The issue was resolved in commit 6259916.

  16. M-14 Medium Share Price May Become Inflated Rounding Resolved
    Location
    Node.sol: 746-770
    Round
    Main Review

    Description

    Proof of concept: PoC

    When the swing pricing is enabled, users' withdrawal operation may be punished, the protocol will burn all shares for this withdrawal and leave some assets in the node contract as one punishment.

    If the last user requests to redeem all shares, and the rebalancer role will start rebalance and finalize the redeem. After the redeem finalization, share amount will be 0, and cacheTotalAssets will not be reduced to 0.

    Based on current condition, if another user wants to deposit, the share's price will be very high(current share = 0, cacheTotalAssets might be larger than 1e18).

    In Nashpoint, the impact of rounding (down or up) on calculations is typically around 1 wei when the share price is not inflated, which is acceptable. However, if the share price is inflated, such rounding can result in significant losses for users.

    Recommendation

    Consider enforcing Node owners to mint some dead shares upon Node deployment.

    Resolution

    Nashpoint Team: The issue was resolved in commit 6259916.

  17. L-01 Low Missing Slippage Control Validation Acknowledged
    Location
    Node.sol: 495
    Round
    Main Review

    Description

    When swing price is enabled and the vault's liquidity reserve is less than the targetReserveRatio, users are incentivized to deposit via a deposit bonus, which mints users more shares than normal.

    However, the bonus calculation uses various factors that can change before function execution such as cash after redemptions, totalAssets(), and owner configurable parameters such as maxSwingFactor and targetReserveRatio. This can cause users to receive a lower bonus than expected, or receive no bonus at all.

    Slippage when exact output amount is not received for deposit/withdraw is also recommended under "Security Considerations" within EIP-4626 documentation.

    Similarly, the requestRedeem function does not have slippage control, and users may encounter unexpected penalties when swing pricing is enabled.

    Recommendation

    Add slippage control for these functions.

    Resolution

    Nashpoint Team: Acknowledged.

  18. L-02 Low Users May Pay More Management Fee Logical Error Acknowledged
    Location
    Node.sol: 377-395
    Round
    Main Review

    Description

    Management fees are paid at the beginning of each rebalance period after cacheTotalAssets are updated. For a default 24-hour rebalancing node, this uses the end-of-day asset value for the entire 24-hour period, regardless of when deposits are made during the day.

    If a large deposit is made just a second before rebalancing, the system still charges fees as if the funds were managed for the entire 24-hour period.

    While the current behavior is similar to fund management in traditional finance, and fees are regularly paid during rebalancing, there is no guarantee that rebalancing will occur frequently enough to make this discrepancy negligible.

    Since nodes are permissionless and rebalancing periods can be set by their owners, the issue can be more pronounced, especially when nodes have longer rebalancing periods, such as weeks.

    Recommendation

    Ideally, every state change that updates cacheTotalAssets should charge fees based on the latest cached value and the period since the lastPayment. However, this would increase complexity.

    If the end-of-day value will still be used for fee payments, document this behavior. Additionally, consider enforcing maximum bounds for rebalancing cooldowns or adding a function that allows a protocol-owned rebalancer to charge fees more frequently, reducing discrepancies even if the node has a longer rebalancing period.

    Resolution

    Nashpoint Team: Acknowledged.

  19. L-03 Low Fee Calculation Oversight Logical Error Acknowledged
    Location
    Node.sol: 307-311
    Round
    Main Review

    Description

    The setAnnualManagementFee function updates the fee rate without first calculating and paying accrued fees for the period from lastPayment till now at the old rate. This results in miscalculated fees using the new rate for past periods.

    A similar situation occurs when updating protocol management fee with setProtocolManagementFee function as well.

    Recommendation

    Pay management fees with previous fee percent and update the last payment before setting the new fee

    Resolution

    Nashpoint Team: Acknowledged.

  20. L-04 Low Indexed Dynamic Arrays In Events Not Supported Events Resolved
    Location
    EventsLib.sol: 121-123
    Round
    Main Review

    Description

    PoliciesAdded and PoliciesRemoved events declare bytes4[] indexed sigs. However, Solidity does not support indexed dynamic array parameters in events. When indexed attribute is used in these types, hash of the value is stored in topics.

    Reference: "A topic can only hold a single word (32 bytes) so if you use a reference type for an indexed argument, the Keccak-256 hash of the value is stored as a topic instead."

    As a result, these events are emitted with the hash of the sigs array, and might be unexpected for offchain listeners.

    For example, the emitted event in the test_addPolicies test in the Node.t.sol file is:

    emit PoliciesAdded(sigs:0xfb5baaecab62c516763cea2dfba17fbbc24907e4e3b0be426bde71be89af495f, policies: [0x0000000000000000000000000000000000000012])

    Recommendation

    Consider removing the indexed attribute from these events or emitting per-element events.

    Resolution

    Nashpoint Team: The issue was resolved in commit 500d450.

  21. L-05 Low Whitelist And Blacklist Flags Can Conflict Logical Error Acknowledged
    Location
    BaseComponentRouter.sol: 44-57
    Round
    Main Review

    Description

    The admin setters in BaseComponentRouter allow a component to be both whitelisted and blacklisted simultaneously. This inconsistent state can cause policy bypass in downstream code that only checks one flag and makes the intended component status ambiguous.

    Recommendation

    Enforce mutual exclusivity in setters: when setting blacklist to true, force whitelist to false (and vice versa).

    Resolution

    Nashpoint Team: Acknowledged.

  22. L-06 Low Owner Updates Should Effect In Future Unexpected Behavior Acknowledged
    Location
    Node.sol: 278-287
    Round
    Main Review

    Description

    The node owner can change the rebalance window during an active rebalance or adjust the cooldown period while in an active cooldown.

    However, ideally, these changes should only affect future rebalance windows and cooldown periods, and the owner should not be able to increase or decrease the current period without notice.

    Recommendation

    Do not allow the owner to change the rebalance window or cooldown period when the Node is already in that particular state.

    Resolution

    Nashpoint Team: Acknowledged.

  23. L-07 Low Malicious Rebalancer Can Steal Incentive Tokens Trust Assumptions Acknowledged
    Location
    OneInchV6RouterV1.sol: 111-154
    Round
    Main Review

    Description

    The swap() function in OneInchV6RouterV1 allows the rebalancer to set minAssetsOut to any value, including zero, with no oracle price validation, this allow the rebalancer to steal all incentive tokens.

    Example:

    • Node has 1000 USDC worth of incentive tokens accumulated
    • Malicious rebalancer manipulates swap pool[asset-incentivetoken] via flash loan
    • Malicious rebalancer calls swap() with minAssetsOut = 0
    • Swap executes at manipulated rate, node receives ~0 assets back
    • Rebalancer back run to swap back , extract the whole incentive , and return the flashloan
    • Loss: up to 100% of incentive value stolen

    Recommendation

    Either use whitelisted oracles to validate the minAmountOut , or maybe only owner should be able to do swap (since owner can sweep incentive tokens anyway , so i assume he's trusted on that )

    Resolution

    Nashpoint Team: Acknowledged.

  24. L-08 Low Same Price Deviation For Different Assets Oracle Resolved
    Location
    DigiftAdapter.sol: 594
    Round
    Main Review

    Description

    The DigiftAdapter contract uses the same priceUpdateDeviation parameter for both the asset token and the Digift token.

    However, these tokens may have different update frequencies, especially when real-world asset price updates are considered.

    While it may be reasonable for Digift tokens to have an update deviation of over 24 hours, or even several days, such a deviation could render the asset token’s price data completely outdated.

    Recommendation

    Consider using different deviation values for different tokens.

    Resolution

    Nashpoint Team: The issue was resolved in commit 1ff5440.

  25. L-09 Low Manager Can Mis-assign Funds Censoring Acknowledged
    Location
    DigiftAdapter.sol: 634-693
    Round
    Main Review

    Description

    In digiFTAdapter, when forwardRequestsToDigift function is called, it snapshots accumulatedDeposit into pendingDepositRequest globally but does not lock or record which specific nodes contributed to that batch.

    While a pending settlement exists, new nodes can still call requestDeposit(), which adds to accumulatedDeposit (separate from the pending amount).

    At settlement via settleDeposit(nodes, ...), the only check is that the sum of node.pendingDepositRequest across the provided nodes[] equals globalPendingDepositRequest. It does not verify that these are the same nodes whose deposits were actually forwarded. As a result, assets can be assigned to other nodes as long as the total sum remains equal.

    Example scenario:

    • Node1 deposits 100 tokens → manager forwards to DigiFT (pending = 100)
    • Node2 deposits 60, Node3 deposits 40 (accumulated = 100, but pending still = 100 from Node1)
    • Manager can settle the DigiFT response to Node2 and Node3 instead of Node1, because 60 + 40 =

    100

    • Node1 remains with pending status even though his batch was settled

    Note that the same issue exist with redeem settlements.

    Recommendation

    Use an auto-incrementing batchId (bumped on each forward); on deposit, snapshot which batchId each node contributed to; at settlement, enforce that returns apply only to nodes tied to that exact batchId.

    Resolution

    Nashpoint Team: Acknowledged.

  26. L-10 Low Overestimated ComponentAssets For Fee Vault Logical Error Resolved
    Location
    ERC4626Router.sol: 242
    Round
    Main Review

    Description

    The use of convertToAssets in ERC4626Router.sol can overestimate the actual asset value of a component's shares. Per ERC-4626 spec, convertToAssets does not account for fees or slippage that would apply during a real redemption.

    This can inflate the reported totalAssets in the Node, leading to incorrect share minting. As a result, users may redeem more assets than they should (causing insolvency), or receive fewer assets than they are entitled to.

    Recommendation

    Consider using previewRedeem instead or convertToAssets, as it's inclusive of fees per spec.

    Resolution

    Nashpoint Team: The issue was resolved in commit 2cd9444.

  27. L-11 Low Liquidation Queue Can Have Inactive Component Error Resolved
    Location
    Node.sol: 829
    Round
    Main Review

    Description

    The protocol docs state that one of the requirements of the liquidation queue is that all addresses must be active components. This is correctly enforced when the Node owner calls setLiquidationQueue.

    However, if the node owner decides to remove the component via removeComponent function, the deleted component is then not removed from the queue, thus breaking this requirement.

    The impact is that, in addition to a broken protocol invariant, this will DoS liquidation orders in _enforceLiquidationOrder:

    try IRouter(router).getComponentAssets(candidate, true) returns (uint256 assets) {
    candidateAssets = assets;
    }
    

    router for the removed candidate will be address(0), and since a return value is expected, this will not go to the catch block and instead entirely revert.

    Recommendation

    Upon removing a component, consider also removing it from the liquidation queue, or check if the owner cleared it from the queue first.

    Resolution

    Nashpoint Team: The issue was resolved in commit fc79a10.

  28. L-12 Low Liquidation Queue Incorrect Ordering Logical Error Resolved
    Location
    Node.sol: 824-841
    Round
    Main Review

    Description

    The protocol allows Node Owners to configure liquidation ordering to prioritize components based on, for example, liquidity costs and withdrawal delays.

    The following example is provided in the documentation:

    For queue [A, B, C] where A is ERC7540 and B, C are ERC4626:
    Must check A's claimable balance first
    Can only use B if A's claimable balance insufficient
    Can only use C if both A and B insufficient
    Pending (non-claimable) balances in A don't block using B or C
    

    There's a case where all of A,B,C may have insufficient claimable assets. In that case, the correct way should be to perform a partial (maximum) withdrawal on A first, followed by partial withdrawal on B, and then C.

    However, a direct partial withdrawal on B without following the correct order can succeed with the current implementation.

    For example, assume total claimable assets are A= 50tokenA, B = 30tokenA, and C = 30 tokenA. The requested withdrawal is 60 tokenA. The rebalancer role calls ERC4626Router.fulfillRedeemRequest for component B. This calls Node::enforceLiquidationOrder with component = B and 60 tokenA.

    _enforceLiquidationOrder will loop through the entire queue list of A,B,C, and since they all hold insufficient tokens, the call succeeds and a partial withdrawal on component B is executed using the minimal balance int256 componentShares = Math.min(IERC4626(component).convertToShares(assetsRequested), IERC20(component).balanceOf(address(node)));.

    This can be a common occurrence as investment portfolios are diversified across various components, and the intention of the Node Owner is to perform a partial withdrawal in the queued order.

    Recommendation

    Within _enforceLiquidationOrder, if each component has insufficient funds, ensure the component specified is equal to the candidate at the top of the queue. This will ensure partial withdrawals can happen in the correct order as the Node Owner intends.

    Resolution

    Nashpoint Team: The issue was resolved in commit fc79a10.

  29. L-13 Low Insufficient Minimum Threshold Check Logical Error Partially resolved
    Location
    BaseComponentRouter.sol: 123
    Round
    Main Review

    Description

    Node Owners can configure a maxDelta parameter for each allocated component, which is responsible for ensuring that the amount of deposited assets during investments are above this threshold. As the protocol docs state, this is to ensure unnecessary transactions are minimized.

    The maxDelta parameter is enforced within the BaseComponentRouter:

    // Validate deposit amount exceeds minimum threshold
    if (depositAmount < Math.mulDiv(totalAssets,
    INode(node).getComponentAllocation(component).maxDelta, WAD)) {
    revert ErrorsLib.ComponentWithinTargetRange(node, component);
    }
    // limit deposit by reserve ratio requirements
    // _validateReserveAboveTargetRatio() ensures currentCash >= idealCashReserve
    depositAmount = Math.min(depositAmount, currentCash - idealCashReserve);
    // subtract execution fee for protocol
    depositAmount = _subtractExecutionFee(depositAmount, node);
    

    Notice how the depositAmount is checked against the maxDelta threshold (to ensure it is not too small), but the depositAmount is subsequently updated to a potentially lower value in the next lines of code, followed by a fee deduction. This updated depositAmount, which is the actual deposit amount, is not checked against the minimum threshold.

    The impact is that unnecessary/small investment transactions will continue to occur, breaking protocol invariant.

    Recommendation

    Check against the minimum threshold after updating the depositAmount and deducting fees

    Resolution

    Nashpoint Team: The issue was resolved in commit ae4a663.

  30. L-14 Low Partial Withdrawals Are Blocked DoS Acknowledged
    Location
    DigiftAdapter.sol: 849
    Round
    Main Review

    Description

    The ERC7540router can requests a partial async withdrawal using Math.min(assetsRequested, maxClaimableAssets) in the fulfillRedeemRequest function:

    assetsReturned = _executeAsyncWithdrawal(node, component, Math.min(assetsRequested,
    maxClaimableAssets));
    

    if the component is DigiftAdapter and the amount requested is assetRequested which is less than maxWithdraw the tx will always revert, as digiFT adapter enforces full-withdraw-only semantics and reverts unless the requested assets equals node.maxWithdraw exactly. Which will block a request fulfilment.

    Recommendation

    Consider relaxing the strict requirement in digiFTAdapter and allowing partial withdraw.

    Resolution

    Nashpoint Team: Acknowledged.

  31. L-15 Low Malicious Owner Can Lock Users' Assets DoS Acknowledged
    Location
    NodePausingPolicy.sol: 82-85
    Round
    Main Review

    Description

    The Node Owner has the ability to block user withdrawals via the following methods:

    1. Changing setRebalanceCooldown (or setRebalanceWindow) to extend cooldown so rebalancer

    cannot call startRebalance() to initiate redemption from components.

    1. Implement incorrect component ratio causing startRebalance() to fail due to

    validateComponentRatios() returning false. Since rebalancing fails, withdrawals from components will also fail.

    1. Utilize policies to, for example, pause the router/rebalancer address for the withdrawal selectors.

    This is dangerous as the Node Owner is a permissionless role, thus causing harm to innocent users and trust for the protocol.

    Recommendation

    1. Set strict limits on how much the owner can change rebalance cooldown and window
    2. Enforce component ratio validation upon adding a new component in Node::addComponent
    3. Ensure that withdrawals cannot be blocked in policies

    Alternatively, document these behaviors for users to inform them of the owner’s capabilities.

    Resolution

    Nashpoint Team: Acknowledged.

  32. L-16 Low Node Owner Can Steal Funds Censoring Acknowledged
    Location
    Node.sol: 192-205
    Round
    Main Review

    Description

    When there is something wrong in one component, the protocol owner will set this component into the blacklist. The node owner is expected to withdraw assets from this component and then remove this component via removeComponent.

    Normally, component tokens are not allowed to be withdrawn in rescueTokens. However, a malicious owner can remove a blacklisted component directly by force and then directly transfer these component tokens.

    function removeComponent(address component, bool force) external onlyOwner
    onlyWhenNotRebalancing {
    if (force && !IRouter(router).isBlacklisted(component)) {
    revert ErrorsLib.NotBlacklisted();
    }
    }
    function rescueTokens(address token, address recipient, uint256 amount) external onlyOwner {
    if (token == asset) revert ErrorsLib.InvalidToken();
    if (_isComponent(token)) revert ErrorsLib.InvalidToken();
    IERC20(token).safeTransfer(recipient, amount);
    emit EventsLib.RescueTokens(token, recipient, amount);
    }
    

    Recommendation

    Consider documenting this behavior. Alternatively, do not allow blacklisted component shares to be rescued either, which forces the owner to withdraw the underlying funds through the regular liquidation flow.

    Resolution

    Nashpoint Team: Acknowledged.

  33. L-17 Low Settlements May Use Incorrect Prices Warning Acknowledged
    Location
    DigiftAdapter.sol: 649
    Round
    Main Review

    Description

    The settleDeposit and settleRedeem functions in the digiftAdapter contract use the latest oracle prices to determine the settlement value.

    While the expected time between the actual settlement event on the DigiFT side and the settlement on the adapter side can be up to 256 blocks, the latest price at the time of adapter settlement may differ from the price at the time of DigiFT settlement.

    This discrepancy can become more pronounced if a time gap occurs due to unexpected downtime in the backend or event listeners, requiring manual intervention after 256 blocks. In this case, the real settlement value and the calculated settlement value may differ significantly.

    Recommendation

    Be aware of this situation. Alternatively, consider using the valid price at the time the event is emitted on DigiFT to determine the settlement value.

    Resolution

    Nashpoint Team: Acknowledged.

  34. L-18 Low Users Can Claim Incentra Rewards Directly Logical Error Acknowledged
    Location
    IncentraRouter.sol: 28-37
    Round
    Main Review

    Description

    In IncentraRouter, the rebalancer can claim some rewards from Incentra on behalf of the node.

    The Incentra distributor on Arb is deployed on:

    https://arbiscan.io/address/0x273d0d19eaC2861FCF6B21893AD6d71b018E25aB#code.

    Users can claim node's rewards on behalf of the node directly via below function.

    function claimAll(address earner, address[] calldata campaignAddrs) public {
    for (uint256 i = 0; i < campaignAddrs.length; i++) {
    IRewardContract(campaignAddrs[i]).claim(earner);
    }
    }
    

    In the current design, only the node rebalancer is allowed to claim rewards. However, regular users can claim rewards directly on behalf of the node, giving them a way to increase the asset amount directly.

    e.g. 1. Alice deposits asset with current cache price. 2. Alice claims Incentra rewards via function claim. Although the current share price does not change, this ensures that the share price will be affected after the next rebalance. 3. After the next rebalance, the cache price will be updated, and the user can request withdraw with new price.

    If the rebalancers claim these rewards frequently in every rebalancing period, the impact would be minimal. However, if rebalancers do not claim rewards, users could anticipate a larger change in share value in the future and deposit before the value increases.

    Recommendation

    Rebalancers should claim rewards as frequently as possible to minimize the impact of claimed rewards on the share value, since there is no way to prevent regular users from claiming on behalf of the Node.

    Resolution

    Nashpoint Team: Acknowledged.

  35. L-19 Low Griefing Rebalancers Via Dust Requests Censoring Acknowledged
    Location
    Node.sol: 451
    Round
    Main Review

    Description

    Share owners create redemption requests, which are expected to be fulfilled by the rebalancer during the next rebalancing period. By default, rebalancers have one hour to perform multiple tasks, such as claiming rewards and fulfilling redemption requests.

    Share owners can create redemption requests for any amount and for any controller, and these requests are stored per controller.

    It is possible to create thousands of small redemption requests across different controller addresses, which increases the rebalancer’s workload in the next rebalancing period.

    This not only results in griefing the rebalancer, but can also cause legitimate users’ redemption requests to be delayed for several rebalancing periods.

    Recommendation

    Consider enforcing a minimum request amount to prevent such griefing.

    Additionally, consider disabling the creation of requests for arbitrary controllers. If this feature is necessary at the node level, the restriction can be implemented through a custom policy, allowing node owners to maintain greater control.

    Resolution

    Nashpoint Team: Acknowledged.

  36. L-20 Low Inaccurate Calculation Of Shares Logical Error Acknowledged
    Location
    Node.sol: 495
    Round
    Main Review

    Description

    totalAssets() uses cacheTotalAssets (refreshed at startRebalance) to price deposits/mints. Deposits are allowed while the cache can be stale, so convertToShares often uses an outdated denominator to calculate the number of shares to mint.

    Depositors can be over-minted when current actual totalAssets is greater than cacheTotalAssets (yield accumulated from lastUpdate until now), or under-minted when current actual totalAssets is less than cacheTotalAssets (strategy loss from last cacheTotalAssets update until now). Example:

    Cache = 1,000,000; actual totalAssets with accumulated yield from all components = 1,050,000;

    • User deposits 100,000 → receives 100,000 shares
    • Shortly after, rebalancer calls startRebalance, cache gets updated to 1,150,000
    • User shares worth: 100,000 * 1,150,000 / 1,100,000 = 104,545, a 4.5k gain extracted from existing

    holders without actually contributing to the yield

    • This gain for the depositing user is a loss for others who have been staking their assets in the node
    • Same applies for strategy losses
    • if total assets are frequently updated by rebalancer, impact will minimal

    Recommendation

    Consider refreshing totalAsset before any deposit.

    Resolution

    Nashpoint Team: Acknowledged.

  37. L-21 Low Unbounded Settlement Loops Gas Griefing Acknowledged
    Location
    DigiftAdapter.sol: 634-693
    Round
    Main Review

    Description

    The DigiftAdapter supports multiple nodes depositing/redeeming in the same batch. When settlement occurs via settleDeposit(nodes, ...) or settleRedeem(nodes, ...), the manager is enforced to provide all contributor nodes to that batch and the function loops through all of them to distribute shares/assets proportionally.

    There is no cap on how many nodes can contribute to a single batch. If too many nodes deposit/redeem for the same batch , the settlement transaction could exceed block gas limits and revert, making it impossible to settle and permanently locking all pending funds, unless upgrade

    Recommendation

    Enforce a maximum number of nodes that can be whitelisted per adapter instance (depending on gas analysis).

    Resolution

    Nashpoint Team: Acknowledged.

  38. L-22 Low Missing cacheTotalAssets Update After Swap Logical Error Acknowledged
    Location
    OneInchV6RouterV1.sol: 111-155
    Round
    Main Review

    Description

    In Nashpoint, the Node may gain some rewards. The rebalancer can swap these incentive tokens into node asset in the rebalancing window.

    One normal rebalancing order may be like as below: 1. Start rebalance. 2. Claim rewards. 3. Swap rewards to node asset token. 4. Fulfill redemptions. 5. Invest into vaults.

    The problem here is that when the incentive token is a node asset, or when other incentive tokens are swapped into a node asset, the Node does not update the claimed or swapped node assets in cacheTotalAssets.

    Then, when the rebalancer fulfills a redemption, the claimed rewards will not be included in the asset calculation, which is unfair to the user who redeemed their shares, as they are also entitled to a portion of these earned incentives. These assets will only be considered in the next rebalancing period.

    Recommendation

    When the rebalancer claims node assets or swaps other incentive tokens to acquire node assets, these assets should be added to cacheTotalAssets. If this is the intended behavior, document this for users.

    Resolution

    Nashpoint Team: Acknowledged.

  39. L-23 Low Can Invest In Blacklisted Components Warning Resolved
    Location
    Global
    Round
    Main Review

    Description

    The whitelist and blacklist status of a component is only checked when adding or removing a component from a Node. There is no check for the blacklist status when investing in components.

    A blacklisted component must be removed from the Node. However, until it is removed, it is still considered a valid Node component.

    The rebalancer might unintentionally continue investing in blacklisted components without noticing that a component has been blacklisted by the registry owner, especially if the rebalancing process is automated.

    Recommendation

    Deposits into blacklisted components should be prevented. This can be achieved by checking the blacklist status in the _computeDepositAmount function of the BaseComponentRouter, which is used by all investment flows.

    Resolution

    Nashpoint Team: The issue was resolved in commit 65ac649.

  40. L-24 Low Unchecked Deviation Upper Bound Validation Resolved
    Location
    DigiftAdapter.sol: 402-403
    Round
    Main Review

    Description

    The withinRange function in MathLib reverts with an underflow error when the allowedDeviation value exceeds WAD. The allowedDeviation values passed to this function are the priceDeviation and settlementDeviation values from the DigiftAdapter contract.

    While the setter functions for these values enforce that the value is less than or equal to WAD, there is no such check during initialization, so these values can exceed WAD, causing reverts during withinRange call.

    Recommendation

    Ensure that args.priceDeviation and args.settlementDeviation in initialize are less than or equal to WAD.

    Resolution

    Nashpoint Team: The issue was resolved in commit 221274d.

  41. L-25 Low Fee Bypass Via Low Decimal Tokens Rounding Acknowledged
    Location
    BaseComponentRouter.sol: 186-195
    Round
    Main Review

    Description

    The protocol earns fees via the following logic which is executed during investments and swaps:

    uint256 executionFee = transactionAmount * registry.protocolExecutionFee() / WAD;

    The executionFee rounds down due to Solidity’s default built-in rounding down. With WAD math, a fee as low as 0.0001% would mean registry.protocolExecutionFee() = 1e12. Because executionFee = floor(amount * 1e12 / 1e18), any amount < 1e6 base units rounds the fee down to zero.

    This is especially problematic for some high value tokens such as WBTC, where 1e6 of the token is currently worth ~$1110.

    In addition, for higher decimal tokens, users can batch swap/investment transactions into small amounts to round down the executionFee to 0 due to a lower transactionAmount.

    The investment/swap calls would proceed as normal since the execution fee function always returns when fee calculated is 0:

    if (executionFee == 0) {
    return transactionAmount;
    }
    

    Another option could be for users to create wrappers for high decimal tokens (i.e 18 decimals) and turn it into low decimal (i.e 2-6 decimals), allowing fee bypass.

    The result is loss of funds/revenue for the protocol.

    Recommendation

    Consider rounding up the executionFee or using fractional protocol fee accounting.

    Resolution

    Nashpoint Team: Acknowledged.

  42. L-26 Low Mint And Withdraw Returns Incorrect Values Compatibility Resolved
    Location
    DigiftAdapter.sol: 845
    Round
    Main Review

    Description

    DigiftAdapter.mint() and DigiftAdapter.withdraw() return values that don't match the actual assets consumed or shares burned, creating inconsistency with their emitted events and ERC-4626/7575 semantics.

    • mint(): Returns claimableDepositRequest (gross assets) but actually consumes assets -

    assetsToReimburse (net assets). The Deposit event correctly emits the net amount, but the return value overstates consumption.

    • withdraw(): Returns claimableRedeemRequest (gross shares) but actually burns shares -

    sharesToReimburse (net shares). The Withdraw event correctly emits the net amount, but the return value doesn't reflect what was burned. The NatSpec also states "returns (uint256 shares) The number of shares burned", which is wrong.

    While the current ERC7540Router does not rely on these return values (it tracks via balance deltas), it is preferable to remain aligned with the specification for any future interactions.

    Recommendation

    Consider returning net amounts (after reimbursement) to match actual state changes and emitted events.

    Resolution

    Nashpoint Team: The issue was resolved in commit c8cca04.

  43. L-27 Low Inaccurate Liquidation Queue Enforcement Logical Error Resolved
    Location
    ERC4626Router.sol: 157-159
    Round
    Main Review

    Description

    Liquidation order enforcement uses inflated component capacity

    • ERC4626Router.getComponentAssets() ignores the claimableOnly parameter and always returns

    convertToAssets(shareBalance), which does not account for withdrawal limits

    • When Node's _enforceLiquidationOrder() calls getComponentAssets(candidate, true) it expects the

    actual claimable assets, not all the asset invested in a component, but in case of ERC4626Router it may receive a value that can be more than what actually can be withdrawn from the component (maxWithdraw), thus it may enforce a wrong liquidation queue .

    Recommendation

    Return maxWithdraw(node) when claimableOnly == true in ERC4626Router.getComponentAssets()

    Resolution

    Nashpoint Team: The issue was resolved in commit fc79a10.

  44. I-01 Informational finalizeRedemption Lacks Rebalancing Modifier Logical Error Resolved
    Location
    Node.sol: 426
    Round
    Main Review

    Description

    The rebalancer role has two ways of finalizing user requested redemptions:

    1. Node::fulfillRedeemFromReserve: Utilizes the reserves within the Node contract to finalize

    redemptions.

    1. Router::fulfillRedeemRequest (which calls Node::finalizeRedemption): Executes withdrawals from

    components, such as ERC4626 vaults, to finalize redemptions.

    The first method, fulfillRedeemFromReserve ensures that the Rebalancer role can only execute this after first calling startRebalance(). This ensures that the total assets are up to date via (_updateTotalAssets) and that fees are distributed (via _payManagementFees) using the current cached balance.

    The problem is that the second option, fulfillRedeemRequest, does not have the requirement that the rebalancing window must have been initiated first (via startRebalance()). This means that when redemptions are finalized, an outdated cacheTotalAssets may be used, causing an incorrect share to asset calculation, thus causing a loss for users.

    In addition, this flow will subsequently skip fee payment (to protocol and node owner). Since finalizeRedemption deducts the cacheTotalAssets, the fee payment for the withdrawal is skipped permanently, as the fees calculated depend on the cacheTotalAssets value. This will cause a loss to the protocol and node owner.

    Recommendation

    Apply the onlyWhenRebalancing modifier to Node::finalizeRedemption

    Resolution

    Nashpoint Team: The issue was resolved in commit e959571.

  45. I-02 Informational Possible Different Share Price Via Deposit/Mint Informational Acknowledged
    Location
    Node.sol: 491-510
    Round
    Main Review

    Description

    In Node, users can choose to deposit assets via deposit or mint. When swing pricing is enabled, users may receive a deposit bonus through deposit, but they will not receive a bonus through mint.

    Recommendation

    Document this behavior to inform users about the difference between deposit and mint.

    Resolution

    Nashpoint Team: Acknowledged.

  46. I-03 Informational rescueTokens Should Restrict Incentive Tokens Trust Assumptions Acknowledged
    Location
    Node.sol: 321-326
    Round
    Main Review

    Description

    The rescueTokens function can be used to withdraw tokens from the Node, except for the Node's asset token and component tokens. However, this restriction does not apply to incentive tokens claimed from external integrations.

    These incentive tokens should also not be withdrawable by the Node owner, similar to the Node’s asset tokens, as they belong to depositors and are intended to be swapped into asset tokens during rebalancing.

    Recommendation

    Restrict incentive tokens from being withdrawn via the rescueTokens function. However, this would require additional functionality to track all incentive token addresses when the Node joins a campaign.

    Alternatively, document this behavior for users.

    Resolution

    Nashpoint Team: Acknowledged.

  47. I-04 Informational Inconsistent Liquidation Ordering Unexpected Behavior Resolved
    Location
    ERC4626Router.sol: 89
    Round
    Main Review

    Description

    The order of liquidation set by the Node Owner is correctly enforced within ERC7540Router::fulfillRedeemRequest, ERC4626Router::fulfillRedeemRequest. These functions execute requested withdrawals and update storage for user withdrawal requests.

    However, the Rebalancer role can directly withdraw from the components via ERC4626Router::liquidate and ERC7540Router::executeAsyncWithdrawal, which do not check the liquidation order.

    The impact is that withdrawals can happen in a complete different order than what the Node Owner specified, potentially withdrawing from more riskier vaults first.

    Recommendation

    Consider enforcing the liquidation order in ERC4626Router::liquidate and ERC7540Router::executeAsyncWithdrawal

    Resolution

    Nashpoint Team: The issue was resolved in commit fc79a10.

  48. I-05 Informational totalAssets() Can Revert Violating ERC7575 Compatibility Acknowledged
    Location
    DigiftAdapter.sol: 1012-1014
    Round
    Main Review

    Description

    The totalAssets() function calls convertToAssets(totalSupply()), which internally fetches prices via _getAssetPrice() and _getPrice(). These price functions enforce staleness and deviation checks that can revert.

    ERC7575 spec requirement: "totalAssets() MUST NOT revert"

    Recommendation

    Make price fetches in view functions non-reverting by returning cached/last-known values when fresh data is unavailable, or clearly document that this implementation does not fully conform to ERC7575 view function requirements

    Resolution

    Nashpoint Team: Acknowledged.

  49. I-06 Informational Division By 0 DoS During Deposit Bonus Math Resolved
    Location
    QuoterV1.sol: 145
    Round
    Main Review

    Description

    QuoterV1 executes the following when calculating deposit bonus:

    uint256 targetReserveAssets = investedAssets.mulDiv(targetReserveRatio, WAD -
    targetReserveRatio);
    ...
    uint256 deltaClosedPct = Math.mulDiv(deltaClosed, WAD, targetReserveAssets);
    

    In case the investedAssets is a low amount (i.e after liquidations) and for example targetReserveRatio is set to 5% (0.05e18), targetReserveAssets can round down to 0. This would cause a revert due to division by 0 when calculating deltaClosedPct during the deposit bonus calculation, causing DoS to deposits completely.

    This edge case can be triggered if getCashAfterRedemptions is depleted due to large withdrawals causing (Math.mulDiv(getCashAfterRedemptions(), WAD, totalAssets()) < targetReserveRatio) thus executing the deposit bonus branch during deposits.

    Recommendation

    Return safely if targetReserveAssets == 0 when calculating deposit bonus

    Resolution

    Nashpoint Team: The issue was resolved in commit 2c6e5b9.

  50. I-07 Informational Unbounded Loops In Event Verification Gas Optimization Resolved
    Location
    DigiftEventVerifier.sol: 223-279
    Round
    Main Review

    Description

    DigiftEventVerifier.verifySettlementEvent() iterates through all logs in the transaction receipt to locate the settlement event, and then iterates through all investors in the event to find the caller's index.

    A transaction can emit multiple logs, and a log can include many investors, which may result in excessive gas consumption or even hit block limits in some cases.

    Recommendation

    Add logIndex and investorIndex to OffchainArgs to allow direct access instead of O(n) scans, which will save a significant amount of gas.

    Resolution

    Nashpoint Team: The issue was resolved in commit a0ee7e7.

  51. I-08 Informational Unused Named Return Value Informational Resolved
    Location
    ERC4626Router.sol: 194
    Round
    Main Review

    Description

    The _getInvestmentSize function in the ERC4626Router and ERC7540Router contracts defines the return value as uint256 depositAssets. However, this depositAssets value is never used, and delta is returned instead.

    Recommendation

    Remove unused named value.

    Resolution

    Nashpoint Team: The issue was resolved in commit c1a5fda.

  52. I-09 Informational Warning About Virtual Functions Warning Resolved
    Location
    BaseComponentRouter.sol: 96-103
    Round
    Main Review

    Description

    Both getComponentAssets and _getInvestmentSize function in BaseComponentRouter contract are declared virtual but have empty bodies that implicitly return zero. These functions must be overridden in the inheriting router contract.

    While this is not an issue at the moment, forgetting to override these functions when adding new routers causes calls to succeed but return 0, which can lead to unexpected behaviors such as incorrect valuations.

    Recommendation

    Consider making these virtual functions revert by default, which would prevent unexpected behaviors and enable early detection if a new router fails to override them.

    Resolution

    Nashpoint Team: The issue was resolved in commit 59402ff.

  53. I-10 Informational _safeApprove Can Revert Informational Acknowledged
    Location
    BaseComponentRouter.sol: 197-200
    Round
    Main Review

    Description

    The helper function approves a spender directly to a non-zero amount without first resetting the allowance to zero. Some tokens require that the allowance be set to zero before assigning a new non-zero allowance.

    Since all flows in the codebase approve an amount and use the same amount immediately afterward, allowances are expected to be cleared after execution. However, care should be taken when adding components or new routers, as this may cause unexpected reverts in edge cases.

    Examples include an underlying component being incompatible with ERC4626 or ERC7540 despite expectations (e.g., requestDeposit using a different asset value than provided), or a new router not utilizing the full allowance.

    Although these are extreme edge cases, they can leave unused allowances, and cause reverts in subsequent actions.

    Recommendation

    Be aware of this situation and ensure that all components and routers always use the full allowance during execution. Alternatively, consider resetting the allowance to 0 first if any remaining allowance exists.

    Resolution

    Nashpoint Team: Acknowledged.

  54. I-11 Informational Users May Be Unable To Withdraw From Adapter Documentation Acknowledged
    Location
    DigiftAdapter.sol: 751
    Round
    Main Review

    Description

    DigiftAdapter enforces minimum amounts for requestDeposit and requestRedeem.

    Assume the minimum for deposit = 1000e6 USDC and minimum for redeem = 10e18 shares (as configured in tests), users may have deposits where their total shares represent less than the minimum required to request a redemption from the component.

    Following the intended design, the rebalancer will be unable to execute a redemption request for these users, unless an admin changes the minimum amount or other users queue redemptions, which will allow the total request to exceed the minimum.

    Recommendation

    Consider documenting this clearly to ensure users are aware of it.

    Resolution

    Nashpoint Team: Acknowledged.

  55. I-12 Informational Unused State Variables In QuoterV1 Informational Resolved
    Location
    QuoterV1.sol: 33-36
    Round
    Main Review

    Description

    QuoterV1 declares three state variables that are never written to or read from anywhere in the codebase:

    /* STATE */
    mapping(address => bool) public isErc4626;
    mapping(address => bool) public isErc7540;
    bool public isInitialized;
    

    Recommendation

    Consider removing unused state variables.

    Resolution

    Nashpoint Team: The issue was resolved in commit 6259916.

  56. I-13 Informational Missing Public Functions In Multiple Interfaces Informational Acknowledged
    Location
    N/A
    Round
    Main Review

    Description

    Several interfaces are incomplete compared to their implementations.

    Missing functions include:

    • INode (9 methods): Ownable functions, multicall(), rebalance getters, swing pricing getters
    • INodeRegistry (9 methods): Ownable functions, UUPS upgrade methods, config getters
    • IQuoterV1 (4 methods): Type checks, initialization state, registry reference
    • INodeFactory (2 methods): Implementation and registry getters

    Recommendation

    Consider adding these methods or documenting why they're excluded.

    Resolution

    Nashpoint Team: Acknowledged.

  57. I-14 Informational Misleading Comment Regarding deltaClosedPct Informational Resolved
    Location
    QuoterV1.sol: 145
    Round
    Main Review

    Description

    The deltaClosedPct value represents the percentage of the delta relative to the targetReserveAssets:

    deltaClosedPct = Math.mulDiv(deltaClosed, WAD, targetReserveAssets).

    However, the comments “It is the inverse of the percentage of the reserve assets shortfall closed by the deposit” and “Get reserveImpact as a measure of how much the deposit helps to close any asset shortfall” gives the impression that deltaClosedPct was meant to represent the percentage of the shortfall, not of the targetReserveAssets.

    Recommendation

    Update the comments.

    If the intention was to calculate the reserve impact based on the shortfall percentage, deltaClosedPct should be Math.mulDiv(deltaClosed, WAD, shortfall)

    Resolution

    Nashpoint Team: The issue was resolved in commit 6259916.

  58. I-15 Informational Incorrect Comment In DigiftEventVerifier Informational Resolved
    Location
    DigiftEventVerifier.sol: 234
    Round
    Main Review

    Description

    In line 234 of the DigiftEventVerifier, the comment "// Structure: (stToken, investorList, quantityList, currencyTokenList, amountList, timestamp)" is incorrect, as the last parameter in the structure is feeList, not timestamp.

    Recommendation

    Update the comment.

    Resolution

    Nashpoint Team: The issue was resolved in commit d48b89f.

  59. I-16 Informational Warning Regarding Dust Accumulation In Digift Warning Acknowledged
    Location
    DigiftAdapter.sol: 677
    Round
    Main Review

    Description

    The settleDeposit and settleRedeem functions in DigiftAdapter accumulate the dust amount to the last node in the loop. The adapter defines minDepositAmount and minRedeemAmount, which are set to non-trivial values in the tests.

    Accumulating dust to the last node is not an issue under normal conditions where the minimum amounts are set. However, if these minimum amounts are ever set to 0, it becomes possible to request only 1 wei of shares.

    In that case, sharesToReimburse may exceed 1 wei due to the dust accumulated on the last node, which can cause an underflow later.

    Note that this is an extreme edge case scenario where the minimum amounts must be set to 0, a node must have a request with an extremely low value, and that node must also be the last one during settlement.

    Recommendation

    Be aware of this and do not set minimum amounts to 0.

    Resolution

    Nashpoint Team: Acknowledged.

  60. I-17 Informational Zero Share Minting Allowed Warning Resolved
    Location
    Node.sol
    Round
    Main Review

    Description

    The Node contract does not revert when the resulting share amount is 0 during a deposit or mint operation. This can cause users to lose their assets without receiving any shares, especially when the share price is inflated.

    Recommendation

    Do not allow zero share minting.

    Resolution

    Nashpoint Team: The issue was resolved in commit 93cc526.

Remediation Review

6 findings
  1. L-01 Low Can Invest In Blacklisted Components Validation Resolved
    Location
    BaseComponentRouter.sol: 120
    Round
    Remediation Review

    Description

    The fix for L-23 introduces whitelist status in _computeDepositAmount and prevents deposits when a component is not whitelisted.

    However, since issue L-05 is acknowledged, whitelist and blacklist statuses can still conflict, allowing a blacklisted component to also remain whitelisted.

    Blacklisting does not automatically remove a component from the whitelist, and the issue persists until the component is manually de-whitelisted or removed by the owner.

    Recommendation

    Check the blacklist status in _computeDepositAmount as well or always ensure blacklisting and de-whitelisting happens together.

    Resolution

    Nashpoint Team: The issue was resolved in commit 2a6584f.

  2. L-02 Low Unsafe External Call In NodeFactory Validation Resolved
    Location
    NodeFactory.sol: 69
    Round
    Remediation Review

    Description

    SetupCalls are introduced to the NodeFactory to allow Node deployers to configure their Nodes during deployment. This is mainly intended for performing policy calls that are protected by the onlyNodeOwner modifier, since the owner at the time of these calls is the factory contract itself.

    However, there is no guarantee that these setup calls will be performed only on policies. Any target address can be called with any payload, without any protection. They could even be used for malicious actions, such as funding an exploiter through the factory contract or interacting with OFAC-sanctioned accounts.

    While there is no incentive to perform such actions and the likelihood is extremely low, it could still be legally binding for Nashpoint in the future because, on paper, it would appear that the ‘Nashpoint-owned factory’ is the entity carrying out these actions.

    Recommendation

    Do not allow arbitrary target addresses to be called through the factory. Keep a list of policies that can be called during setup, and allow only those addresses as valid targets.

    Resolution

    Nashpoint Team: The issue was resolved in commit 4fe012b.

  3. L-03 Low Users May Avoid Paying Performance Fees Logical Error Acknowledged
    Location
    Node.sol: 359-366
    Round
    Remediation Review

    Description

    In the fix for M-03, the management fee payment can be bypassed if there is not enough balance in the Node. This will make sure that startBalance operation will not be blocked.

    This will raise one new issue here: The users may avoid paying performance fees.

    e.g.

    1. The node owner starts rebalance in timestamp X. rebalanceWindow = 1 hour , rebalanceCooldown

    = 23 hours.

    1. Alice deposits 1000 USDC in timestamp X + 1.
    2. In timestamp X + 1 hour, the rebalancer invests 1000 USDC into one component. Assume that the

    target reserve ratio is 0.

    1. In timestamp X + 1 hour + 1, Alice requests redeem.
    2. In timestamp X + 24 hour, the node rebalancer will start another round of rebalance. The balance

    in the node is 0. The management fee will be bypassed here.

    1. The rebalancer triggers fulfillRedeemRequest. Alice can get back her asset without paying any

    fees here.

    Recommendation

    In startRebalance, if the balance is not enough to pay the management fee, record the management fee as debt, and update the lastPayment timely.

    Resolution

    Nashpoint Team: Acknowledged.

  4. I-01 Informational Consider Early Return In _fulfillRedeemFromReserve Informational Acknowledged
    Location
    Node.sol: 698
    Round
    Remediation Review

    Description

    The _fulfillRedeemFromReserve function sets balance = max(currentBalance, 1) and, if assetsToReturn > balance, sets assetsToReturn = balance. When actual reserve is 0, this forces assetsToReturn = 1, which later fails with ExceedsAvailableReserve in _finalizeRedemption function.

    function _fulfillRedeemFromReserve(address controller) internal {
    ...
    uint256 balance = Math.max(IERC20(asset).balanceOf(address(this)), 1);
    uint256 assetsToReturn = convertToAssets(request.pendingRedeemRequest);
    ...
    if (assetsToReturn > balance) {
    sharesPending = (sharesPending * balance - 1) / assetsToReturn + 1;
    assetsToReturn = balance; // balance may be 0, but forced to 1 above
    }
    _finalizeRedemption(controller, assetsToReturn, sharesPending);
    }
    

    Recommendation

    Although the flow eventually reverts when the balance is zero, consider reverting or returning early in that case.

    Resolution

    Nashpoint Team: Acknowledged.

  5. I-02 Informational Tradeoffs Related To Share Valuation In M-08 Warning Acknowledged
    Location
    ERC7540Router.sol: 278
    Round
    Remediation Review

    Description

    The fix for the previous M-08 introduces a mixed approach in which the maxWithdraw value at the locked rate is used for the normal case, while the current-rate share conversion is used when withdrawals are paused.

    While this approach fixes the issue in most cases and the likelihood of the paused state is low, it is important to note that it will still result in incorrect valuation when withdrawals are paused.

    Unfortunately, this is one of the trade-offs of asynchronous share valuations. In a paused scenario, the protocol must either intentionally undervalue the total underlying assets by continuing to use maxWithdraw and keeping claimable assets at 0, or accept the risk of overvaluation by using the current rate.

    From a security perspective, undervaluing total assets may be the safer option, as it would prevent insolvency if everyone attempted to redeem their shares in an extremely rare scenario. However, this also means that new depositors would receive more shares than their deposits justify.

    Recommendation

    Note that this issue aims to highlight possible edge-cases after the fix of M-08. The decision to use the current rate when withdrawals are paused should be made deliberately, as it still carries some risk of overvaluation, and it is important to consider the trade-offs of both scenarios.

    Resolution

    Nashpoint Team: Acknowledged.

  6. I-03 Informational Blocked Revoke Actions In Policies With Blacklist Logical Error Acknowledged
    Location
    GatePolicyBase.sol: 64
    Round
    Remediation Review

    Description

    The updated policies now include a blacklisting feature as well. Both the whitelist and blacklist variants apply actor checks to the spender on every approval, regardless of the amount.

    This also blocks approve(spender, 0) calls intended to revoke existing allowances if the spender has since become blacklisted.

    Similarly, removing an operator via a setOperator(operator, false) call is also blocked once the operator becomes blacklisted. Ability to revoke approvals and operators when they become blacklisted is crucial and should not be blocked.

    Recommendation

    Allow revoking approvals and removing operators when using GatePolicy with blacklisting, and do not perform actorCheck on spender/operator in these special cases.

    Resolution

    Nashpoint Team: Acknowledged.

Invariants 113

The review's fuzzing suite asserted 113 invariants. 113 held.

Every invariant tested
IDInvariantResult
DIGIFT-01Global Pending Deposit Must Match Forwarded AmountHeld
DIGIFT-02Global Pending Redeem Must Match Forwarded AmountHeld
DIGIFT-03No Pending Deposits Must Remain After SettleHeld
DIGIFT-04No Pending Redemptions Must Remain After SettleHeld
DIGIFT-05Max Mintable Shares Must Be Non-Zero After Settle DepositHeld
DIGIFT-06Max Withdrawable Assets Must Be Non-Zero After Settle RedeemHeld
DIGIFT-07Total Max Withdrawable Must Match Expected AssetsHeld
DIGIFT-08Withdraw Assets Must Match Max Withdraw BeforeHeld
DIGIFT-09Node Balance Must Not Decrease After WithdrawHeld
DIGIFT-10Max Withdraw Must Be Zero After WithdrawHeld
DIGIFT-11Pending Redeem Must Increase After RequestHeld
DIGIFT-12Balance Must Not Increase After Request RedeemHeld
FACTORY-01Deployed Node Address Must Not Be ZeroHeld
FACTORY-02Deployed Escrow Address Must Not Be ZeroHeld
FACTORY-03Node Escrow Link Must Match Deployed EscrowHeld
FACTORY-04Node Asset Must Match Init Args AssetHeld
FACTORY-05Node Owner Must Match Init Args OwnerHeld
FACTORY-06Node Total Supply Must Be Zero After DeployHeld
FACTORY-07Node Must Be Registered In Registry After DeployHeld
NODE-01User Share Balance Must Increase After Successful Deposit/MintHeld
NODE-02Escrow Share Balance Must Increase By The Redemption Request AmountHeld
NODE-03Escrow Share Balance Must Decrease After A Redeem Is FinalizedHeld
NODE-04User Asset Balance Must Increase By Requested Asset Amount After WithdrawHeld
NODE-05Escrow Asset Balance Must Be >= Sum Of All claimableAssetsHeld
NODE-06Component's Asset Ratio Should Not Exceed Target After InvestHeld
NODE-07Node's Reserve Should Not Decrease Below Target After InvestHeld
NODE-08Receiver Share Balance Must Increase By Minted Shares After DepositHeld
NODE-09Node Asset Balance Must Increase By Deposited AssetsHeld
NODE-10Node Total Assets Must Increase By Deposited AssetsHeld
NODE-11Node Total Supply Must Increase By Minted SharesHeld
NODE-12Receiver Share Balance Must Increase By Minted SharesHeld
NODE-13Receiver Asset Balance Must Decrease By Assets SpentHeld
NODE-14Node Total Assets Must Increase By Assets SpentHeld
NODE-15Node Total Supply Must Increase By Requested SharesHeld
NODE-16Owner Share Balance Must Decrease By Requested SharesHeld
NODE-17Pending Redeem Must Increase By Requested SharesHeld
NODE-18Claimable Redeem Must Remain Unchanged After RequestHeld
NODE-19Claimable Assets Must Remain Unchanged After RequestHeld
NODE-20Pending Redeem Must Decrease After FulfillHeld
NODE-21Claimable Redeem Must Increase After FulfillHeld
NODE-22Claimable Assets Must Decrease By Withdrawn AmountHeld
NODE-23Escrow Asset Balance Must Decrease By Withdrawn AmountHeld
NODE-24Pending Redeem Must Decrease By Finalized SharesHeld
NODE-25Claimable Redeem Must Increase By Finalized SharesHeld
NODE-26Claimable Assets Must Increase By Returned AssetsHeld
NODE-27Escrow Asset Balance Must Increase By Returned AssetsHeld
NODE-28Node Asset Balance Must Decrease By Returned AssetsHeld
NODE-29Claimable Redeem Must Decrease By Redeemed SharesHeld
NODE-30Claimable Assets Must Decrease By Returned AssetsHeld
NODE-31Receiver Asset Balance Must Increase By Returned AssetsHeld
NODE-32Escrow Asset Balance Must Decrease By Returned AssetsHeld
NODE-33Component Must Be Registered After AddHeld
NODE-34Component Must Be Unregistered After RemoveHeld
NODE-35Node Balance Must Decrease By Rescued AmountHeld
NODE-36Recipient Balance Must Increase By Rescued AmountHeld
NODE-37Policy Must Be Registered After AddHeld
NODE-38Policy Must Be Unregistered After RemoveHeld
NODE-39Component Balance Must Increase By Delta After Gain BackingHeld
NODE-40Component Balance Must Decrease By Delta After Lose BackingHeld
NODE-41Shares Exiting Must Not Exceed Total SupplyHeld
ONEINCH-01Asset Token Balance Of Node Must Increase After Successful SwapHeld
ONEINCH-02All Incentive Token Input Must Be Used During SwapHeld
ONEINCH-03Node Must Receive At Least 99% Of Min Assets OutHeld
ONEINCH-04Node Must Spend Exact Incentive AmountHeld
ONEINCH-05Executor Must Receive At Least Incentive AmountHeld
POOL-01Pending Deposits Must Not Increase After ProcessHeld
POOL-02Pending Redemptions Must Be Zero After ProcessHeld
REGISTRY-01Protocol Fee Address Must Match Set ValueHeld
REGISTRY-02Protocol Management Fee Must Match Set ValueHeld
REGISTRY-03Protocol Execution Fee Must Match Set ValueHeld
REGISTRY-04Policies Root Must Match Set ValueHeld
REGISTRY-05Registry Type Status Must Match Set ValueHeld
REGISTRY-06Owner Must Match After TransferHeld
RWD-FLD-01Claim Recipient Must Be Node AddressHeld
RWD-FLD-02Claim Cumulative Amount Must Match ParamsHeld
RWD-FLD-03Claim Position ID Must Match ParamsHeld
RWD-FLD-04Claim Cycle Must Match ParamsHeld
RWD-FLD-05Claim Proof Hash Must Match ParamsHeld
RWD-INC-01Last Earner Must Be Node AddressHeld
RWD-INC-02Campaign Addresses Hash Must MatchHeld
RWD-INC-03Rewards Hash Must MatchHeld
RWD-MKL-01Users Hash Must Match ParamsHeld
RWD-MKL-02Tokens Hash Must Match ParamsHeld
RWD-MKL-03Amounts Hash Must Match ParamsHeld
RWD-MKL-04Proofs Hash Must Match ParamsHeld
ROUTER-01Blacklist Status Must Match Set ValueHeld
ROUTER-02Whitelist Status Must Match Set ValueHeld
ROUTER-03Tolerance Value Must Match Set ValueHeld
ROUTER4626-01Invest Must Return Non-Zero Deposit AmountHeld
ROUTER4626-02Node Component Shares Must Not Decrease After InvestHeld
ROUTER4626-03Node Asset Balance Must Not Increase After InvestHeld
ROUTER4626-04Liquidate Must Return Non-Zero Assets When ExpectedHeld
ROUTER4626-05Node Component Shares Must Not Increase After LiquidateHeld
ROUTER4626-06Node Asset Balance Must Not Decrease After LiquidateHeld
ROUTER4626-07Fulfill Must Return Non-Zero AssetsHeld
ROUTER4626-08Escrow Balance Must Not Decrease After FulfillHeld
ROUTER4626-09Node Asset Balance Must Not Increase After FulfillHeld
ROUTER7540-01Invest Must Request Non-Zero AssetsHeld
ROUTER7540-02Pending Deposit Must Not Decrease After InvestHeld
ROUTER7540-03Node Asset Balance Must Not Increase After InvestHeld
ROUTER7540-04Node Component Shares Must Increase By Received Shares After MintHeld
ROUTER7540-05Claimable Must Not Increase After MintHeld
ROUTER7540-06Pending Redeem Must Not Decrease After Request WithdrawalHeld
ROUTER7540-07Component Share Balance Must Not Increase After Request WithdrawalHeld
ROUTER7540-08Execute Withdrawal Must Return Non-Zero AssetsHeld
ROUTER7540-09Execute Withdrawal Assets Must Match Max Withdraw BeforeHeld
ROUTER7540-10Claimable Must Not Increase After Execute WithdrawalHeld
ROUTER7540-11Node Asset Balance Must Not Decrease After Execute WithdrawalHeld
ROUTER7540-12Max Withdraw Must Be Zero After Execute WithdrawalHeld
ROUTER7540-13Fulfill Redeem Must Return Non-Zero Assets When ExpectedHeld
ROUTER7540-14Escrow Balance Must Not Decrease After Fulfill RedeemHeld
ROUTER7540-15Node Asset Balance Must Not Increase After Fulfill RedeemHeld
ROUTER7540-16Component Shares Must Not Increase After Fulfill RedeemHeld

More from Nashpoint

  1. Contract Updates

    21 findings1 high 21 findings: 1 high, 2 medium, 4 low, 14 informational

Put your code through the same review.

This review started with a conversation about scope. Tell us what you are building and we will plan yours with you.

Get a quote