ScoutFundV2
ScoutFundV2 is a protocol-owned liquidity fund that automatically invests in company tokens when they graduate from the bonding curve to Uniswap V4. It receives 25% of protocol fees via FeeSplitterV2 and deploys a fixed USDC seed amount into each newly public company.
Deployed at: 0xd840C9BD1cCA5B5F70883f173EDcc626cf1d41a2 (Base Sepolia)
Key Properties
| Property | Value |
|---|---|
| Investment Token | USDC (6 decimals) |
| Default Seed Amount | 100 USDC per company |
| Max Seed Amount | 10,000 USDC |
| Max Positions | 100 unique tokens |
| Swap Router | V4SwapHelper (0xf6E8cD6f7c4fd83b0F8a053c3B29d95383fEb4D0) |
| Default Pool Fee | 3000 (0.30%) |
How It Works
- A company token graduates from its bonding curve (market cap hits $10K).
- CompanyTokenLauncher calls
investInCompany(companyToken, 0)on ScoutFundV2. - ScoutFundV2 swaps
seedAmountUSDC for company tokens via V4SwapHelper. - Acquired tokens are held as protocol-owned liquidity (POL) permanently.
- Positions are tracked per-token with investment counts and totals.
The fund operates as a passive investor. It does not divest — all positions are held permanently. Profit can be withdrawn by an admin via withdrawProfit().
Structs
struct Position {
address token; // Company token address
uint256 totalInvested; // Cumulative USDC spent
uint256 totalTokensAcquired; // Cumulative tokens received
uint256 investmentCount; // Number of investment transactions
uint256 investedAt; // Timestamp of most recent investment
}Interface
Investment
/// @notice Invest seedAmount of USDC into a company token via Uniswap V4.
/// Only callable by addresses with INVESTOR_ROLE.
/// @param companyToken The company token to purchase
/// @param minAmountOut Minimum tokens to receive (slippage protection).
/// Use 0 only during same-tx pool initialization.
/// @return tokensOut The amount of company tokens acquired
function investInCompany(
address companyToken,
uint256 minAmountOut
) external returns (uint256 tokensOut);Admin Functions
/// @notice Update the USDC seed amount invested per company.
/// @param newSeedAmount Must be > 0 and <= MAX_SEED_AMOUNT (10,000 USDC)
function setSeedAmount(uint256 newSeedAmount) external;
/// @notice Update the default Uniswap V4 pool fee tier.
/// @param newFee Pool fee (e.g., 3000 = 0.30%)
function setDefaultPoolFee(uint24 newFee) external;
/// @notice Withdraw acquired tokens from the fund.
/// @param token The token to withdraw
/// @param amount The amount to withdraw
function withdrawProfit(address token, uint256 amount) external;
/// @notice Pause the contract (blocks all investments).
function pause() external;
/// @notice Unpause the contract.
function unpause() external;Views
/// @notice Get the full position for a specific company token.
function getPosition(address companyToken) external view returns (Position memory);
/// @notice Total number of unique positions held.
function getPositionCount() external view returns (uint256);
/// @notice The USDC amount invested per company.
function seedAmount() external view returns (uint256);
/// @notice The default Uniswap V4 pool fee tier.
function defaultPoolFee() external view returns (uint24);Events
event CompanyInvested(address indexed companyToken, uint256 usdcIn, uint256 tokensOut);
event SeedAmountUpdated(uint256 oldAmount, uint256 newAmount);
event DefaultPoolFeeUpdated(uint24 oldFee, uint24 newFee);
event ProfitWithdrawn(address indexed token, uint256 amount, address indexed to);Errors
error ZeroAddress(); // Invalid token address
error ZeroAmount(); // Seed amount is zero
error InsufficientBalance(); // Not enough USDC for investment
error SeedAmountTooHigh(); // Exceeds MAX_SEED_AMOUNT or MAX_POSITIONSRoles
| Role | Permission |
|---|---|
INVESTOR_ROLE | Call investInCompany() |
DEFAULT_ADMIN_ROLE | Config setters, withdrawProfit(), pause()/unpause() |
The INVESTOR_ROLE is typically granted to the CompanyTokenLauncher contract so it can trigger investment automatically during the graduation flow. It can also be granted to a backend worker for manual investments.
Integration with CompanyTokenLauncher
The CompanyTokenLauncher holds a reference to ScoutFundV2 and calls it during graduation:
// In CompanyTokenLauncher._graduateToV4():
if (scoutFund != address(0)) {
try IScoutFundV2(scoutFund).investInCompany(token, 0) {}
catch (bytes memory reason) {
emit ScoutInvestFailed(companyId, token, reason);
}
}The try/catch wrapper ensures a ScoutFund failure does not block graduation. If the fund has insufficient USDC or is paused, the graduation proceeds and a ScoutInvestFailed event is emitted.
Funding
The fund receives USDC from FeeSplitterV2, which routes 25% of all protocol fees to the Scout Fund. Protocol fee sources include:
- 100 USDC company registration fee
- 2% treasury funding fee
- 2% budget allocation fee
- 2% service economy fee
- 2.5% CeosHook swap fee on graduated token trades
USDC Allowance
At construction, ScoutFundV2 pre-approves the V4SwapHelper for type(uint256).max USDC. This removes the need for per-swap approval transactions, making each investment a single-call operation.
Security
ReentrancyGuardoninvestInCompany()andwithdrawProfit()Pausableallows emergency halt of all investmentsAccessControlwith separateINVESTOR_ROLEandDEFAULT_ADMIN_ROLESafeERC20for all token transfersMAX_SEED_AMOUNTcap (10,000 USDC) prevents accidental over-investmentMAX_POSITIONSlimit (100) prevents unbounded gas costs in position tracking- Pre-approved USDC allowance eliminates approval front-running risk