Quantillon Protocol Security Guide
Signed quotes are active on Base since 17 October 2026. See Signed Quotes in Production for verified bindings, transaction wrappers and emergency-mode limits.
Overview
The Quantillon Protocol implements comprehensive security measures to protect user funds and ensure protocol integrity. This document outlines security best practices, the vulnerability disclosure process, and risk management guidelines.
Security Architecture
Multi-Layer Security Model
graph TB
subgraph "Application Layer"
AC[Access Control]
RB[Role-Based Permissions]
EM[Emergency Mechanisms]
end
subgraph "Smart Contract Layer"
RP[Reentrancy Protection]
OV[Overflow Protection]
PA[Pause Mechanisms]
end
subgraph "Oracle Layer"
CB[Circuit Breakers]
PV[Price Validation]
SF[Staleness Checks]
end
subgraph "Infrastructure Layer"
UP[Upgradeable Proxies]
TL[Timelock Controls]
AU[Audit Trails]
end
subgraph "External Layer"
AE[Internal AI-assisted Security Reviews]
BB[Responsible Disclosure]
MC[Monitoring & Alerting]
end
Security Features
1. Access Control
Role-Based Access Control (RBAC):
- Hierarchical permission system
- Principle of least privilege
- Time-locked upgrades: the eight
SecureUpgradeablecore proxies (QuantillonVault, QEUROToken, QTIToken, UserPool, HedgerPool, YieldShift, stQEUROFactory, stQEUROToken) can only be upgraded through the 12 h OpenZeppelinTimelockController - Multi-signature control: every privileged role is held by the 2-of-3 governance Safe. Parameter changes, and upgrades of the plain-UUPS FeeCollector / oracle / SlippageStorage proxies, execute as direct Safe transactions with no delay (see the governance-flow column of the 2026-08-26 bundle runbook)
Key Roles per contract:
// QEUROToken
MINTER_ROLE = keccak256("MINTER_ROLE"); // Vault: mint QEURO
BURNER_ROLE = keccak256("BURNER_ROLE"); // Vault: burn QEURO
PAUSER_ROLE = keccak256("PAUSER_ROLE"); // Emergency: pause token, minting killswitch
COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE"); // Blacklist/whitelist management
// QuantillonVault / QTIToken / UserPool / HedgerPool / YieldShift / stQEUROFactory / stQEUROToken
GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE"); // Parameter updates, wiring
EMERGENCY_ROLE = keccak256("EMERGENCY_ROLE"); // Emergency pause/withdraw
VAULT_OPERATOR_ROLE = keccak256("VAULT_OPERATOR_ROLE"); // Vault: external-vault USDC deployment
YIELD_DISTRIBUTOR_ROLE = keccak256("YIELD_DISTRIBUTOR_ROLE"); // Vault: harvestAndDistributeVaultYield / creditVaultYield
VAULT_FACTORY_ROLE = keccak256("VAULT_FACTORY_ROLE"); // stQEUROFactory: vault self-registration
// HedgerPool has no hedger role: it uses a single-hedger allowlist (setSingleHedger)
// FeeCollector
GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE"); // Ratios, fund addresses, fee sources, upgrades (plain UUPS)
TREASURY_ROLE = keccak256("TREASURY_ROLE"); // distributeFees
FEE_SOURCE_ROLE = keccak256("FEE_SOURCE_ROLE"); // Contracts allowed to push fees
// OracleRouter / ChainlinkOracle / HyperliquidEurUsdOracle / StorkOracle / LighterEurUsdOracle (inert)
ORACLE_MANAGER_ROLE = keccak256("ORACLE_MANAGER_ROLE"); // Feed/source updates, bounds, staleness, oracle switching
EMERGENCY_ROLE = keccak256("EMERGENCY_ROLE"); // Circuit breakers, pause
UPGRADER_ROLE = keccak256("UPGRADER_ROLE"); // Plain-UUPS upgrades (Safe direct, no timelock)
// SlippageStorage
MANAGER_ROLE = keccak256("MANAGER_ROLE"); // Store configuration (sources, thresholds, intervals)
WRITER_ROLE = keccak256("WRITER_ROLE"); // Publishing the venue mid on-chain
2. Reentrancy Protection
Implementation:
nonReentrantmodifier on all state-changing functions- Checks-effects-interactions pattern
- External call isolation
- State variable protection
Example:
// OpenZeppelin ReentrancyGuardUpgradeable: a re-entrant call reverts with ReentrancyGuardReentrantCall()
function redeemQEURO(uint256 qeuroAmount, uint256 minUsdcOut) external nonReentrant whenNotPaused {
// ... function logic
}
3. Oracle Security
Price Feed Validation:
- Multiple price feed sources
- Staleness checks (Hyperliquid: 15 min; Chainlink EUR/USD: 2 h; USDC/USD: 25 h)
- Price bound validation
- Circuit breaker mechanisms
The active deployment enforces a strict independent Chainlink EUR/USD reference check (enabled on 23 September 2026). Invalid, unavailable, stale or excessively divergent reference data blocks minting and normal redemption, including on weekends. The release must verify both successful operation with valid prices and rejection with an invalid reference. Configuration targets and live-state checks are documented in Oracle Architecture.
Implementation:
// QuantillonVault: an invalid oracle read is a hard stop for mint / redeem
(uint256 price, bool isValid) = oracle.getEurUsdPrice();
if (!isValid) revert CommonErrorLibrary.InvalidOraclePrice();
// Oracles: staleness, bounds and deviation checks return isValid = false instead of
// reverting; governance setters validate inputs with CommonErrorLibrary.InvalidPrice()
4. Emergency Mechanisms
Pause System:
- Global pause functionality
- Role-based pause controls
- Contract-specific emergency controls; the vault has no generic emergency withdrawal from an external strategy
- Circuit breaker activation
Implementation:
// OpenZeppelin PausableUpgradeable: `whenNotPaused` reverts with EnforcedPause()
function pause() external onlyRole(EMERGENCY_ROLE) { // PAUSER_ROLE on QEUROToken
_pause(); // emits Paused(msg.sender)
}
Responsible Disclosure
Reporting a vulnerability
Report suspected vulnerabilities privately to team@quantillon.money before any public disclosure. Include the affected contract(s) and addresses, a reproduction (preferably a Foundry test against a local fork) and your assessment of impact. The team acknowledges reports, works with the reporter on validation and remediation, and coordinates a reasonable disclosure date.
In scope: Quantillon-written contracts under src/ as deployed on Base mainnet (see the API Reference), including the oracle stack and external-vault integration logic. A defect in third-party code is outside this programme, but an exploitable impact caused by Quantillon's configuration or integration remains in scope.
Do not execute exploit or disruptive transactions on Base mainnet without prior written authorization. Do not access or move data or funds that are not yours, and do not perform denial of service, abusive automated traffic, oracle or market manipulation, governance interference, phishing, or social engineering.
The canonical, current terms and programme status are maintained at security.quantillon.money/disclosure. The disclosure programme is active and unpaid.
Audits
No professional security audit has been performed. Review to date is internal and AI-assisted; findings and corrections are ongoing work, not a completed audit milestone. Internal review does not establish that deployed contracts are free of defects. No external audit report is promised.
Risk Management
Risk Categories
1. Smart Contract Risks
Oracle Manipulation:
- Risk: Price feed manipulation
- Mitigation: Multiple price sources, circuit breakers
- Monitoring: Price deviation alerts
Reentrancy Attacks:
- Risk: State manipulation during external calls
- Mitigation: Reentrancy guards, CEI pattern
- Monitoring: Function call analysis
Access Control Bypass:
- Risk: Unauthorized function execution
- Mitigation: Role-based access control, multi-sig
- Monitoring: Permission change alerts
2. Economic Risks
Liquidity Risk:
- Risk: Insufficient liquidity for operations
- Mitigation: Liquidity requirements, emergency procedures
- Monitoring: Liquidity ratio tracking
Interest Rate Risk:
- Risk: Adverse interest rate movements
- Mitigation: Hedging mechanisms, rate limits
- Monitoring: Rate change alerts
Market Risk:
- Risk: Extreme market volatility
- Mitigation: Circuit breakers, position limits
- Monitoring: Volatility tracking
3. Operational Risks
Key Management:
- Risk: Private key compromise
- Mitigation: Hardware security modules, multi-sig
- Monitoring: Access pattern analysis
Upgrade Risk:
- Risk: Malicious or faulty upgrades
- Mitigation: Timelock controls, governance
- Monitoring: Upgrade proposal tracking
- Version traceability: every core contract exposes
IVersioned.version()and any change is traced through a semver bump (CI-enforced viamake check-version-bump);deployments/{chainId}/versions.jsonrecords the live implementation + commit per contract, so an auditor can confirm exactly which source version is deployed (cast call <proxy> "version()(string)"). - Controller authority: after bootstrap,
setTimelockrequires the current controller. The 23 September 2026 activation transferred coreDEFAULT_ADMIN_ROLEto that controller and removed the Safe's direct default-admin role. Operational governance, pause and emergency roles remain with their existing holders. The handover is complete; readhasRoleto confirm current authority.
Integration Risk:
- Risk: Third-party protocol failures
- Mitigation: Risk limits, emergency procedures
- Monitoring: External protocol health
Security Best Practices
For Developers
1. Code Security
Input Validation:
function deposit(uint256 amount) external {
if (amount == 0) revert CommonErrorLibrary.InvalidAmount();
if (amount > MAX_DEPOSIT) revert CommonErrorLibrary.AboveLimit();
// ... function logic
}
Access Control:
// Role checks use OpenZeppelin AccessControl (`onlyRole`) or the shared library helpers,
// which revert with custom errors (e.g. CommonErrorLibrary.NotGovernance / NotAuthorized)
function setParameter(uint256 value) external onlyRole(GOVERNANCE_ROLE) {
// ... function logic
}
Reentrancy Protection:
function withdraw(uint256 amount) external nonReentrant {
// ... function logic
}
2. Testing
Unit Testing:
- Test all function paths
- Test edge cases and boundary conditions
- Test access control mechanisms
- Test error conditions
Integration Testing:
- Test contract interactions
- Test external integrations
- Test upgrade scenarios
- Test emergency procedures
Fuzz Testing:
- Random input generation
- Property-based testing
- Stress testing
- Gas limit testing
3. Code Review
Review Checklist:
- Access control implementation
- Input validation
- Reentrancy protection
- Error handling
- Gas optimization
- Event emission
- Documentation
For Integrators
1. Integration Security
Contract Verification:
// Verify contract addresses
const VAULT_ADDRESS = "0x..."; // Verified on Etherscan
const QEURO_ADDRESS = "0x..."; // Verified on Etherscan
// Verify contract state
const isPaused = await vault.paused();
if (isPaused) {
throw new Error("Contract is paused");
}
Transaction Security:
// Use slippage protection
const slippage = 0.05; // 5%
const minOutput = expectedOutput * (1 - slippage);
// Validate transaction parameters
const gasEstimate = await contract.estimateGas.function(params);
const gasLimit = gasEstimate.mul(120).div(100); // 20% buffer
Error Handling:
try {
await contract.function();
} catch (error) {
const msg = error.reason || error.message;
if (msg.includes('InsufficientBalance') || msg.includes('ERC20InsufficientBalance')) {
// Handle insufficient balance
} else if (msg.includes('InvalidOraclePrice')) {
// Handle stale / invalid oracle price
} else if (msg.includes('EnforcedPause')) {
// Handle paused contract
} else {
// Handle other errors
}
}
2. Monitoring
Event Monitoring:
// Monitor important events (QEUROminted: lowercase m, 3 arguments)
vault.on('QEUROminted', (user, usdcAmount, qeuroAmount) => {
console.log(`User ${user} minted ${qeuroAmount} QEURO`);
});
// Pause is the OpenZeppelin Pausable event
vault.on('Paused', (account) => {
console.log(`Contract paused by ${account}`);
});
Health Checks:
async function healthCheck() {
const [isPaused, [price, isValid], collateralizationRatio, mintFloor, criticalRatio] = await Promise.all([
vault.paused(),
oracleRouter.callStatic.getEurUsdPrice(), // non-view: simulate instead of sending a tx
vault.getProtocolCollateralizationRatio(), // 18-decimal percent, 1e20 == 100% (0 while nothing is minted)
vault.minCollateralizationRatioForMinting(), // 1.025e20 live (102.5%)
vault.criticalCollateralizationRatio() // 1.01e20 (101%)
]);
if (isPaused) {
console.warn("Vault is paused");
}
if (!isValid) {
console.warn("Oracle price is stale or invalid");
}
if (collateralizationRatio.lt(mintFloor)) {
console.warn("Protocol CR below the minting floor: mints are refused");
}
if (collateralizationRatio.lte(criticalRatio)) {
console.error("Protocol CR at or below the critical ratio: liquidation-mode redemptions");
}
}
Incident Response
Response Procedures
1. Detection
Automated Monitoring:
- Price deviation alerts
- Liquidity threshold alerts
- Access control violation alerts
- Unusual transaction pattern alerts
Manual Monitoring:
- Community reports
- Security researcher reports
- Internal security reviews
- External audit findings
2. Assessment
Severity Classification:
- Critical: Immediate threat to user funds
- High: Significant risk to protocol integrity
- Medium: Moderate risk with workarounds
- Low: Minor issues with minimal impact
Impact Analysis:
- Affected contracts and functions
- Potential financial impact
- User impact assessment
- Recovery time estimation
3. Response
Immediate Actions:
- Activate emergency pause if necessary
- Notify security team and stakeholders
- Assess and contain the issue
- Implement temporary mitigations
Recovery Actions:
- Develop and test fixes
- Deploy fixes through governance
- Monitor system recovery
- Conduct post-incident review
4. Communication
Stakeholder Notification:
- Internal team notification
- Community announcement
- Partner notification
- Regulatory notification (if required)
Transparency:
- Incident timeline
- Root cause analysis
- Remediation steps
- Prevention measures
Security Monitoring
Real-Time Monitoring
Key Metrics:
- Contract pause status
- Oracle price staleness
- Liquidity ratios
- Access control changes
- Unusual transaction patterns
Alerting:
- Price deviation > 5%
- Protocol collateralization ratio below the governance-set minting floor (
minCollateralizationRatioForMinting: 105% at launch, 102.5% under the September 2026 margin policy; liquidation mode at <= 101%) - Emergency role activation
- Large transaction volumes
- Failed transaction spikes
Security Dashboards
Operational Dashboard:
- System health indicators
- Performance metrics
- Error rates
- Gas usage patterns
Security Dashboard:
- Access control events
- Emergency activations
- Oracle health status
- Risk metrics
Security Contacts
Primary Contacts
Security contact: team@quantillon.money — vulnerability reports and responsible disclosure (see Responsible Disclosure)
Security Resources
Documentation
- Smart Contract Security Best Practices
- OpenZeppelin Security Guidelines
- Ethereum Security Considerations
Tools
Security Analysis Results
Use generated artifacts from each run instead of hardcoded snapshots:
scripts/results/slither/slither-report.txtscripts/results/slither/slither-report.jsonscripts/results/mythril-reports/scripts/results/natspec-validation-report.txtscripts/results/contract-sizes/contract-sizes-summary.txt
Running Mythril Analysis:
# Run Mythril analysis
make mythril
# Or run directly
./scripts/run-mythril.sh
# Run Slither static analysis
make slither
# Validate NatSpec coverage
make validate-natspec
# Run EIP-170 contract size checks
make analyze-contract-sizes
# Run comprehensive security analysis (Slither + Mythril)
make security
This security guide is maintained by Quantillon Labs and updated regularly.
Operational invariants for minting and yield
Public minting continues to use the configured mint floor. Yield conversion is independent and is permitted only when current collateralization is strictly above max(101%, criticalCollateralizationRatio) and projected collateralization is at least 100%. The final QEURO token mint remains authoritative for pause, killswitch, cap, whitelist, blacklist, and rate-limit checks; failed token or hedger checks revert atomically, preserving the harvest for retry.
Accepted operational-scope risks
This remediation release preserves the current publisher and Safe Roles assignments, including the existing shared publisher/reporting identity, operational FeeCollector recipients, and Zodiac Roles module permissions. It does not rotate the publisher key, split those identities, replace recipients, or remove the existing Zodiac permissions. These remain separately governed operational risks; the contract safeguards and monitoring in this release do not change those assignments.