Skip to content

fix(venus): tolerate 96-byte uint256 view returns from BSC vTokens (closes #418)#420

Merged
obchain merged 1 commit intomainfrom
fix/418-venus-uint256-relaxed-decode
May 3, 2026
Merged

fix(venus): tolerate 96-byte uint256 view returns from BSC vTokens (closes #418)#420
obchain merged 1 commit intomainfrom
fix/418-venus-uint256-relaxed-decode

Conversation

@obchain
Copy link
Copy Markdown
Owner

@obchain obchain commented May 3, 2026

Summary

Several Venus vTokens on BSC are deployed behind upgradeable proxies that return 96 bytes (3 32-byte words) where the ABI declares a single `uint256` — leading word carries the value, trailing 64 bytes are zero padding (Diamond fallback / proxy quirk). `alloy`'s `sol!`-generated decoder rejects the surplus with `SolTypes(ReserveMismatch)`, surfaced as `borrowBalanceStored failed err=AbiError(SolTypes(ReserMismatch))` on every (vToken × seed-borrower) pair. Result on main: `tracked=N returned=0`, no opportunity ever emitted.

Fix

Add `read_uint256_view` helper that issues the `eth_call` ourselves, takes the first 32 bytes, decodes as `U256` — matching `cast call ... '(uint256)'`'s lenient policy. Use it for `borrowBalanceStored`, `balanceOf`, `exchangeRateStored` in `fetch_position`. Oracle `getUnderlyingPrice` stays on the typed call (no padding observed there).

Test plan

…loses #418)

Several Venus vTokens on BSC are deployed behind upgradeable proxies
that return 96 bytes (3 32-byte words) where the ABI declares a single
uint256 — the leading word carries the value, the trailing 64 bytes
are zero padding (likely a Diamond fallback / proxy quirk).

alloy's sol!-generated decoder is strict and rejects the surplus bytes
with SolTypes(ReserveMismatch), which surfaced as
`borrowBalanceStored failed err=AbiError(SolTypes(ReserMismatch))`
on every (vToken × seed-borrower) pair. Result: tracked=N returned=0,
the bot scanned every block but never emitted an opportunity, even
when the seed borrowers were genuinely liquidatable on mainnet.

Add `read_uint256_view` helper that issues the eth_call ourselves,
takes the first 32 bytes, and decodes as U256 — matching cast's
lenient policy. Use the helper for `borrowBalanceStored`,
`balanceOf`, and `exchangeRateStored` in fetch_position. The oracle
`getUnderlyingPrice` path stays on the typed call because no padding
issue has been observed there.

Local validation: replay against block 91323624 with the four
documented seed borrowers now reports
`tracked=4 returned=4 liquidatable=4` (was `returned=0` on main).
@obchain obchain merged commit 9108a4c into main May 3, 2026
4 checks passed
@obchain obchain deleted the fix/418-venus-uint256-relaxed-decode branch May 3, 2026 16:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant