I built a TWAP (time-weighted average price) oracle and a collateral-valuation engine from scratch. This reproduces the Tectonic incident — pumping a token's price 100x in 20 minutes to over-borrow against inflated collateral — and the code that stops it. Both free right now.
On 2026-08-30, an attacker on Tectonic (Cronos's largest lending protocol) pumped a thin-liquidity governance token's price ~100x in about 20 minutes, then deposited the inflated token as collateral to borrow far beyond its real value — exploiting the assumption that "price equals collateral value."
A TWAP-based oracle — ignores momentary spot-price manipulation, anchoring to a trailing average instead.
// TWAP (time-weighted average price) oracle — defends spot-price manipulation
class PriceOracleGuard {
constructor({ windowMs = 1200000, maxDeviation = 0.1 } = {}) {
this.history = []; // {price, time}
this.windowMs = windowMs; // default 20 minutes
this.maxDeviation = maxDeviation; // allowed deviation vs TWAP, 10%
}
pushPrice(spotPrice) {
const now = Date.now();
this.history.push({ price: spotPrice, time: now });
this.history = this.history.filter(p => now - p.time < this.windowMs);
}
twap() {
if (!this.history.length) return 0;
const sum = this.history.reduce((s, p) => s + p.price, 0);
return sum / this.history.length;
}
getSafePrice(spotPrice) {
this.pushPrice(spotPrice);
const twap = this.twap();
const deviation = Math.abs(spotPrice - twap) / (twap || spotPrice);
if (deviation > this.maxDeviation) {
return { price: twap, flagged: true, reason: 'spot_deviation_exceeded', spotPrice, twap };
}
return { price: spotPrice, flagged: false };
}
}
Collateral valuation + per-asset loan-to-value limits — takes the oracle above as its price source.
// Collateral valuation + borrow-limit logic (Tectonic incident response)
class CollateralEngine {
constructor(getPrice, { ltv = 0.7 } = {}) {
this.getPrice = getPrice; // injected oracle price lookup
this.ltv = ltv; // loan-to-value ratio, 70%
this.positions = {};
}
deposit(user, token, amount) {
this.positions[user] = this.positions[user] || { collateral: {}, borrowed: 0 };
this.positions[user].collateral[token] = (this.positions[user].collateral[token] || 0) + amount;
}
collateralValue(user) {
const pos = this.positions[user];
if (!pos) return 0;
return Object.entries(pos.collateral)
.reduce((sum, [token, amt]) => sum + amt * this.getPrice(token), 0);
}
borrow(user, amount) {
const maxBorrow = this.collateralValue(user) * this.ltv;
const pos = this.positions[user];
if (pos.borrowed + amount > maxBorrow) {
return { status: 'rejected', reason: 'insufficient_collateral', maxBorrow };
}
pos.borrowed += amount;
return { status: 'approved', borrowed: pos.borrowed };
}
}
Questions or feedback about this product? Leave your email if you'd like a reply (optional).