| Sözleşme | Risk Seviyesi | Kritik Fonksiyonlar | Önerilen Audit |
|---|---|---|---|
| QuadraticVotingGovernor | 🔴 Yüksek | castQuadraticVote, _execute | OpenZeppelin Audit + Manuel |
| ContentIPManager | 🔴 Yüksek | createDerivative, payCommercialRevenue | Story Protocol Review |
| ContentRevenueStream | 🟡 Orta-Yüksek | startSubscription, distributeAdRevenue | Superfluid Best Practices |
| ContentCurationMarket | 🟡 Orta | buyTokens, sellTokens | Bonding Curve Specialist |
| TransferableRoyaltyNFT | 🟡 Orta | tokenizeRoyalties, payRoyalty | ERC-2981 Compliance |
Risk Noktaları:
// ❌ ZAFIYET ÖRNEĞİ
function withdraw() external {
uint256 amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
balances[msg.sender] = 0; // ÇOK GEÇ!
}Savunma:
// ✅ GÜVENLİ UYGULAMA
function withdraw() external nonReentrant {
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0; // ÖNCELİK: State değişikliği
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}Uygulanan Yerler:
ContentCurationMarket.sellTokens()ContentRevenueStream.sendTip()RoyaltyPaymentSplitter.claim()Test Senaryosu:
// Hardhat test
it("should prevent reentrancy attack", async function() {
const AttackerContract = await ethers.getContractFactory("ReentrancyAttacker");
const attacker = await AttackerContract.deploy(marketContract.address);
await expect(
attacker.attack()
).to.be.revertedWith("ReentrancyGuard: reentrant call");
});Senaryo:
Savunmalar:
// 1. Snapshot-based voting
function propose(...) override returns (uint256) {
uint256 proposalId = super.propose(...);
_proposalSnapshots[proposalId] = block.number; // Oylamadan ÖNCE snapshot
return proposalId;
}
// 2. Timelock minimum 48 saat
constructor(...) {
_setMinDelay(172800); // 2 gün
}
// 3. Delegasyon gerektir (1 blok önceden)
function castQuadraticVote(...) public {
require(
getPastVotes(msg.sender, block.number - 1) > 0,
"Must delegate before voting"
);
}Test Senaryosu:
it("should block flash loan governance attack", async function() {
// 1. Flash loan simülasyonu
await token.transfer(attacker.address, ethers.utils.parseEther("1000000"));
// 2. Aynı blokta proposal ve vote
await expect(
attacker.proposeAndVoteInSameBlock()
).to.be.revertedWith("Voting power not delegated");
// 3. Next block'ta vote dene
await ethers.provider.send("evm_mine");
const voteAttempt = await attacker.voteOnProposal();
// 4. Voting power 0 olmalı (snapshot öncesi)
expect(voteAttempt).to.equal(0);
});Saldırı 1: Front-Running
Senaryo:
1. Kullanıcı A, 100 token almak için işlem gönderir
2. MEV botu işlemi görür
3. Bot, daha yüksek gaz ile 1000 token alır (fiyat yükselir)
4. Kullanıcı A'nın işlemi çok yüksek fiyata gerçekleşir
5. Bot tokenları hemen geri satarSavunma:
function buyTokens(uint256 amount, uint256 maxPrice) external payable {
uint256 actualPrice = getBuyPrice(contentId, amount);
require(actualPrice <= maxPrice, "Price slippage too high");
// ...
}Saldırı 2: Wash Trading
Senaryo:
1. Saldırgan kendi içeriği için market açar
2. Kendi kendine al-sat yaparak hacim yaratır
3. Diğer kullanıcılar "popüler" zannederSavunma:
mapping(uint256 => mapping(address => uint256)) public lastPurchaseBlock;
function buyTokens(...) external payable {
require(
block.number > lastPurchaseBlock[contentId][msg.sender] + 5,
"Minimum 5 block wait between trades"
);
lastPurchaseBlock[contentId][msg.sender] = block.number;
}Saldırı:
Gerçek Senaryo:
- 100 ses kredisi ile 1 hesap = √100 = 10 etkili oy
- 100 ses kredisi / 10 hesap = 10 × √10 = 31.6 etkili oySavunma Katmanları:
// Katman 1: Proof of Personhood
interface IProofOfPersonhood {
function isHuman(address account) external view returns (bool);
function humanScore(address account) external view returns (uint256); // 0-100
}
function castQuadraticVote(...) public {
require(pohRegistry.isHuman(msg.sender), "Not verified human");
require(pohRegistry.humanScore(msg.sender) >= 50, "Low humanity score");
}
// Katman 2: Gitcoin Passport entegrasyonu
interface IGitcoinPassport {
function getScore(address user) external view returns (uint256);
}
function getVotingPower(address user, uint256 voiceCredits) public view returns (uint256) {
uint256 baseVotes = sqrt(voiceCredits);
uint256 passportScore = gitcoinPassport.getScore(user); // 0-100
// Score < 20 ise oy gücü %50 azalır
if (passportScore < 20) {
return baseVotes / 2;
}
return baseVotes;
}
// Katman 3: On-chain aktivite analizi
function getActivityBonus(address user) internal view returns (uint256) {
uint256 txCount = user.code.length > 0 ? 0 : block.number - creationBlock[user];
uint256 holderDuration = block.timestamp - firstTokenReceived[user];
// Yeni cüzdan cezası
if (holderDuration < 30 days) return 50; // %50 azalma
if (txCount < 10) return 75; // %25 azalma
return 100; // Tam güç
}Test Senaryosu:
describe("Sybil Resistance", function() {
it("should reduce voting power for unverified users", async function() {
const voiceCredits = 100;
const expectedNormalVotes = Math.floor(Math.sqrt(voiceCredits)); // 10
// Verified user
await pohRegistry.setHuman(alice.address, true);
const aliceVotes = await governor.getVotingPower(alice.address, voiceCredits);
expect(aliceVotes).to.equal(expectedNormalVotes);
// Unverified user
const bobVotes = await governor.getVotingPower(bob.address, voiceCredits);
expect(bobVotes).to.equal(0); // Hiç oy yok
});
it("should penalize new wallets", async function() {
await pohRegistry.setHuman(newUser.address, true);
// Immediately after wallet creation
const votes1 = await governor.getActivityBonus(newUser.address);
expect(votes1).to.equal(50); // %50 penalty
// After 30 days
await ethers.provider.send("evm_increaseTime", [30 * 24 * 3600]);
await ethers.provider.send("evm_mine");
const votes2 = await governor.getActivityBonus(newUser.address);
expect(votes2).to.equal(100); // Tam güç
});
});Senaryo:
Rakip Platform:
1. "100x daha iyi telif oranları!" diye kampanya başlatır
2. Creator'ları teşviklerle transfer etmeye ikna eder
3. Platformun TVL'si düşer, token fiyatı çökerSavunma:
// Token vesting ile creator loyalty
struct VestingSchedule {
uint256 totalAmount;
uint256 startTime;
uint256 cliff; // 6 ay
uint256 duration; // 2 yıl
uint256 released;
}
mapping(address => VestingSchedule) public creatorVesting;
function claimVestedTokens() external {
VestingSchedule storage schedule = creatorVesting[msg.sender];
uint256 vested = calculateVestedAmount(schedule);
uint256 claimable = vested - schedule.released;
require(claimable > 0, "Nothing to claim");
schedule.released += claimable;
governanceToken.transfer(msg.sender, claimable);
}
// Erken ayrılma cezası
function migrateContent(uint256 contentId) external {
require(ownerOf(contentId) == msg.sender);
VestingSchedule memory schedule = creatorVesting[msg.sender];
if (block.timestamp < schedule.startTime + schedule.cliff) {
// Cliff öncesi migration = vesting iptal
delete creatorVesting[msg.sender];
emit VestingForfeited(msg.sender, schedule.totalAmount - schedule.released);
}
}Risk: Superfluid dağıtım havuzundaki paylar, off-chain oracle'dan gelen görüntülenme verilerine dayanır.
Savunma:
// Çoklu oracle + medyan
struct OracleData {
uint256 value;
uint256 timestamp;
address source;
}
mapping(address => OracleData[3]) public creatorMetrics; // 3 oracle
function updateCreatorMetrics(
address creator,
uint256 views,
uint8 oracleIndex
) external onlyOracle {
require(oracleIndex < 3, "Invalid oracle");
creatorMetrics[creator][oracleIndex] = OracleData({
value: views,
timestamp: block.timestamp,
source: msg.sender
});
}
function getMedianViews(address creator) public view returns (uint256) {
uint256[3] memory values;
for(uint i = 0; i < 3; i++) {
values[i] = creatorMetrics[creator][i].value;
}
// Bubble sort (3 eleman için yeterli)
if (values[0] > values[1]) (values[0], values[1]) = (values[1], values[0]);
if (values[1] > values[2]) (values[1], values[2]) = (values[2], values[1]);
if (values[0] > values[1]) (values[0], values[1]) = (values[1], values[0]);
return values[1]; // Medyan
}
// Outlier rejection
function isOutlier(uint256 value, uint256 median) internal pure returns (bool) {
uint256 deviation = value > median ? value - median : median - value;
return deviation > median / 2; // %50'den fazla sapma
}| Firma | Özellik | Maliyet | Süre |
|---|---|---|---|
| OpenZeppelin | Industry standard | $50K-$100K | 3-4 hafta |
| Trail of Bits | Derinlemesine analiz | $80K-$150K | 4-6 hafta |
| Consensys Diligence | Ethereum uzmanı | $40K-$80K | 2-3 hafta |
| Cyfrin (Patrick Collins) | Eğitim odaklı | $30K-$60K | 2-3 hafta |
| Sherlock | Crowdsourced | $20K-$40K | 1-2 hafta |
Tavsiye: OpenZeppelin (primary) + Sherlock (secondary) kombinasyonu.
| Seviye | Etki | Örnek | Ödül |
|---|---|---|---|
| Critical | Fon kaybı, governance takeover | Flash loan attack başarılı | $50,000 |
| High | Kısmi fon kaybı, DoS | Reentrancy exploit | $25,000 |
| Medium | Gas optimizasyonu, UX sorunu | Front-running açığı | $5,000 |
| Low | Kozmetik, best practice | Eksik event | $500 |
In Scope:
Out of Scope:
Immunefi - Kripto odaklı bug bounty platformu
Setup:
- Create program page
- Escrow ödül fonları
- Define scope clearly
- Set severity matrix
Process:
1. Researcher submits report
2. Team triages (48 saat içinde)
3. Severity belirlenir
4. Fix implement edilir
5. Ödül ödenirgraph TD
A[Exploit Detected] --> B{Severity?}
B -->|Critical| C[PAUSE ALL CONTRACTS]
B -->|High| D[Isolate Affected Module]
B -->|Medium| E[Monitor Closely]
C --> F[Notify Team via PagerDuty]
F --> G[Assemble War Room]
G --> H[Root Cause Analysis]
H --> I{Fix Available?}
I -->|Yes| J[Deploy Patch via Timelock]
I -->|No| K[Implement Workaround]
J --> L[Post-Mortem Report]
K --> L
E --> L// Multi-sig guardian adresleri
address[] public guardians = [
0x1234..., // Lead Developer
0x5678..., // Security Auditor
0x9abc..., // Protocol Architect
0xdef0..., // Community Multisig
];
// Emergency pause (2/4 multi-sig)
function emergencyPause() external onlyGuardian {
_pause();
emit EmergencyPause(msg.sender, block.timestamp);
}
// Unpause requires 3/4 + 48 hour delay
function unpause() external onlyGuardian {
require(
block.timestamp > pausedAt + 48 hours,
"Minimum pause duration not met"
);
_unpause();
}# Security Incident Report
**Date:** 2024-11-26 14:30 UTC
**Severity:** CRITICAL
**Status:** CONTAINED
## Summary
[Brief description of exploit]
## Impact
- Funds at risk: $XXX
- Affected users: XXX
- Contracts paused: [List]
## Actions Taken
1. [Timestamp] Contracts paused
2. [Timestamp] Team notified
3. [Timestamp] Root cause identified
4. [Timestamp] Patch deployed
## User Action Required
- [X] Revoke approvals to contract XXX
- [X] Withdraw funds from pool YYY
## Timeline to Resolution
- ETA for full resolution: 24-48 hours
- Updates every 2 hours
## Contact
- Discord: #emergency-response
- Email: security@contentdao.appTools:
Example Forta Bot:
// Detect large withdrawals
const handleTransaction = async (txEvent) => {
const findings = [];
const withdrawals = txEvent.filterLog(WITHDRAW_EVENT_ABI);
for (const withdrawal of withdrawals) {
if (withdrawal.args.amount > ethers.utils.parseEther("100")) {
findings.push(Finding.fromObject({
name: "Large Withdrawal Detected",
description: `${withdrawal.args.user} withdrew ${withdrawal.args.amount}`,
alertId: "LARGE-WITHDRAWAL",
severity: FindingSeverity.High,
type: FindingType.Suspicious,
}));
}
}
return findings;
};Q1 Focus: Governance mechanisms Q2 Focus: Economic exploits Q3 Focus: Integration security Q4 Focus: Comprehensive re-audit
Checklist per Review:
Güvenlik bir süreçtir, son nokta değildir.
En Kritik 3 Öncelik:
Risk Azaltma Timeline:
Budget Allocation:
Security Total: $100,000
├── Audit: $60,000 (60%)
├── Bug Bounty Pool: $25,000 (25%)
├── Monitoring Tools: $10,000 (10%)
└── Insurance (Nexus Mutual): $5,000 (5%)