Creating a DeFiLlama TVL Adapter
Drafted May 2023. Finished and published August 2026 as part of the migration away from WordPress.
Flashstake’s DeFiLlama listing was measuring an earlier version of the protocol. I replaced it with an adapter for Flashstake V3, then updated it as the protocol added Optimism, Arbitrum and new strategies.
The JavaScript was the easy part. The work was deciding what counted as TVL, making every read historical and finding the right boundary between the adapter and DeFiLlama’s pricing system.
Deciding what counted
Flashstake had four relevant balances:
- principal deposited into active strategies;
- yield generated and still held by those strategies;
- FLASH locked in the FlashBack staking contract; and
- LP tokens deposited into FlashBack liquidity-mining contracts.
Putting them into one total would mix protocol assets, project-token staking and incentivised liquidity. The adapter reported strategy principal and yield as base tvl, FLASH deposits as staking, and liquidity-mining deposits as pool2.
Strategy capital could also be deployed into another protocol, causing the same underlying assets to appear in both protocol-level figures. The adapter therefore declared doublecounted: true.
Replacing the project API
My first commit called a Flashstake endpoint with DeFiLlama’s requested block number and returned its aggregate totalTVL value as a synthetic USDT balance.
That corrected the obsolete listing, but it made DeFiLlama dependent on a number calculated by the project it was measuring. During review, DeFiLlama asked for on-chain measurement instead.
I rewrote the adapter before it merged. A subgraph discovered each strategy address and its principal token. The adapter then called getPrincipalBalance() and getYieldBalance() on each strategy, and totalLockedAmount() on the staking and liquidity-mining contracts. It returned raw token balances and left the dollar conversion to DeFiLlama.
This was still a hybrid design. The subgraph identified which contracts existed, while the contracts supplied the measured balances.
Historical reads had to be historical throughout
The initial rewrite exported timetravel: true, but some calls still read current state. I corrected this in two follow-up changes:
- PR 3981 passed the requested block into the subgraph query.
- PR 3983 passed it into every relevant contract call.
Otherwise the adapter could combine a historical strategy list with current balances, or today’s strategy list with historical balances. Both results could look reasonable while representing no real point in time.
Permissionless deployment needed curated reporting
Anyone could register a Flashstake strategy. Including every registration would let an arbitrary contract influence the public metric.
When I added Optimism, the adapter began checking discovered strategies against a reviewed list. That API supplied identity, not TVL. Once a strategy was accepted, its balances were still measured on-chain.
The Optimism change also introduced per-chain configuration and unwrapped locked LP positions. For an LP token balance, the adapter read the pool’s total supply and reserves, then returned the proportional share of each underlying asset:
share = locked LP tokens / total LP supply
underlying balance = pool reserve * share
The first version used bare token addresses as balance keys. A follow-up patch added the chain prefix, such as optimism:0x..., so the pricing system could identify the asset correctly.
The Arbitrum problem was a pricing problem
I added Arbitrum with its protocol deployment and two liquidity-mining contracts. One GLP strategy then appeared with no value because its token representation was not being priced.
I proposed substituting the GLP token inside the adapter. DeFiLlama instead added a server-side price for the original token representation and closed the patch.
That was the correct boundary. The adapter had found the right strategy and returned a legitimate balance. The failure happened when that balance reached the price service.
The adapter also carried hallmarks for protocol and strategy launches, ending with the Rocket Pool hallmark added yesterday. These markers did not change TVL, but they made changes in the historical chart easier to interpret.
Across nine pull requests, eight merged and one was replaced by the server-side pricing fix. The reusable rules were simple: define the metric before writing calls, pass the historical block through the entire read path, separate protocol identity from balance measurement, qualify token addresses by chain and trace a bad result through pricing before changing the adapter.