The Art of DeFi Price Calculation: How Deep Does the Rabbit Hole Go?

Vestige Labs
6 min readOct 23, 2024

--

The Art of DeFi Price Calculation: How Deep Does the Rabbit Hole Go?

Ever wondered how DeFi platforms calculate asset prices across thousands of liquidity pools? It’s not as straightforward as you might think. In the Algorand ecosystem alone, there are 26,977 pools, with 19,075 actively holding funds. Let’s dive into the fascinating complexity behind price calculation in decentralized finance.

The Simple Case: Direct ALGO Pairs

At first glance, price calculation seems straightforward. When an asset is paired with ALGO (Algorand’s native token), you simply divide the amounts on each side of the pool. With 11,479 pools containing ALGO, this works well for many assets. However, this approach misses a crucial part of the picture.

The Complex Reality: Asset-to-Asset Pools

The real challenge lies in the 7,596 pools that pair non-ALGO assets together (ASA/ASA combinations). These pools create an intricate web of price relationships that can’t be ignored. When a significant swap occurs in one pool, it creates a ripple effect throughout the entire ecosystem.

Consider this scenario: A large ALGO/USDC swap doesn’t just affect the ALGO/USDC price — it impacts every asset with a USDC pool. The magnitude of this impact is proportional to the percentage of an asset’s Total Value Locked (TVL) held in USDC.

Our Solution: A Pragmatic Approach

To tackle this complexity, we developed a method that approximates prices while maintaining accuracy. Here’s how it works:

1. Aggregate Pool Data

First, we need to sum up all asset amounts across different pools. To simplify these calculations for the purpose of this article, we will only include the simplest AMM pools using the k=x*y formula.

asset_lockups = {}
total_asset_lockups = {}

# rows are fetched from the database to get current pool states
for row in rows:
_, asset_1_id, asset_2_id, asset_1_supply, asset_2_supply = row
a1s = int(asset_1_supply)
a2s = int(asset_2_supply)

if asset_1_id not in asset_lockups:
asset_lockups[asset_1_id] = defaultdict(int)
if asset_2_id not in asset_lockups:
asset_lockups[asset_2_id] = defaultdict(int)

asset_lockups[asset_1_id][asset_2_id] += a1s
asset_lockups[asset_2_id][asset_1_id] += a2s
total_asset_lockups[asset_1_id] = total_asset_lockups.get(asset_1_id, 0) + a1s
total_asset_lockups[asset_2_id] = total_asset_lockups.get(asset_2_id, 0) + a2s

2. Initialize Price Tracking

We establish confidence scores and initial prices, starting with the network token (ALGO):

asset_confidence = defaultdict(lambda: 0.)
asset_confidence[network_token] = 1.0
asset_price = defaultdict(lambda: 0.)
asset_price[network_token] = 1.0

# Set up initial state if not provided
if not previous_confidence:
previous_confidence = defaultdict(lambda: 0.)
previous_confidence[network_token] = 1.0
if not previous_price:
previous_price = defaultdict(lambda: 0.)
previous_price[network_token] = 1.0

3. Calculate Prices Iteratively

The heart of our solution lies in this iterative calculation:

for asset in assets:
confidence = 0
asset_sum = 0
network_token_sum = 0
asset_pairs = asset_lockups[asset]
total_lockup = total_asset_lockups[asset]

for other_asset, lockup_amount in asset_pairs.items():
# Calculate confidence based on the other asset
prev_conf = previous_confidence.get(other_asset, 0)
confidence += (lockup_amount / total_lockup) * prev_conf

if lockup_amount == 0:
continue

prev_price = previous_price.get(other_asset, 0)
if prev_price == 0:
continue
# Adjust price impact based on confidence
asset_sum += prev_conf * lockup_amount
network_token_sum += prev_conf * asset_lockups[other_asset][asset] * prev_price

asset_confidence[asset] = confidence
asset_price[asset] = (network_token_sum / asset_sum) if asset_sum != 0 else 0

The Results: Confidence vs. Performance

Let’s look at how this plays out across multiple calculation loops:

=== Loop 1 ===
USDC [31566704]: Confidence = 0.8902, price = 8.2639 ALGO per USDC
gALGO [793124631]: Confidence = 0.9925, price = 0.9805 ALGO per gALGO
goBTC [386192725]: Confidence = 0.7284, price = 547099.8007 ALGO per goBTC
TINY [2200000000]: Confidence = 0.8477, price = 0.2802 ALGO per TINY
VEST [700965019]: Confidence = 0.9107, price = 0.0802 ALGO per VEST
ORA [1284444444]: Confidence = 0.6985, price = 0.7087 ALGO per ORA
ASASTATS [393537671]: Confidence = 0.7525, price = 0.0012 ALGO per ASASTATS
OSTDLUKENS [1189552380]: Confidence = 0.4879, price = 0.1115 ALGO per OSTDLUKENS
OSTDHBVA [1105062628]: Confidence = 0.2434, price = 0.0994 ALGO per OSTDHBVA
GTVL2 [764075427]: Confidence = 0.0000, price = 0.0000 ALGO per GTVL2
ITVL [758205935]: Confidence = 0.0000, price = 0.0000 ALGO per ITVL

=== Loop 2 ===
USDC [31566704]: Confidence = 0.9497, price = 8.2573 ALGO per USDC
gALGO [793124631]: Confidence = 0.9960, price = 0.9805 ALGO per gALGO
goBTC [386192725]: Confidence = 0.8666, price = 546648.5336 ALGO per goBTC
TINY [2200000000]: Confidence = 0.9729, price = 0.2802 ALGO per TINY
VEST [700965019]: Confidence = 0.9726, price = 0.0801 ALGO per VEST
ORA [1284444444]: Confidence = 0.9287, price = 0.7094 ALGO per ORA
ASASTATS [393537671]: Confidence = 0.9711, price = 0.0012 ALGO per ASASTATS
OSTDLUKENS [1189552380]: Confidence = 0.6167, price = 0.1116 ALGO per OSTDLUKENS
OSTDHBVA [1105062628]: Confidence = 0.6584, price = 0.0993 ALGO per OSTDHBVA
GTVL2 [764075427]: Confidence = 0.0000, price = 0.0000 ALGO per GTVL2
ITVL [758205935]: Confidence = 0.0000, price = 0.0000 ALGO per ITVL

=== Loop 3 ===
USDC [31566704]: Confidence = 0.9886, price = 8.2571 ALGO per USDC
gALGO [793124631]: Confidence = 0.9988, price = 0.9805 ALGO per gALGO
goBTC [386192725]: Confidence = 0.9507, price = 546615.8312 ALGO per goBTC
TINY [2200000000]: Confidence = 0.9884, price = 0.2801 ALGO per TINY
VEST [700965019]: Confidence = 0.9943, price = 0.0801 ALGO per VEST
ORA [1284444444]: Confidence = 0.9682, price = 0.7094 ALGO per ORA
ASASTATS [393537671]: Confidence = 0.9910, price = 0.0012 ALGO per ASASTATS
OSTDLUKENS [1189552380]: Confidence = 0.8267, price = 0.1116 ALGO per OSTDLUKENS
OSTDHBVA [1105062628]: Confidence = 0.7508, price = 0.0993 ALGO per OSTDHBVA
GTVL2 [764075427]: Confidence = 0.0000, price = 0.0000 ALGO per GTVL2
ITVL [758205935]: Confidence = 0.0000, price = 0.0079 ALGO per ITVL

With each loop, the difference in price gets smaller & the confidence gets larger. You may also notice that the confidence number works for the judgement of how healthy is the distribution of an assets TVL: if the asset has a lot of pools with ALGO and USDC, the confidence grows a lot faster than when most of the assets TVL is in less popular pools. This is especially displayed in the scores of GTVL2 and ITVL, which were the infamous assets used to exploit Tinymans TVL calculation.

Confidence of course does not imply any sort of security about the asset. It just means how close we are to knowing the actual price.

=== Loop 100 ===
USDC [31566704]: Confidence = 1.0000, price = 8.2563 ALGO per USDC
gALGO [793124631]: Confidence = 1.0000, price = 0.9805 ALGO per gALGO
goBTC [386192725]: Confidence = 1.0000, price = 546562.2704 ALGO per goBTC
TINY [2200000000]: Confidence = 1.0000, price = 0.2801 ALGO per TINY
VEST [700965019]: Confidence = 1.0000, price = 0.0801 ALGO per VEST
ORA [1284444444]: Confidence = 1.0000, price = 0.7094 ALGO per ORA
ASASTATS [393537671]: Confidence = 1.0000, price = 0.0012 ALGO per ASASTATS
OSTDLUKENS [1189552380]: Confidence = 1.0000, price = 0.1115 ALGO per OSTDLUKENS
OSTDHBVA [1105062628]: Confidence = 1.0000, price = 0.0992 ALGO per OSTDHBVA
GTVL2 [764075427]: Confidence = 0.0000, price = 0.0086 ALGO per GTVL2
ITVL [758205935]: Confidence = 0.0000, price = 0.0079 ALGO per ITVL

Calculated 100 loops in 1.0117 seconds

You may notice that the price of USDC from loop 3 (8.2571) and from loop 100 (8.2563) is different only by 0.01%, much lower than the difference in confidence: 1.14%. So, we’re set, right? Calculate as many loops as you can and your prices are perfect. If only we could!

The Engineering Trade-off

While 100 loops might seem ideal, there’s a catch: processing time. With each block Algorand can process thousands of swaps. New blocks also appear every 3 seconds. We had to find a balance.

Our solution? Five loops — achieving 99.7% confidence for USDC and over 90% for most assets, while maintaining real-time performance.

This explains why platforms like Dexscreener show single-pool charts rather than true asset prices — aggregating DeFi data is surprisingly complex!

This article provides a glimpse into our work on Defindexer, our upcoming DeFi analytics platform. Stay tuned for more insights into the fascinating world of decentralized finance.

--

--

Vestige Labs
Vestige Labs

Written by Vestige Labs

Vestige Labs is a software house for Algorand Virtual Machine.