Skip to content
SECTION 10 / 15

Contract reference

Every VIRUS contract function, event, error and struct with exact signatures and access, plus the Pons functions VIRUS calls.

§Conventions

  • Solidity 0.8.30, EVM cancun, OpenZeppelin 5.x (SafeERC20, ReentrancyGuard, Ownable2Step, Clones).
  • No upgradeability. The only proxies are the immutable EIP-1167 feeder clones.
  • "Access" names who may call a function successfully. "Anyone" means no restriction.
  • Errors inherited from OpenZeppelin (for example OwnableUnauthorizedAccount(address), ReentrancyGuardReentrantCall(), SafeERC20FailedOperation(address)) can also surface and are not repeated per contract.

§VirusLaunchCoordinator

Launches a virus on Pons V2 in one transaction: host checks, live economics, a unique feeder, the Pons launch with that feeder as creator fee recipient, post-launch verification, binding, registration, and an optional first buy carrying the 2.5% inoculation fee. It has no admin, no receive function and no trade function, and holds no ETH or tokens between transactions.

§Functions

constructor(IPonsV2LaunchFactory ponsFactory_, IVirusRegistry registry_, VirusFeederFactory feederFactory_);

function inoculate(InoculateParams calldata p)
    external payable returns (address virus, address curve, address feeder);

function hostCheck(address host) public view returns (HostCheck memory c);
function previewEconomics(uint256 launchConfigId) public view returns (EconomicsPreview memory e);
function previewInoculation(address caller, address host, uint256 launchConfigId, uint256 firstBuy)
    external view returns (InoculationPreview memory pv);
function predictFeeder(address developer, bytes32 userSalt, address host) external view returns (address);

function ponsFactory() external view returns (IPonsV2LaunchFactory);   // immutable
function registry() external view returns (IVirusRegistry);             // immutable
function feederFactory() external view returns (VirusFeederFactory);    // immutable
Function Access Notes
constructor Deployer Reverts WiringMismatch if feederFactory_.registry() != registry_ or the Pons factory has no code.
inoculate Anyone who passes the Pons gate payable, nonReentrant. msg.value must equal ponsFactory.launchFee(). See Launching.
hostCheck View Runs checks A, B, E, F, G, H. viable is true only if all pass.
previewEconomics View Live config and live currentFeePolicy(), the computed creator tax, both phase fee rows, parity, launch fee.
previewInoculation View Everything the confirmation screen shows for caller, in one call.
predictFeeder View Feeder address for (developer, userSalt, host) under the current treasury.

§Structs

struct Metadata {
    string name;
    string symbol;
    string logo;
    string description;
    IPonsV2LaunchFactory.Socials socials;   // twitter, telegram, discord, website, farcaster
}

struct InoculateParams {
    address host;
    uint256 launchConfigId;
    bytes32 expectedPonsEconomics;
    uint16 expectedCreatorTaxBps;
    bytes32 salt;
    uint256 firstBuy;
    uint256 minVirusOut;
    uint256 deadline;
    Metadata meta;
}

struct HostCheck {
    bool isContract;
    bool ponsLaunchToken;
    bool provenance;
    bool pairApproved;
    bool economicsUsable;
    bool notDisabled;
    bool depthOk;
    bool viable;
    uint8 hostDepth;
    uint8 childDepth;
    uint8 decimals;
    address ponsCurve;
    address ponsPairToken;
    uint256 phantomQuote;
    uint256 graduationThreshold;
}

struct EconomicsPreview {
    uint256 launchConfigId;
    bool configEnabled;
    uint256 supply;
    uint256 curveFeeBps;
    uint256 hookFeeBps;
    uint256 protocolShareBps;
    uint256 creatorTaxBps;
    uint256 maxCreatorTaxBps;
    bool taxWithinCap;
    VirusEconomics.PhaseFees curvePhase;
    VirusEconomics.PhaseFees poolPhase;
    bool parityExact;
    uint256 launchFeeWei;
}

struct InoculationPreview {
    HostCheck host;
    EconomicsPreview economics;
    bytes32 ponsEconomicsDigest;
    uint256 firstBuy;
    uint256 inoculationFee;
    uint256 totalHostRequired;
    uint256 estimatedVirusOut;
    bool callerCanLaunch;
    bool coordinatorCanLaunch;
    bool launchesPaused;
    bool canInoculate;
}

§Events

Event Fields
Inoculated address indexed virus, address indexed host, address indexed developer, address feeder, address curve, uint16 creatorTaxBps, bytes32 ponsEconomics
FirstInfection address indexed virus, address indexed developer, uint256 firstBuy, uint256 inoculationFee, uint256 virusOut, uint256 hostRefund

In FirstInfection, firstBuy is the host actually spent after any partial fill, and hostRefund is the unspent host plus the unearned part of the fee.

§Errors

Error Raised when
Expired() block.timestamp > deadline
LaunchesPaused() VIRUS launches are paused in the registry
CallerCannotLaunch() ponsFactory.canLaunch(msg.sender) is false
CoordinatorCannotLaunch() ponsFactory.canLaunch(coordinator) is false
EconomicsNotPinned() expectedPonsEconomics is zero
ZeroSlippageBound() firstBuy > 0 and minVirusOut == 0
WrongLaunchFee(uint256 sent, uint256 required) msg.value != launchFee()
HostNotViable(address host) hostCheck(host).viable is false
ConfigDisabled(uint256 launchConfigId) The Pons launch config is not enabled
CreatorTaxUnreachable(uint256 required, uint256 cap) The tax exceeds maxCreatorTaxBps, does not fit uint16, or breaks a 2,000bps combined ceiling. Reports the tax and maxCreatorTaxBps in every case.
CreatorTaxMismatch(uint256 expected, uint256 actual) The live tax differs from expectedCreatorTaxBps
InvalidMetadata() Empty or oversized name or symbol, or oversized logo, description or social
LaunchVerificationFailed() The Pons launch record or curve does not match what was requested
InexactHostTransfer(uint256 expected, uint256 received) The first-buy pull did not deliver exactly firstBuy + fee
WiringMismatch() Constructor wiring check failed

§VirusFeeder

The Pons creator fee recipient of exactly one virus. Deployed as an EIP-1167 clone whose immutable arguments fix the host, developer, treasury and every Pons reference; the split is in the implementation bytecode. After a one-shot bind, nothing about a feeder can change. It has no owner.

§Functions

function config() public view returns (Config memory);
function host() external view returns (address);
function developer() external view returns (address);
function treasury() external view returns (address);

function bind(address virus_, address curve_) external;
function feed() external returns (uint256 gross);
function claimDev() external returns (uint256 amount);
function burnStrayVirus() external returns (uint256 amount);
function state() external view returns (State memory s);

// public storage
function virus() external view returns (address);
function curve() external view returns (address);
function poolId() external view returns (bytes32);
function severed() external view returns (bool);
function devOwed() external view returns (uint256);
function devClaimed() external view returns (uint256);
function totalFed() external view returns (uint256);
function totalBurned() external view returns (uint256);
function totalDevBooked() external view returns (uint256);
function totalTreasury() external view returns (uint256);
function lastSweep() external view returns (SweepOutcome);
Function Access Notes
config, host, developer, treasury View, clones only Decoded from the clone's immutable arguments. Revert NotClone on the implementation.
bind The feeder factory only, once Verifies the Pons launch record (exists, token, curve, pairToken == host, creatorFeeRecipient == this, buyback off), stores virus, curve and the derived V4 poolId.
feed Anyone nonReentrant, bound clones only. Sweep where Pons allows, claim host from escrow, split balance − devOwed 50/30/20, burn, pay treasury, record. Returns the gross distributed. See Feeding.
claimDev Anyone; pays only the immutable developer nonReentrant, bound clones only. Pays all of devOwed.
burnStrayVirus Anyone nonReentrant, bound clones only. Burns the feeder's entire balance of its own virus token.
state View, bound clones only The lab readout below.

§Structs and enums

struct Config {
    address host;
    address developer;
    address treasury;
    address registry;
    address ponsFactory;
    address feeEscrow;
    address memeHook;
    address feederFactory;
}

enum SweepOutcome {
    None,              // 0: nothing to sweep in this phase
    Swept,             // 1: Pons sweep executed
    CurveSweepFailed,  // 2: curve sweep reverted; fees remain on the curve
    AwaitingPonsSweep, // 3: post-graduation fees need the Pons sweep operator first
    NotRecipient       // 4: Pons no longer routes this child's creator fees here
}

struct State {
    address virus;
    address host;
    address developer;
    address treasury;
    PonsGraduationPhase phase;
    bool isPonsRecipient;
    uint256 escrowed;       // host credited to this feeder in the Pons escrow
    uint256 undistributed;  // host held and not yet distributed (excludes devOwed)
    uint256 devOwed;
    uint256 devClaimed;
    uint256 totalFed;
    uint256 totalBurned;
    uint256 totalTreasury;
}

§Events

Event Fields
Bound address indexed virus, address indexed curve, bytes32 poolId
VirusFed address indexed virus, address indexed host, uint256 grossReceived, uint256 burned, uint256 devBooked, uint256 treasuryAmount, uint256 hostTotalSupplyAfter, SweepOutcome sweep
DevClaimed address indexed developer, uint256 amount
RecipientStatus bool active
StrayVirusBurned uint256 amount

§Errors

Error Raised when
NotClone() A clone-only function is called on the implementation
NotFeederFactory() bind is called by anyone but the feeder factory
AlreadyBound() bind is called a second time
NotBound() feed, claimDev, burnStrayVirus or state before binding
BindMismatch() The Pons launch record does not match the feeder
NothingToClaim() claimDev with devOwed == 0, or burnStrayVirus with no virus balance
BurnNotReflected(uint256 expected, uint256 actual) Host totalSupply did not fall by exactly the burned amount

§What the feeder cannot do

It has no function that calls Pons transferCreatorFeeRecipient or setBuybackEnabled, makes an arbitrary call, accepts a caller-supplied target or calldata, or moves host anywhere except the burn, the treasury transfer and the developer payout. Every external call targets an immutable clone argument or the bound virus (security invariant 14).

§VirusFeederFactory

Deploys one deterministic feeder clone per virus and binds it exactly once. A feeder is never reused and a virus never gets a second feeder. Its feeder implementation is created in its constructor and is immutable.

§Functions

constructor(IVirusRegistry registry_);

function deployFeeder(bytes32 salt, VirusFeeder.Config calldata cfg) external returns (address feeder);
function bind(address feeder, address virus, address curve) external;
function predictFeeder(bytes32 salt, VirusFeeder.Config calldata cfg) external view returns (address);

function registry() external view returns (IVirusRegistry);        // immutable
function implementation() external view returns (address);          // immutable
function isFeeder(address feeder) external view returns (bool);
function virusOfFeeder(address feeder) external view returns (address);
function feederOf(address virus) external view returns (address);
Function Access Notes
deployFeeder registry.coordinator() only Clones.cloneDeterministicWithImmutableArgs(implementation, abi.encode(cfg), salt). Requires cfg.feederFactory == this and cfg.registry == registry.
bind registry.coordinator() only One-shot on both sides; calls feeder.bind(virus, curve).
predictFeeder View CREATE2 address for (salt, cfg).

§Events

Event Fields
FeederDeployed address indexed feeder, address indexed host, address indexed developer, bytes32 salt
FeederBound address indexed feeder, address indexed virus, address curve

§Errors

Error Raised when
NotCoordinator() Caller is not the registry's current coordinator
ConfigMismatch() cfg names a different factory or registry
UnknownFeeder(address feeder) bind on an address this factory did not deploy
FeederAlreadyBound(address feeder) The feeder is already bound
VirusAlreadyHasFeeder(address virus) The virus already has a feeder

§VirusRegistry

Canonical record of every host and every virus. Ownable2Step, owned by the VIRUS admin. Writers are narrow: the current coordinator registers viruses; each virus's own feeder records its feeds, developer claims and recipient status. Feeder writes are never pausable. Nothing edits an existing record.

§Functions

constructor(address initialOwner, address initialTreasury);

// admin (onlyOwner)
function setLaunchesPaused(bool paused) external;
function setHostDisabled(address host, bool disabled) external;
function proposeTreasury(address proposed) external;
function cancelTreasury() external;
function executeTreasury() external;
function setInitialCoordinator(address initial) external;
function proposeCoordinator(address proposed) external;
function cancelCoordinator() external;
function executeCoordinator() external;
function renounceOwnership() public pure;            // always reverts

// coordinator
function registerVirus(Registration calldata r) external returns (uint32 strain);

// feeders
function recordFeed(uint256 gross, uint256 burned, uint256 devBooked, uint256 treasuryAmount, uint256 hostSupplyAfter) external;
function recordDevClaim(uint256 amount) external;
function recordRecipientStatus(bool active) external;

// views
function getHost(address host) external view returns (Host memory);
function getVirus(address token) external view returns (Virus memory);
function isVirus(address token) external view returns (bool);
function hostCount() external view returns (uint256);
function virusCount() external view returns (uint256);
function hostAt(uint256 index) external view returns (address);
function virusAt(uint256 index) external view returns (address);
function childrenOf(address host) external view returns (address[] memory);
function virusesOf(address developer) external view returns (address[] memory);
function hosts(uint256 offset, uint256 limit) external view returns (address[] memory);
function viruses(uint256 offset, uint256 limit) external view returns (address[] memory);

// public storage and constants
function coordinator() external view returns (address);
function treasury() external view returns (address);
function launchesPaused() external view returns (bool);
function pendingTreasury() external view returns (address);
function pendingTreasuryAt() external view returns (uint256);
function pendingCoordinator() external view returns (address);
function pendingCoordinatorAt() external view returns (uint256);
function hostDisabled(address host) external view returns (bool);
function virusOfFeeder(address feeder) external view returns (address);
function TREASURY_DELAY() external view returns (uint256);      // 2 days
function COORDINATOR_DELAY() external view returns (uint256);   // 2 days

// inherited from Ownable2Step
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function transferOwnership(address newOwner) external;           // onlyOwner
function acceptOwnership() external;                             // pending owner only
Function Access Notes
setLaunchesPaused Owner Blocks inoculate and registerVirus only.
setHostDisabled Owner New launches on that host only. Reversible.
proposeTreasury / cancelTreasury / executeTreasury Owner Execute only after TREASURY_DELAY. Only feeders created afterwards use the new treasury.
setInitialCoordinator Owner, once One-time wiring at deployment.
proposeCoordinator / cancelCoordinator / executeCoordinator Owner Execute only after COORDINATOR_DELAY.
renounceOwnership — Always reverts, so the registry can never be left ownerless and paused.
registerVirus Current coordinator Blocked while paused. Assigns the next strain number. Registers the host on its first virus.
recordFeed, recordDevClaim, recordRecipientStatus The registered feeder of a virus Never pausable. The feeder can only write its own virus's record.

§Structs

struct Host {
    bool registered;
    bool disabled;
    uint8 depth;
    uint64 registeredAt;
    uint32 children;
    address ponsCurve;
    address ponsPairToken;
    uint256 totalControlledFees;
    uint256 totalBurned;
    uint256 totalDevBooked;
    uint256 totalTreasury;
    uint256 totalInoculationFees;
}

struct Virus {
    address token;
    address curve;
    address host;
    address feeder;
    address developer;
    address treasury;
    uint8 depth;
    bool active;           // false while Pons routes creator fees somewhere other than the feeder
    uint16 creatorTaxBps;
    uint32 strain;
    uint64 launchBlock;
    uint256 launchConfigId;
    uint256 graduationThreshold;
    uint256 firstBuy;
    uint256 inoculationFee;
    uint256 totalFed;
    uint256 totalBurned;
    uint256 devBooked;
    uint256 devClaimed;
    uint256 treasuryPaid;
}

struct Registration {
    address token;
    address curve;
    address host;
    address feeder;
    address developer;
    address treasury;
    uint8 depth;
    uint16 creatorTaxBps;
    uint256 launchConfigId;
    uint256 graduationThreshold;
    uint256 firstBuy;
    uint256 inoculationFee;
    address hostPonsCurve;
    address hostPonsPairToken;
}

§Events

Event Fields
HostRegistered address indexed host, uint8 depth, address ponsCurve, address ponsPairToken
HostDisabled address indexed host, bool disabled
VirusInoculated address indexed virus, address indexed host, address indexed developer, address feeder, address curve, uint32 strain, uint8 depth, uint16 creatorTaxBps, uint256 launchConfigId
InoculationRecorded address indexed virus, address treasury, uint256 graduationThreshold, uint256 firstBuy, uint256 inoculationFee
VirusFed address indexed virus, address indexed host, uint256 grossReceived, uint256 burned, uint256 devBooked, uint256 treasuryAmount, uint256 hostTotalSupplyAfter
HostBurned address indexed host, address indexed virus, uint256 amount, uint256 hostTotalSupplyAfter
DevBooked address indexed virus, address indexed developer, uint256 amount
DevClaimed address indexed virus, address indexed developer, uint256 amount
TreasuryPaid address indexed virus, address indexed treasury, uint256 amount
VirusRecipientStatus address indexed virus, bool active
LaunchesPausedSet bool paused
TreasuryUpdateProposed address indexed current, address indexed proposed, uint256 executableAt
TreasuryUpdateCancelled address indexed proposed
TreasuryUpdated address indexed previous, address indexed current
CoordinatorUpdateProposed address indexed current, address indexed proposed, uint256 executableAt
CoordinatorUpdateCancelled address indexed proposed
CoordinatorUpdated address indexed previous, address indexed current

Plus the OpenZeppelin OwnershipTransferStarted and OwnershipTransferred events.

§Errors

Error Raised when
ZeroAddress() A required address is zero
NotCoordinator() registerVirus from anyone but the coordinator
NotFeeder() A record call from an address that is not a registered feeder
LaunchesPaused() registerVirus while paused
HostIsDisabled(address host) registerVirus on a disabled host
DepthExceeded(uint8 depth) Depth above 3
InvalidDepth() Depth 0
AlreadyRegistered(address token) The virus is already registered
FeederAlreadyUsed(address feeder) The feeder already belongs to a virus
HostMismatch(address host) The implied host depth differs from the recorded one
CoordinatorAlreadySet() setInitialCoordinator a second time
NothingPending() Cancel or execute with nothing proposed
DelayNotElapsed(uint256 executableAt) Execute before the delay
OwnershipCannotBeRenounced() renounceOwnership

§VirusTreasury

Ownable2Step holder of the treasury's 20% of fed host tokens and of inoculation fees. It has no reference to feeders and no way to touch a developer's booked balance.

§Functions

constructor(address initialOwner);

function withdraw(IERC20 token, address to, uint256 amount) external;          // onlyOwner
function withdrawETH(address payable to, uint256 amount) external;             // onlyOwner
function renounceOwnership() public pure;                                       // always reverts
receive() external payable;

// inherited from Ownable2Step
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function transferOwnership(address newOwner) external;                          // onlyOwner
function acceptOwnership() external;                                            // pending owner only

§Events

Event Fields
Withdrawn address indexed token, address indexed to, uint256 amount
EthWithdrawn address indexed to, uint256 amount

§Errors

Error Raised when
ZeroAddress() to is zero
EthTransferFailed() The ETH transfer failed
OwnershipCannotBeRenounced() renounceOwnership

§VirusEconomics (library)

Internal library compiled into the contracts that use it. Mirrored bit for bit by packages/chain/src/economics.ts.

function split(uint256 gross) internal pure returns (Split memory s);
function inoculationFee(uint256 firstBuy) internal pure returns (uint256);
function creatorBaseScaled(uint256 baseFeeBps, uint256 protocolShareBps) internal pure returns (uint256);
function requiredCreatorTax(uint256 baseFeeBps, uint256 protocolShareBps) internal pure returns (uint256);
function phaseFees(uint256 baseFeeBps, uint256 protocolShareBps, uint256 creatorTaxBps)
    internal pure returns (PhaseFees memory f);
function isParity(PhaseFees memory f) internal pure returns (bool);

struct Split { uint256 burn; uint256 dev; uint256 treasury; }
struct PhaseFees { uint256 ponsScaled; uint256 feederScaled; uint256 totalScaled; }

Constants are listed in Parameters.

§Pons functions VIRUS calls

VIRUS declares only the Pons members it calls or reads, in contracts/src/interfaces/IPonsV2.sol, written from the published factory source. Each is exercised by the fork tests before deployment.

§Called by the coordinator

Contract Function Purpose
Factory canLaunch(address) Gate for the caller and for the coordinator
Factory launchFee() Exact msg.value
Factory getLaunchConfig(uint256) Config terms and enabled
Factory maxCreatorTaxBps() Tax cap
Factory memeHook(), feeEscrow() Feeder config; fee policy source
Factory getLaunchedToken(address) Host provenance, depth walk, post-launch verification
Factory approvedPairTokens(address), pairTokenEconomics(address) Checks E and F
Factory previewLaunchEconomics(uint256, address) Digest for the confirmation screen
Factory launchToken(TokenParams, uint256, address) The launch, payable
Meme hook currentFeePolicy() Live protocol share and hook fee
Curve pairToken(), feeBps(), creatorTaxBps(), token() Post-launch verification
Curve buy(uint256, uint256, address) The first buy only
Host token launchFactory(), curve(), decimals() Provenance and decimals

§Called by each feeder

Contract Function Purpose
Factory getLaunchedToken(address) Bind check, phase, recipient status
Curve sweepFees(uint256) Pre-graduation sweep with a zero buyback minimum
Meme hook sweepPoolFees(bytes32, uint256, uint256) Post-graduation sweep, as creator, with zero minimums
Fee escrow balanceOfToken(address, address), claimToken(address) Claim host
Host token balanceOf, totalSupply, burn(uint256), transfer Measure, burn, pay
Virus token balanceOf, burn(uint256) burnStrayVirus only

§Called by the app, not by VIRUS contracts

Contract Function Purpose
Factory graduate(address), createGraduatedPool(address) COMPLETE MIGRATION (sent by the user)
Factory getLaunchFeePolicy(address) Frozen per-virus economics
Factory pendingCreatorFeeRecipient(address) Owner-override detection
Factory launchConfigCount(), launchEnabled(), snipeTaxStartBps(), snipeTaxSeconds(), poolManager(), positionManager(), locker() Health and parameters
Curve buy, sell, getReserves, realQuoteReserve, sellableTokens, reservedTokens, readyToGraduate, graduated, quoteFeeBalance, creatorTaxBalance, phantomQuote, graduationThreshold, protocolFeeShareBps, deployer Trading, quotes, progress
Meme hook pendingFees(bytes32, address), pendingCreatorTax(bytes32, address) Fee pipeline

IPonsV2.sol also declares read-only members that no VIRUS contract calls: launchForwarder() (read by the fork tests), feeSweepOperator(), pendingBuyback(bytes32, address) and curve.factory(). It declares transferCreatorFeeRecipient(address, address) only so that tests can prove a developer cannot call it successfully. No VIRUS contract calls it.