BonqDAO ALBT Oracle Manipulation via TellorFlex
Exploit Transactions
0x31957ecc43774d19f54d9968e95c69c882468b46860f921668f2c55fadd51b19Victim Addresses
0x3bB7fFD08f46620beA3a9Ae7F096cF2b213768B3Polygon0x20D50159aff262f953C8913Ec859Cac13A010b8aPolygonLoss Breakdown
Similar Incidents
0VIX ovGHST Oracle Inflation
33%Midas LP Oracle Read-Only Reentrancy via Curve stMATIC/WPOL
31%LunaFi VLFI Reward Replay
27%NovaX TokenStake Oracle Manipulation Exploit
26%Polygon Uninitialized Clone Wallet Takeover and TEL Drain
25%Aave AMM LP Oracle Manipulation Through Delegated Recursive LP Looping
25%Root Cause Analysis
BonqDAO ALBT Oracle Manipulation via TellorFlex
1. Incident Overview TL;DR
BonqDAO accepted a permissionless TellorFlex SpotPrice(albt, usd) report inside the live ALBT collateral price path. In Polygon transaction 0x31957ecc43774d19f54d9968e95c69c882468b46860f921668f2c55fadd51b19, the adversary cluster staked 10 TRB, submitted an ALBT/USD value of 5e27, opened a fresh ALBT trove with only 0.1 ALBT, and minted 100,000,000 BEUR. In Polygon transaction 0xa02d0c3d16d6ee0e0b6a42c3cc91997c2b40c87d777136dedebe8ee0f47f32b1, the same cluster submitted 1e11 for the same Tellor query and then liquidated Bonq troves that became unhealthy under the manipulated price.
The root cause is a direct oracle-trust failure. Bonq's solvency logic treated raw Tellor reporter output as authoritative ALBT pricing without bounds checks, freshness validation, dispute delay, or trusted-source aggregation. That made both borrow() and liquidate() depend on an attacker-controlled oracle value.
2. Key Background
Bonq's ALBT collateral flow matters because the protocol uses the current token price to decide whether a trove may borrow more BEUR or must be liquidated. On-chain validation in the current validator rerun at block 38792977 confirms:
BonqProxy / OriginalTroveFactoryat0x3bb7ffd08f46620bea3a9ae7f096cf2b213768b3points toTokenToPriceFeedat0x20D50159aff262f953C8913Ec859Cac13A010b8a.TokenToPriceFeed.tokenPriceFeed(ALBT)returns0x7D4c36c79b89E1f3eA63A38C1DdB16EF8c394bc8.TokenToPriceFeed.mcr(ALBT)returns2000000000000000000, so ALBT troves must remain at or above 200% collateralization.TokenToPriceFeed.tokenPrice(ALBT)returns97690490106405379before the exploit transaction.
The configured ALBT feed is not a local Bonq calculation. It is a converted feed that multiplies one price source by another conversion leg:
contract ConvertedPriceFeed is IPriceFeed, Constants {
IPriceFeed public immutable priceFeed;
IPriceFeed public immutable conversionPriceFeed;
function price() public view override returns (uint256) {
return (priceFeed.price() * DECIMAL_PRECISION) / conversionPriceFeed.price();
}
}
The ALBT-specific priceFeed leg resolves to Tellor-backed pricing. TellorFlex itself is permissionless: any address that stakes the required TRB can submit a report for any query ID as long as it respects the reporting lock. The relevant TellorFlex logic is:
function depositStake(uint256 _amount) external {
...
require(token.transferFrom(msg.sender, address(this), _amount));
_updateStakeAndPayRewards(msg.sender, _stakedBalance + _amount);
}
function submitValue(
bytes32 _queryId,
bytes calldata _value,
uint256 _nonce,
bytes calldata _queryData
) external {
...
require(_staker.stakedBalance >= stakeAmount, "balance must be greater than stake amount");
require(_queryId == keccak256(_queryData), "query id must be hash of query data");
...
_report.valueByTimestamp[block.timestamp] = _value;
}
Validator-side RPC checks confirm that TellorFlex.getStakeAmount() returned 10000000000000000000, so the exploit prerequisite was the public 10 TRB stake and nothing more.
3. Vulnerability Analysis & Root Cause Summary
This incident is an ATTACK-class oracle-manipulation failure in BonqDAO's collateral solvency path. Bonq's TokenToPriceFeed contract simply forwards token pricing to an external feed, and the ALBT feed chain eventually reaches TellorPriceFeed. TellorPriceFeed.price() converts the current Tellor report directly into a uint256 and returns it without validating reporter identity, value range, report age, or dispute status. Trove accounting then multiplies ALBT collateral by that oracle output and compares the resulting collateralization ratio against mcr(). Because the oracle value is attacker-controlled after a public Tellor stake and report submission, Bonq accepts an otherwise undercollateralized borrow and later also accepts liquidations created by the opposite price move. The protocol therefore violated a core solvency invariant: borrowing and liquidation should depend on manipulation-resistant collateral prices rather than on a single permissionless reporter's current output.
4. Detailed Root Cause Analysis
4.1 Code-level breakpoint
Bonq's first critical step is the price lookup itself:
function tokenPrice(address _token) public view override returns (uint256) {
return IPriceFeed(tokens[_token].priceFeed).price();
}
The Tellor-backed feed then exposes the raw value:
function price() public view virtual override returns (uint256) {
return uint256(bytes32(oracle.getCurrentValue(queryId)));
}
That raw price is consumed by Bonq's trove solvency logic:
function collateralValue() public view override returns (uint256) {
return (normalisedDecimals(collateral()) * factory.tokenToPriceFeed().tokenPrice(address(token))) / DECIMAL_PRECISION;
}
function _collateralization() private view returns (uint256) {
if (_debt > 0) {
return (normalisedDecimals(recordedCollateral) * factory.tokenToPriceFeed().tokenPrice(address(token))) / _debt;
} else {
return MAX_INT;
}
}
function insertTrove(address _newNextTrove) private {
require(_collateralization() >= mcr(), "41670 TCR must be > MCR");
}
function liquidate() public {
_updateCollateral();
require(_collateralization() < mcr(), "454f4 CR must lt MCR");
}
This is the concrete breakpoint. Once the Tellor value moves, Bonq's borrow and liquidation predicates move with it.
4.2 Upward manipulation and unbacked BEUR mint
The collector trace for tx 0x31957ecc43774d19f54d9968e95c69c882468b46860f921668f2c55fadd51b19 shows the exploit sequence directly:
TellorFlex::depositStake(10000000000000000000)
TellorFlex::submitValue(
0x12906c5e9178631dba86f1f750f7ab7451c61e6357160eb890029b9eac1fb235,
0x00000000000000000000000000000000000000001027e72f1f12813088000000,
0,
SpotPrice("albt","usd")
)
OriginalTroveFactory::createTrove(WrappedToken: [0x35b2ECE5B1eD6a7a99b83508F8ceEAB8661E0632])
Trove::increaseCollateral(0, 0x0000000000000000000000000000000000000000)
Trove::borrow(0xED596991ac5F1Aa1858Da66c67f7CFA76e54B5f1, 100000000000000000000000000, 0x0000000000000000000000000000000000000000)
The query ID in that trace is the Tellor hash for SpotPrice("albt","usd"). The submitted value bytes decode to 5000000000000000000000000000 (5e27). Validator-side RPC checks show the effect immediately:
- Before block
38792978,TokenToPriceFeed.tokenPrice(ALBT) = 97690490106405379. - After tx
0x31957ecc...,TokenToPriceFeed.tokenPrice(ALBT) = 4579375226679073720614149170.
Bonq therefore saw ALBT as orders of magnitude more valuable than it was in the pre-state. The root cause's ACT success predicate is satisfied because the same transaction minted BEUR from a trove that would be below the required mcr() under the baseline price.
The balance diff makes the economic realization deterministic:
{
"token": "0x338eb4d394a4327e5db80d08628fa56ea2fd4b81",
"holder": "0xed596991ac5f1aa1858da66c67f7cfa76e54b5f1",
"before": "0",
"after": "100000000000000000000000000",
"delta": "100000000000000000000000000"
}
That same balance diff also shows the Tellor stake movement into TellorFlex and the 0.1 ALBT movement into the newly created trove.
4.3 Downward manipulation and liquidation wave
The second seed transaction uses the same primitive in the opposite direction. The collector trace for tx 0xa02d0c3d16d6ee0e0b6a42c3cc91997c2b40c87d777136dedebe8ee0f47f32b1 shows:
TellorFlex::depositStake(10000000000000000000)
TellorFlex::submitValue(
0x12906c5e9178631dba86f1f750f7ab7451c61e6357160eb890029b9eac1fb235,
0x000000000000000000000000000000000000000000000000000000174876e800,
0,
SpotPrice("albt","usd")
)
0x454C40FC7C61B2153728871D11bEA6C56F99FE77::liquidate()
0x6e237d5fB96D9C7aEdfD37e679017849dc845502::liquidate()
0x605778C9B0938fd60634FcE0f73b908500ACb8AA::liquidate()
...
Validator-side RPC checks show the ALBT price collapsing to 91594383432 after this transaction. At that point Bonq's require(_collateralization() < mcr()) liquidation branch becomes true for many ALBT troves.
The second balance diff shows the liquidation harvest landing in the attacker path, starting with:
{
"token": "0x35b2ece5b1ed6a7a99b83508f8ceeab8661e0632",
"holder": "0xeec2266a0ea9d1970fe692273839dbbb6a9ae598",
"before": "0",
"after": "858342997708983721666464",
"delta": "858342997708983721666464"
}
and continuing across many liquidated troves. The root cause artifact summarizes the total ALBT realization from this phase as 113813998.369826208354681311 ALBT.
4.4 Deterministic ACT framing
This is an ACT opportunity because every dependency is public:
- the Tellor stake amount is public and permissionless;
- the Tellor query is public and can be derived from the visible query data;
- Bonq's trove creation, collateral increase, borrow, and liquidation entrypoints are public;
- the relevant pre-state is observable from public RPC and verified source.
No private key, governance privilege, or non-public order flow is required.
5. Adversary Flow Analysis
The adversary cluster identified in the root cause is:
0xcacf2d28b2a5309e099f0c6e8c60ec3ddf656642: EOA that sent both seed exploit transactions.0xed596991ac5f1aa1858da66c67f7cfa76e54b5f1: attacker entry contract that receives the BEUR and ALBT gains.0xbaf48429b4d30bdfad488508d3b528033331fe8a: first Tellor reporter helper used in tx0x31957ecc....0xb5c0ba8ed0f4fb9a31fccf84b9fb3da639a1ede5: second Tellor reporter helper used in tx0xa02d0c3d....
Their execution flow is straightforward:
- Acquire or route 10 TRB into a fresh helper contract.
- Stake into TellorFlex and submit
SpotPrice("albt","usd")with a manipulated value. - While the manipulated value is live, call Bonq trove functions.
- First realization: create a fresh ALBT trove, add
0.1 ALBT, and mint100,000,000 BEUR. - Second realization: submit the low ALBT value and execute repeated liquidations against Bonq troves that now fail the MCR check.
Bonq victim-facing addresses relevant to this path are:
0x3bb7ffd08f46620bea3a9ae7f096cf2b213768b3(BonqProxy / OriginalTroveFactory)0x20d50159aff262f953c8913ec859cac13a010b8a(TokenToPriceFeed)0x35b2ece5b1ed6a7a99b83508f8ceeab8661e0632(ALBT WrappedToken)
6. Impact & Losses
The incident created two deterministic impacts:
100,000,000 BEURwas minted into the attacker path in tx0x31957ecc43774d19f54d9968e95c69c882468b46860f921668f2c55fadd51b19.113,813,998.369826208354681311 ALBTwas accumulated during the liquidation phase summarized by tx0xa02d0c3d16d6ee0e0b6a42c3cc91997c2b40c87d777136dedebe8ee0f47f32b1.
The non-monetary exploit predicate is equally important: Bonq accepted a borrow from a newly created ALBT trove that would be undercollateralized under the pre-manipulation price. That is the core solvency break. The profit fields in root_cause.json now quantify the first BEUR realization deterministically: attacker entry contract 0xed596991ac5f1aa1858da66c67f7cfa76e54b5f1 moved from 0 to 100000000000000000000000000 BEUR, while BEUR-denominated fees remained 0 because gas was paid in POL by the attacker EOA.
7. References
- Seed exploit tx 1:
0x31957ecc43774d19f54d9968e95c69c882468b46860f921668f2c55fadd51b19 - Seed exploit tx 2:
0xa02d0c3d16d6ee0e0b6a42c3cc91997c2b40c87d777136dedebe8ee0f47f32b1 - Bonq factory / proxy:
0x3bb7ffd08f46620bea3a9ae7f096cf2b213768b3 - TokenToPriceFeed:
0x20D50159aff262f953C8913Ec859Cac13A010b8a - Converted ALBT feed:
0x7D4c36c79b89E1f3eA63A38C1DdB16EF8c394bc8 - TellorPriceFeed:
0xa1620Af6138D2754F7250299DC9024563bd1a5D6 - TellorFlex:
0x8f55D884CAD66B79e1a131f6bCB0e66f4fD84d5B - ALBT token:
0x35b2ECE5B1eD6a7a99b83508F8ceEAB8661E0632 - BEUR token:
0x338Eb4d394a4327E5dB80d08628fa56EA2FD4B81 - Tellor query ID used in both exploit phases:
0x12906c5e9178631dba86f1f750f7ab7451c61e6357160eb890029b9eac1fb235 - Collector evidence used for validation:
- seed trace and balance diff for tx
0x31957ecc43774d19f54d9968e95c69c882468b46860f921668f2c55fadd51b19 - seed trace and balance diff for tx
0xa02d0c3d16d6ee0e0b6a42c3cc91997c2b40c87d777136dedebe8ee0f47f32b1
- seed trace and balance diff for tx