Coming soon
Starts null

Virtuals Protocol

Co-ownership of AI agents in entertainment and gaming

  • Start date19 Mar 2025
  • End date2 Apr 2025
  • Total awards$60,000 in USDT
  • Duration14 days

Virtuals Protocol audit details

  • Total Prize Pool: $60,000 in USDT
    • HM awards: $47,800 in USDT
      • If no valid Highs or Mediums are found, the HM pool is $0
    • QA awards: $2,000 in USDT
    • Judge awards: $5,800 in USDT
    • Validator awards: $3,900 in USDT
    • Scout awards: $500 in USDT
  • Read our guidelines for more details
  • Starts April 17, 2025 20:00 UTC
  • Ends May 7, 2025 20:00 UTC

❗️ Important notes

  1. A coded, runnable PoC is required for all High/Medium submissions to this audit.
    • An example file was provided and can be run using npx hardhat test test/poc.js
    • Exception: PoC is optional (though recommended) for wardens with signal ≥ 0.68
    • The audit repo includes a basic template to run the test suite.
    • Your submission will be marked as Insufficient if the POC is not runnable and working.
  2. Since this audit includes live/deployed code, all submissions will be treated as sensitive:
    • Wardens are encouraged to use the sensitive disclosure process for High-risk submissions affecting live code, to ensure timely disclosure of such vulnerabilities to the sponsor and guarantee payout in the case where a sponsor patches a live critical during the audit.
    • Submissions will be hidden from all wardens (SR and non-SR alike) by default, to ensure that no sensitive issues are erroneously shared.
    • If the submissions include findings affecting live code, there will be no post-judging QA phase. This ensures that awards can be distributed in a timely fashion, without compromising the security of the project. (Senior members of C4 staff will review the judges’ decisions per usual.)
    • By default, submissions will not be made public until the report is published.
    • Exception: if the sponsor indicates that no submissions affect live code, then we’ll make submissions visible to all authenticated wardens, and open PJQA to SR wardens per the usual C4 process.
  3. Judging phase risk adjustments:
    • High- or Medium-risk submissions downgraded to Low-risk (QA) will be ineligible for awards.
    • Upgrading a Low-risk finding from a QA report to a Medium- or High-risk finding is not supported.
    • As such, wardens are encouraged to select the appropriate risk level carefully during the submission phase.

Automated Findings / Publicly Known Issues

The 4naly3er report can be found here.

The Slither report can be found here.

Note for C4 wardens: Anything included in this Automated Findings / Publicly Known Issues section is considered a publicly known issue and is ineligible for awards.

Many protocol contracts have some form of Admin or role-related execution functions. All of these higher access functions are separate multi-sig wallets such as Fireblocks, that require at least 2 of 3 approval to run

Overview

ContractPurposeAccess ControlUpgradable
veVirtualTokenThis is a non-transferrable voting token to be used to vote on Virtual Protocol DAO and Virtual Genesis DAOOwnableN
VirtualProtocolDAORegular DAO to maintain the VIRTUAL ecosystem-N
VirtualGenesisDAOUsed to vote for instantiation of a VIRTUAL. This DAO allows early execution of proposal as soon as quorum (10k votes) is reached.-N
AgentFactoryHandles the application & instantiation of a new VIRTUAL. References to TBA registry, VIRTUAL DAO/Token implementation and Persona NFT vault contracts are stored here.Roles : DEFAULT_ADMIN_ROLE, WITHDRAW_ROLEY
AgentNftThis is the main registry for Persona, Core and Validator. Used to generate ICV wallet address.Roles: DEFAULT_ADMIN_ROLE, VALIDATOR_ADMIN_ROLE, MINTER_ROLEY
ContributionNftEach contribution will mint a new ContributionNft. Anyone can propose a new contribution at the VIRTUAL DAO and mint token using the proposal Id.-Y
ServiceNftAccepted contribution will mint a ServiceNft, restricted to only VIRTUAL DAO can mint a ServiceNft. User can query the latest service NFT for a VIRTUAL CORE.-Y
AgentTokenThis is implementation contract for VIRTUAL staking. AgentFactory will clone this during VIRTUAL instantiation. Staked token is non-transferable.-N
AgentDAOThis is implementation contract for VIRTUAL specific DAO. AgentFactory will clone this during VIRTUAL instantiation. It holds the maturity score for each core service.-N
AgentRewardThis is reward distribution center.Roles: GOV_ROLE, TOKEN_SAVER_ROLEY
TimeLockStakingAllows user to stake their VIRTUAL in exchange for sVIRTUALRoles: GOV_ROLE, TOKEN_SAVER_ROLEN
VirtualVIRTUAL tokenOwnableN
AirdropAirdrop token to holders-N

Main Activities

VIRTUAL Genesis

  1. Submit a new application at AgentFactory
    a. It will transfer VIRTUAL to AgentFactory
  2. Propose at VirtualGenesisDAO (action = VirtualFactory.executeApplication )
  3. Start voting at VirtualGenesisDAO
  4. Execute proposal at VirtualGenesisDAO , it will do following:
    a. Clone AgentToken
    b. Clone AgentDAO
    c. Mint AgentNft
    d. Stake VIRTUAL -> $`PERSONA (depending on the symbol sent to application)
    e. Create TBA with AgentNft

Submit Contribution

  1. Create proposal at AgentDAO (action = ServiceNft.mint)
  2. Mint ContributionNft , it will authenticate by checking whether sender is the proposal's proposer.

Upgrading Core

  1. Validator vote for contribution proposal at AgentDAO
  2. Execute proposal at AgentDAO, it will mint a ServiceNft, and trigger following actions:
    a. Update maturity score
    b. Update VIRTUAL core service id.

Distribute Reward

  1. On daily basis, protocol backend will conclude daily profits into a single amount.
  2. Protocol backend calls AgentReward.distributeRewards , triggering following:
    a. Transfer VIRTUAL into AgentReward
    b. Account & update claimable amounts for: Protocol, Stakers, Validators, Dataset Contributors, Model Contributors

Claim Reward

  1. Protocol calls AgentReward.withdrawProtocolRewards
  2. Stakers, Validators, Dataset Contributors, Model Contributors calls AgentReward.claimAllRewards

Staking VIRTUAL

  1. Call AgentToken.stake , pass in the validator that you would like to delegate your voting power to. It will take in sVIRTUAL and mint $`PERSONA to you.
  2. Call AgentToken.withdraw to withdraw , will burn your $`PERSONA and return sVIRTUAL to you.

Scope

See scope.txt

Files in scope

FileLogic ContractsInterfacesnSLOCPurposeLibraries used
/contracts/AgentInference.sol1****63@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/utils/math/Math.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol
/contracts/contribution/ContributionNft.sol1****113@openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol, @openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol, @openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol, @openzeppelin/contracts/access/AccessControl.sol, @openzeppelin/contracts/interfaces/IERC5805.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
/contracts/contribution/IContributionNft.sol****14@openzeppelin/contracts/governance/IGovernor.sol
/contracts/contribution/IServiceNft.sol****13
/contracts/contribution/ServiceNft.sol1****124@openzeppelin/contracts/governance/IGovernor.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol, @openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol, @openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol, @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol, @openzeppelin/contracts/interfaces/IERC5805.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
/contracts/fun/Bonding.sol1****300@openzeppelin/contracts/utils/ReentrancyGuard.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol
/contracts/fun/FERC20.sol1****94@openzeppelin/contracts/access/Ownable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol
/contracts/fun/FFactory.sol1****58@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol, @openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol
/contracts/fun/FPair.sol1****78@openzeppelin/contracts/utils/ReentrancyGuard.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
/contracts/fun/FRouter.sol1****108@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol
/contracts/fun/IFPair.sol****13
/contracts/genesis/FGenesis.sol1****115@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
/contracts/genesis/Genesis.sol1****353@openzeppelin/contracts/utils/ReentrancyGuard.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
/contracts/genesis/GenesisLib.sol1****35
/contracts/genesis/GenesisTypes.sol********34
/contracts/genesis/MockAgentFactoryV3.sol1****40@openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol, @openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol
/contracts/genesis/MockERC20.sol1****12@openzeppelin/contracts/token/ERC20/ERC20.sol
/contracts/governance/GovernorCountingSimple.sol1****54@openzeppelin/contracts/governance/Governor.sol
/contracts/governance/VirtualGenesisDAO.sol1****95@openzeppelin/contracts/governance/Governor.sol, @openzeppelin/contracts/governance/extensions/GovernorSettings.sol, @openzeppelin/contracts/governance/extensions/GovernorStorage.sol, @openzeppelin/contracts/governance/extensions/GovernorVotes.sol, @openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol, @openzeppelin/contracts/utils/structs/Checkpoints.sol, @openzeppelin/contracts/access/AccessControl.sol
/contracts/governance/VirtualProtocolDAO.sol1****49@openzeppelin/contracts/governance/Governor.sol, @openzeppelin/contracts/governance/extensions/GovernorSettings.sol, @openzeppelin/contracts/governance/extensions/GovernorStorage.sol, @openzeppelin/contracts/governance/extensions/GovernorVotes.sol, @openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol
/contracts/governance/veVirtualToken.sol1****32@openzeppelin/contracts/token/ERC20/ERC20.sol, @openzeppelin/contracts/access/Ownable.sol, @openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol, @openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol
/contracts/libs/AddressCheckpoints.sol1****61@openzeppelin/contracts/utils/math/Math.sol
/contracts/libs/Elo.sol1****32
/contracts/libs/FixedPointMathLib.sol1****131
/contracts/libs/IERC6551Registry.sol****111
/contracts/libs/RewardSettingsCheckpoints.sol1****68@openzeppelin/contracts/utils/math/Math.sol
/contracts/libs/RewardSettingsCheckpointsV2.sol1****65@openzeppelin/contracts/utils/math/Math.sol
/contracts/libs/TokenSaver.sol1****17@openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts/access/AccessControl.sol
/contracts/pool/AeroAdaptor.sol1136@openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
/contracts/pool/IRouter.sol****13
/contracts/pool/IUniswapV2Factory.sol****14
/contracts/pool/IUniswapV2Pair.sol****15
/contracts/pool/IUniswapV2Router01.sol****13
/contracts/pool/IUniswapV2Router02.sol****14
/contracts/tax/AgentTax.sol1****219@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/utils/math/Math.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol
/contracts/tax/BondingTax.sol1****112@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/utils/math/Math.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol
/contracts/tax/IBondingTax.sol****13
/contracts/tax/ITBABonus.sol****13
/contracts/tax/LPRefund.sol1****45@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol
/contracts/tax/TBABonus.sol1****61@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol
/contracts/token/Airdrop.sol1****45@openzeppelin/contracts/token/ERC20/ERC20.sol
/contracts/token/Virtual.sol1****15@openzeppelin/contracts/token/ERC20/ERC20.sol, @openzeppelin/contracts/token/ERC20/extensions/ERC20Capped.sol, @openzeppelin/contracts/access/Ownable.sol
/contracts/virtualPersona/AgentDAO.sol1****150@openzeppelin/contracts-upgradeable/governance/extensions/GovernorSettingsUpgradeable.sol, @openzeppelin/contracts-upgradeable/governance/extensions/GovernorStorageUpgradeable.sol, @openzeppelin/contracts-upgradeable/governance/extensions/GovernorVotesQuorumFractionUpgradeable.sol, @openzeppelin/contracts/utils/structs/Checkpoints.sol, @openzeppelin/contracts/token/ERC721/IERC721.sol, @openzeppelin/contracts/utils/Strings.sol
/contracts/virtualPersona/AgentFactory.sol1****275@openzeppelin/contracts/proxy/Clones.sol, @openzeppelin/contracts/governance/IGovernor.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts/access/AccessControl.sol, @openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol
/contracts/virtualPersona/AgentFactoryV3.sol1****327@openzeppelin/contracts/proxy/Clones.sol, @openzeppelin/contracts/governance/IGovernor.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts/access/AccessControl.sol, @openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol
/contracts/virtualPersona/AgentFactoryV4.sol1****369@openzeppelin/contracts/proxy/Clones.sol, @openzeppelin/contracts/governance/IGovernor.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts/access/AccessControl.sol, @openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol
/contracts/virtualPersona/AgentMigrator.sol1****129@openzeppelin/contracts/proxy/Clones.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/governance/IGovernor.sol, @openzeppelin/contracts/access/Ownable.sol, @openzeppelin/contracts/utils/Pausable.sol
/contracts/virtualPersona/AgentNftV2.sol1****192@openzeppelin/contracts/governance/extensions/GovernorVotes.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/interfaces/IERC5805.sol, @openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol, @openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol, @openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol, @openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
/contracts/virtualPersona/AgentToken.sol1****422@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol, @openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol, @openzeppelin/contracts/utils/structs/EnumerableSet.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
/contracts/virtualPersona/AgentVeToken.sol1****98@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol, @openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol, @openzeppelin/contracts/utils/structs/Checkpoints.sol, @openzeppelin/contracts/access/IAccessControl.sol
/contracts/virtualPersona/CoreRegistry.sol1****18@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
/contracts/virtualPersona/ERC20Votes.sol1****19@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol, @openzeppelin/contracts/utils/math/SafeCast.sol
/contracts/virtualPersona/EloCalculator.sol1****49@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol, @openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol, @openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol
/contracts/virtualPersona/GovernorCountingSimpleUpgradeable.sol1****55@openzeppelin/contracts-upgradeable/governance/GovernorUpgradeable.sol
/contracts/virtualPersona/IAgentDAO.sol****15@openzeppelin/contracts/governance/IGovernor.sol, @openzeppelin/contracts/governance/utils/IVotes.sol
/contracts/virtualPersona/IAgentFactory.sol****14@openzeppelin/contracts/governance/IGovernor.sol
/contracts/virtualPersona/IAgentFactoryV3.sol****14@openzeppelin/contracts/governance/IGovernor.sol
/contracts/virtualPersona/IAgentFactoryV4.sol****14@openzeppelin/contracts/governance/IGovernor.sol
/contracts/virtualPersona/IAgentNft.sol****116
/contracts/virtualPersona/IAgentToken.sol****1144@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol, @openzeppelin/contracts/token/ERC20/IERC20.sol
/contracts/virtualPersona/IAgentVeToken.sol****13
/contracts/virtualPersona/IERC20Config.sol****128
/contracts/virtualPersona/IEloCalculator.sol****13
/contracts/virtualPersona/IErrors.sol****1156
/contracts/virtualPersona/IExecutionInterface.sol****13
/contracts/virtualPersona/IValidatorRegistry.sol****14
/contracts/virtualPersona/ValidatorRegistry.sol1****51@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol
Totals43245238

Files out of scope

See out_of_scope.txt

File
./contracts/AgentRewardV2.sol
./contracts/AgentRewardV3.sol
./contracts/IAgentReward.sol
./contracts/IAgentRewardV3.sol
./contracts/dev/BMWToken.sol
./contracts/dev/BMWTokenChild.sol
./contracts/dev/ERC6551BytecodeLib.sol
./contracts/dev/ERC6551Registry.sol
./contracts/dev/FxERC20ChildTunnel.sol
./contracts/dev/FxERC20RootTunnel.sol
./contracts/dev/ProxyAdmin.sol
./contracts/dev/tba/lib/ERC6551AccountLib.sol
./contracts/dev/tba/lib/ERC6551BytecodeLib.sol
./contracts/token/IMinter.sol
./contracts/token/Minter.sol
Totals: 15

Scoping Q & A

General questions

QuestionAnswer
ERC20 used by the protocolVIRTUAL Creating new agent tokens
ERC721 used by the protocolCreating new ERC721 tokens as proof of agentic work
ERC777 used by the protocolNone
ERC1155 used by the protocolNone
Chains the protocol will be deployed onBase

External integrations (e.g., Uniswap) behavior in scope

QuestionAnswer
Enabling/disabling fees (e.g. Blur disables/enables fees)Yes
Pausability (e.g. Uniswap pool gets paused)No
Upgradeability (e.g. Uniswap gets upgraded)Yes

EIP compliance checklist

N/A

Additional context

Main invariants

N/A

Attack ideas (where to focus for bugs)

Bonding pool

Is there a way for users to pull liquidity from the bonding pool

Any risk of loss of funds

Access control related

Are all privileged actions guarded by access controls

All trusted roles in the protocol

RoleDescription
Admincontrols fees and other higher-order functions
ExecutorRuns execution functions like fee/tax distribution
DeployerDeployer for contracts

Describe any novel or unique curve logic or mathematical models implemented in the contracts

Simple bonding curve with a y = kx

Running tests

git clone --recurse https://github.com/code-423n4/2025-04-virtuals-protocol.git cd 2025-04-virtuals-protocol yarn npx hardhat test

To run code coverage

npx hardhat coverage

Miscellaneous

Employees of Virtuals and employees' family members are ineligible to participate in this audit.

Code4rena's rules cannot be overridden by the contents of this README. In case of doubt, please check with C4 staff.