World
Vault

The Worldcoin Vault's interest accrual logic and its integration with the WorldIDAddressBook verification registry were in scope.
report id
0328
category
DeFi & Yield
language
Solidity
final report
May 16, 2025
lines of code
252
Content

Download audit report

1

Executive Summary

This document outlines the security review conducted by Nethermind Security for the Worldcoin Vault. The Vault is a smart contract allowing World App users to deposit WLD tokens and earn interest for their deposited amounts. Interest accrual is calculated based on the time elapsed since the deposit or the last claimed rewards. Furthermore, the contract integrates with the WorldIDAddressBook, which maintains a registry of verified users alongside with their verification time. Rewards are only accrued during periods when users are verified.

The audited code comprises 252 lines of code in Solidity. The Worldcoin team has provided inline documentation that explains the purpose and functionality behind the audited contracts. The audit was performed using: (a) manual analysis of the codebase, (b) simulation of the smart contracts.

Audit assessment
Audit TypeSecurity Review
Initial ReportSep 26, 2024
Final ReportMay 16, 2025
MethodsManual Review
Repositoryworldcoin-vault
Commit (Audit)fef27a34f4
Commit (Final)fef27a34f4
DocumentationInline comments
Documentation AssessmentLow
Test Suite AssessmentMedium
Total findings
5
Fixed
0
Acknowledged
5
Mitigated
0
Unresolved
0

Findings by severity

  • Critical0
  • High0
  • Medium0
  • Low0
  • Informational4
  • Best Practices1

2

Audited Files

#ContractLoCCommentsRatioBlankTotal
1src/WLDVault.sol25211947.2%94465
Total25211947.2%94465

4

System Overview

The WLDVault enables World App users to deposit WLD tokens and earn yield over time. It interacts with the WorldIDAddressBook contract, which maintains a registry of verified users along with their verification timestamps. This ensures that only verified users can accrue interest based on their deposits.

4.1 WorldIDAddressBook

The WorldIDAddressBook contract is responsible for the verification of user addresses that can earn yield in WLDVault. This contract integrates with the WorldID proof verification system to authenticate users using zero-knowledge proofs.

The WorldIDAddressBook contract is initialized with several key parameters that control the verification process. These parameters are set during the contract's deployment:

  • worldIdRouter: The instance of the WorldID router responsible for managing groups and verifying zero-knowledge proofs.
  • groupId: A unique identifier for the group within the WorldID system.
  • externalNullifierHash: A hash used to prevent double-signaling by ensuring each proof is unique.
  • verificationLength: The duration, in seconds, for which a verification is considered valid.
  • maxProofTime: The maximum allowable time, in seconds, in which a proof must be validated to be considered legitimate.

Users submit their verification proof through the verify(...) function, providing necessary details such as account address, nullifier hash, proof, and proof time.

function verify(
address account,
uint256 root,
uint256 nullifierHash,
uint256[8] calldata proof,
uint256 proofTime
) external payable override;

The contract verifies the proof's validity with the worldIdRouter and updates the (nullifierHashes and addressVerifiedUntil) mappings to reflect the user's verification status and validity period.

4.2 WLDVault

The WLDVault contract is a savings vault designed to allow verified users within the Worldcoin ecosystem to deposit their WLD tokens and earn yield over time. The WLDVault contract's functionality include:

  • Deposits: Users can deposit WLD tokens into the vault. Upon deposit, the tokens start accruing interest immediately if the user is verified. The contract maintains a record of each user's deposited amount.
  • Withdrawals: Users can withdraw their deposited tokens along with the accrued interest at any time. The contract ensures that the correct amount of interest is calculated and distributed upon withdrawal.
  • Interest Calculation: Interest is computed based on a predefined yield rate, the amount deposited, and the duration for which the tokens have been saved in the vault.

4.2.1 Deposits and Withdrawals

Users primarily engage with the WLDVault contract through its deposit and withdrawal functions. These functionalities allow users to deposit their WLD tokens and subsequently withdraw them along with any accrued interest. The accrued interest is calculated and immediately added to the user's balance during each deposit operation.

The deposit(...) function allows users to deposit WLD tokens into the vault. This function can be called with or without specifying an account, allowing deposits for other users.

During the deposits, the vault interacts with WorldIDAddressBook to query the addressVerifiedUntil mapping to obtain a timestamp, which will be saved to the user's deposit struct Deposit as endTime variable. This timestamp refers to the point to which the user can accrue interest for his deposited amount.

function deposit(uint256 amount) external virtual;

The withdraw(...) and withdrawAll(...) functions allow users to withdraw their tokens from the vault. Users can also use the withdrawWithSig(...) function to authorize withdrawals with an off-chain signature.

function withdraw(uint256 amount) external virtual;

function withdrawAll() external virtual;

function withdrawWithSig(
address receiver,
uint256 amount,
uint256 nonce,
bytes calldata signature
) external virtual;

Moreover, users can use the recoverDeposit() function to withdraw their deposited amounts only, excluding the accrued interests.

function recoverDeposit() external virtual;

4.2.2 Interest Calculation

Interest in the WLDVault contract is calculated based on several key parameters:

  • Yield Rate: The annual percentage yield for the vault. This value is set by the owners of the WLDVault contract.
  • Deposit Amount: The amount of WLD tokens deposited by the user.
  • Time Elapsed: The time (in seconds) that has elapsed since the last interest calculation.

The formula for the interest calculation is as follows:

Interest = ( (Deposit Amount × Yield Rate × Time Elapsed)/(Seconds Per Year × 10000) )

Additionally, users' interests are controlled by additional parameters managed by the contract owner:

  1. maxYieldAmount: The maximum amount of tokens for which the user is able to earn interest.
  2. yieldAccrualDeadline: The maximum timestamp to which the user is able to accrue rewards.

5

Risk Rating Methodology

The risk rating methodology used by Nethermind Security follows the principles established by the OWASP Foundation. The severity of each finding is determined by two factors: Likelihood and Impact.

Likelihood measures how likely the finding is to be uncovered and exploited by an attacker. This factor will be one of the following values:

  1. High: the issue is trivial to exploit and has no specific conditions that need to be met;
  2. Medium: the issue is moderately complex and may have some conditions that need to be met;
  3. Low: the issue is very complex and requires very specific conditions to be met.

When defining the likelihood of a finding, other factors are also considered. These can include but are not limited to motive, opportunity, exploit accessibility, ease of discovery, and ease of exploit.

Impact is a measure of the damage that may be caused if an attacker exploits the finding. This factor will be one of the following values:

  1. High: the issue can cause significant damage, such as loss of funds or the protocol entering an unrecoverable state;
  2. Medium: the issue can cause moderate damage, such as impacts that only affect a small group of users or only a particular part of the protocol;
  3. Low: the issue can cause little to no damage, such as bugs that are easily recoverable or cause unexpected interactions that cause minor inconveniences.

When defining the impact of a finding, other factors are also considered. These can include but are not limited to data and state integrity, loss of availability, financial loss, and reputation damage. After defining the likelihood and impact of an issue, the severity is determined according to the table below.

Impact / LikelihoodLowMediumHigh
HighMediumHighCritical
MediumLowMediumHigh
LowInfo / Best PracticesLowMedium
UndeterminedUndeterminedUndeterminedUndetermined

To address issues that do not fit a High, Medium or Low severity, Nethermind Security also uses three more finding severities: Informational, Best Practices, and Undetermined.

  1. Informational findings do not pose any risk to the application, but they carry some information that the audit team intends to pass to the client formally;
  2. Best Practice findings are used when some piece of code does not conform with smart contract development best practices;
  3. Undetermined findings are used when we cannot predict the impact or likelihood of the issue.

6

Issues

6.1 Changes to yieldRate cause wrong computation of rewards

FileFix commitTypeStatus
src/WLDVault.sol—InfoAcknowledged

The interest computed in the calculateInterest(...) function is based on the currently set yieldRate and is not tied to any time periods. This means that after the yieldRate is increased, the protocol will pay more to users who have not yet triggered the interest calculation for the current period.

function calculateInterest(
Deposit memory userDeposit
) internal view returns (uint256) {
// ...
// @audit When yieldRate goes up,
// the interest for the current timeElapsed goes up as well
return
(userDeposit.amount * yieldRate * timeElapsed) /
(SECONDS_PER_YEAR * ONE_HUNDRED_PERCENT);
}

Users who don't call refresh(...) and don't extend their verification are at an advantage compared to users who do since they benefit from the higher yield for the past period.

Recommendation

Consider revisiting the interest calculation logic to correctly account for changes to the yieldRate.

6.2 The AllowanceModule cannot be changed after it is set

FileFix commitTypeStatus
src/WLDVault.sol—InfoAcknowledged

The AllowanceModule is set once during the WLDVault contract's construction. The WLDVault contract does not define the functionality to change the address of the AllowanceModule similar to the setter functions for other key parameters. A lack of a dedicated setter function might cause inconvenience when the Safe module must be updated to a new version. Since the AllowanceModule is not an upgradeable contract, a new version would have to be redeployed at a new address, forcing the redeployment of the WLDVault contract as well.

Recommendation

If the AllowanceModule contract is expected to change in the future, consider implementing a setter function to change the AllowanceModule address after the initial deployment.

6.3 Users must trigger calculateInterest(...) whenever their verified endTime changes to receive the correct rewards

FileFix commitTypeStatus
src/WLDVault.sol—InfoAcknowledged

Whenever users call refresh(...) their current verification endTime is attached to their deposit and marked as the end timestamp of interest accrual. Since only the endTime is tracked, users can miss the reward period if they extend the verification time but forget to call refresh(...). Consider the following scenario:

  • Alice verifies herself at time T and calls refresh(...);
  • Her verification endTime is set to T+1;
  • Between T and T+1, she extends her verification in the AddressBook until T+2 but does not call refresh(...);
  • Between T+1 and T+2, she extends the verification to T+3 and calls refresh(...) towards the end of the period at T+2;
Timeline (T, T+1, T+2, T+3):
-T----------------------T+1---------------------T+2----------------------T+3->
| Period T -> T + 1 | Period T+1 -> T+2 | Period T+2 -> T+3 |
| (user is verified) | (user is verified) | (user is verified) |
|call refresh(...) at T | |call refresh(...) at T+2|
+------------------------------------------------------------------------+
| Interest Accrual |
+------------------------------------------------------------------------+
| Earns Interest for |No call to refresh(...)| Last saved endTime=T+1 |
| Period T->T+1 | No interest accrued | New endTime=T+3 |
| Extends Verif to T+2 | Extends Verif to T+3 |Missed yield T+1 -> T+2 |
+-----------------------+-----------------------+------------------------+

Since Alice didn't call refresh(...) after extending the verification endTime to T+2, from the perspective of the WLDVault contract, it looks like she skipped T+2 and went straight from T+1 to T+3. Even though Alice was verified from T+1 to T+2, she won't earn the interest for that period.

Recommendation

Consider documenting this behavior so that users are aware of the need to call refresh(...) to earn the rewards for all time periods when verified.

6.4 Users that refresh interest often, accrue the yield faster due to compounding

FileFix commitTypeStatus
src/WLDVault.sol—InfoAcknowledged

Whenever the interest is refreshed for a verified user, the interest accrued for the current period is added to the user's balance.

function refreshInterest(Deposit storage userDeposit) internal {
if (userDeposit.amount == 0) return;

uint256 interest = calculateInterest(userDeposit);

// @audit Interest is added to the user's balance
userDeposit.amount += interest;
userDeposit.lastInterestCalculation = block.timestamp;
}

Subsequently, when interest is refreshed again, the user's balance is used to compute the interest for that period.

function calculateInterest(
Deposit memory userDeposit
) internal view returns (uint256) {
// ...
// @audit
return
(userDeposit.amount * yieldRate * timeElapsed) /
(SECONDS_PER_YEAR * ONE_HUNDRED_PERCENT);
}

This setup leads to a compounding effect, where users who refresh their interest frequently will accrue interest faster than users who refresh less often. This happens because each time the interest is refreshed, the newly accumulated interest is added to the balance, which increases the principal for the following interest calculation. The more frequently interest is compounded, the faster the overall balance grows.

This behavior may lead to an uneven distribution of yields, favoring users who call the refresh function more frequently. The overall impact of this mechanism on the protocol is minimized because of the maxYieldAmount cap that limits the amount of tokens the users can earn. However, some users can reach the cap faster than others in the current setup.

Recommendation

Consider revisiting the yield strategy to make it stable for the users.

6.5 Missing address(0) checks

FileFix commitTypeStatus
src/WLDVault.sol—Best PracticesAcknowledged

The AlowanceModule address can be set to address(0) in the WLDVault contract's constructor. In this state, the withdrawals from the yield source would revert since the address(0) does not have the executeAllowanceTransfer(...) function defined. The WLDVault contract would have to be re-deployed to work correctly.

Recommendation

To keep the logic consistent with the rest of the existing code, consider implementing an address(0) check for the AllowanceModule address in the constructor.

7

Documentation Evaluation

Software documentation refers to the written or visual information that describes the functionality, architecture, design, and implementation of software. It provides a comprehensive overview of the software system and helps users, developers, and stakeholders understand how the software works, how to use it, and how to maintain it. Software documentation can take different forms, such as user manuals, system manuals, technical specifications, requirements documents, design documents, and code comments. Software documentation is critical in software development, enabling effective communication between developers, testers, users, and other stakeholders. It helps to ensure that everyone involved in the development process has a shared understanding of the software system and its functionality. Moreover, software documentation can improve software maintenance by providing a clear and complete understanding of the software system, making it easier for developers to maintain, modify, and update the software over time. Smart contracts can use various types of software documentation. Some of the most common types include:

  • Technical whitepaper: A technical whitepaper is a comprehensive document describing the smart contract’s design and technical details. It includes information about the purpose of the contract, its architecture, its components, and how they interact with each other;
  • User manual: A user manual is a document that provides information about how to use the smart contract. It includes step-by-step instructions on how to perform various tasks and explains the different features and functionalities of the contract;
  • Code documentation: Code documentation is a document that provides details about the code of the smart contract. It includes information about the functions, variables, and classes used in the code, as well as explanations of how they work;
  • API documentation: API documentation is a document that provides information about the API (Application Programming Interface) of the smart contract. It includes details about the methods, parameters, and responses that can be used to interact with the contract;
  • Testing documentation: Testing documentation is a document that provides information about how the smart contract was tested. It includes details about the test cases that were used, the results of the tests, and any issues that were identified during testing;
  • Audit documentation: Audit documentation includes reports, notes, and other materials related to the security audit of the smart contract. This type of documentation is critical in ensuring that the smart contract is secure and free from vulnerabilities.

These types of documentation are essential for smart contract development and maintenance. They help ensure that the contract is properly designed, implemented, and tested, and they provide a reference for developers who need to modify or maintain the contract in the future.

8

Remarks & Test Suite

The Worldcoin team has provided documentation about their protocol in the form of in-line comments within the code. However, these comments did not provide a comprehensive explanation of the architecture and decisions behind various functionalities. Despite this, the Worldcoin team was available to address any questions or concerns from the Nethermind Security team.

Test suite
65Tests passed1Failed

The complete test output is in the full report.

As auditors, we recommend that any changes or updates made to the audited codebase undergo a re-audit or security review to address potential vulnerabilities or risks introduced by the modifications. By conducting a re-audit or security review of the modified codebase, you can significantly enhance the overall security of your system and reduce the likelihood of exploitation. However, we do not possess the authority or right to impose obligations or restrictions on our clients regarding codebase updates, modifications, or subsequent audits. Accordingly, the decision to seek a re-audit or security review lies solely with you.

This report is based on the scope of materials and documentation provided by you to Nethermind in order that Nethermind could conduct the security review outlined in 1. Executive Summary and 2. Audited Files. The results set out in this report may not be complete nor inclusive of all vulnerabilities. Nethermind has provided the review and this report on an as-is, where-is, and as-available basis. You agree that your access and/or use, including but not limited to any associated services, products, protocols, platforms, content, and materials, will be at your sole risk. Blockchain technology remains under development and is subject to unknown risks and flaws. The review does not extend to the compiler layer, or any other areas beyond the programming language, or other programming aspects that could present security risks. This report does not indicate the endorsement of any particular project or team, nor guarantee its security. No third party should rely on this report in any way, including for the purpose of making any decisions to buy or sell a product, service or any other asset. To the fullest extent permitted by law, Nethermind disclaims any liability in connection with this report, its content, and any related services and products and your use thereof, including, without limitation, the implied warranties of merchantability, fitness for a particular purpose, and non-infringement. Nethermind does not warrant, endorse, guarantee, or assume responsibility for any product or service advertised or offered by a third party through the product, any open source or third-party software, code, libraries, materials, or information linked to, called by, referenced by or accessible through the report, its content, and the related services and products, any hyperlinked websites, any websites or mobile applications appearing on any advertising, and Nethermind will not be a party to or in any way be responsible for monitoring any transaction between you and any third-party providers of products or services. As with the purchase or use of a product or service through any medium or in any environment, you should use your best judgment and exercise caution where appropriate. FOR AVOIDANCE OF DOUBT, THE REPORT, ITS CONTENT, ACCESS, AND/OR USAGE THEREOF, INCLUDING ANY ASSOCIATED SERVICES OR MATERIALS, SHALL NOT BE CONSIDERED OR RELIED UPON AS ANY FORM OF FINANCIAL, INVESTMENT, TAX, LEGAL, REGULATORY, OR OTHER ADVICE.