back to docs
contributions

Variational Omni

Track Variational Omni volume & open interest

My fourth contribution to DefiLlama, tracking volume and open interest for Variational Omni perpetual futures on Arbitrum.

  • Protocol: Variational Omni
  • Chain: Arbitrum
  • Type: DEX derivatives (volume + open interest)
  • Start Date: 2025-01-30 (Mainnet Private Beta)
  • Approach: API-based stats tracking
Status

Status: Closed (Refactored by maintainer)

My PR: #5352

Maintainer’s PR: #5359 Merged

Closed: December 24, 2024

1. Background story

a. Picking a protocol

After Solomon, I wanted to keep building on what I learned:

  • API-based adapters (not events)
  • Revenue taxonomy (protocol vs supply side)
  • SimpleAdapter patterns

I found Variational Omni, a derivatives DEX on Arbitrum that had public API stats.

b. Issue #5349

There was an issue requesting tracking for Variational:

  • Track daily volume
  • Track open interest
  • Data source: https://omni-client-api.prod.ap-northeast-1.variational.io/metadata/stats

This seemed straightforward. Fetch the API, extract volume and OI metrics.

2. My implementation (PR #5352)

a. Initial code

I created open-interest/variational.ts:

import { FetchOptions, SimpleAdapter } from "../adapters/types";
import { CHAIN } from "../helpers/chains";
import fetchURL from "../utils/fetchURL";

const URL = "https://omni-client-api.prod.ap-northeast-1.variational.io/metadata/stats";

const fetch = async (_: any, __: any, options: FetchOptions) => {
  const data = await fetchURL(URL);
  const listings = data?.listings ?? [];

  // Calculate open interest by summing long and short OI
  const openInterestAtEnd = listings.reduce((acc: number, market: any) => {
    const longOI = Number(market?.open_interest?.long_open_interest || 0);
    const shortOI = Number(market?.open_interest?.short_open_interest || 0);
    return acc + longOI + shortOI;
  }, 0);

  const dailyVolume = Number(data?.total_volume_24h || 0);

  return {
    openInterestAtEnd,
    dailyVolume,
  };
};

const adapter: SimpleAdapter = {
  version: 2,
  fetch,
  chains: [CHAIN.ARBITRUM],
  runAtCurrTime: true,
  start: "2025-01-30", // Mainnet Private Beta
};

export default adapter;

b. What I thought

I was confident because:

  • Used SimpleAdapter (learned from Solomon)
  • API-based approach
  • Tracked both volume and open interest
  • Correct start date (mainnet launch)
  • runAtCurrTime: true for real-time data

c. Test results

CI tests passed! llamabutler showed:

> [email protected] test
> ts-node --transpile-only cli/testAdapter.ts open-interest variational.ts

🦙 Running VARIATIONAL.TS adapter 🦙
---------------------------------------------------
Start Date:	Mon, 22 Dec 2025 21:26:17 GMT
End Date:	Tue, 23 Dec 2025 21:26:17 GMT
---------------------------------------------------

ARBITRUM 👇
Backfill start time: 30/1/2025
Open interest at end: 239.34 M
Daily volume: 976.74 M
End timestamp: 1766525176 (2025-12-23T21:26:16.000Z)

Both metrics were working! I thought this was ready to merge.

d. My comment

I asked for validation:

“#5349 If this interpretation is incorrect, please let me know. Thank you.”

I was learning to ask for feedback proactively.

3. The issue

a. Maintainer couldn’t checkout

treeoflife2 closed my PR with this comment:

“closing this PR as for some reason i can’t checkout to your master branch”

Technical issue: I was working on my master branch instead of a feature branch. This made it difficult for maintainers to:

  • Check out my code locally
  • Test it in their environment
  • Make edits if needed

b. The refactor: PR #5359

treeoflife2 created a new PR with my code, but with an important change:

“your approach was good, only problem was they are still working on the oi, so i’ve ommited that for now.”

4. What changed (comparison)

a. My version (PR #5352):

const fetch = async (_: any, __: any, options: FetchOptions) => {
  const data = await fetchURL(URL);
  const listings = data?.listings ?? [];

  // Calculate OI
  const openInterestAtEnd = listings.reduce((acc: number, market: any) => {
    const longOI = Number(market?.open_interest?.long_open_interest || 0);
    const shortOI = Number(market?.open_interest?.short_open_interest || 0);
    return acc + longOI + shortOI;
  }, 0);

  const dailyVolume = Number(data?.total_volume_24h || 0);

  return {
    openInterestAtEnd,  // ← Included OI
    dailyVolume,
  };
};

b. Maintainer’s version (PR #5359):

const fetch = async (_: any) => {  // ← Simplified signature
  const data = await fetchUrl(URL);
  const listings = data?.listings ?? [];
  const dailyVolume = Number(data?.total_volume_24h || 0);

  // COMMENTED OUT - OI still being worked on by protocol
  // const openInterestAtEnd = listings.reduce((acc: number, market: any) => {
  // const longOI = Number(market?.open_interest?.long_open_interest || 0);
  // const shortOI = Number(market?.open_interest?.short_open_interest || 0);
  // return acc + longOI + shortOI;
  // }, 0);

  // const dualSidedOpenInterestAtEnd = openInterestAtEnd * 2;

  return {
    // openInterestAtEnd: dualSidedOpenInterestAtEnd,  // ← Commented out
    dailyVolume,  // ← Only volume tracked
  };
};

c. Key differences

AspectMy VersionFinal Version
File Locationopen-interest/variational.tsdexs/variational-omni/index.ts
Open InterestTrackedCommented out (protocol still working on it)
Function Signature(_: any, __: any, options: FetchOptions)(_: any)
OI CalculationSingle-sided (long + short)Would be dual-sided * 2 (commented)
BranchMy masterMaintainer’s feature branch

5. What went wrong

a. Git workflow issue

My mistake: Worked on my master branch

Why this is bad:

  • Maintainers can’t easily checkout your code
  • Hard to collaborate (can’t push commits to help)
  • Makes it difficult to keep your fork in sync
  • Not following Git best practices

Correct workflow:

1
Create feature branch
git checkout -b feat/variational-volume-oi

Always branch off from the latest main/master before starting work.

2
Make changes and commit
git add .
git commit -m "feat: track variational vol & OI"

Use descriptive commit messages following conventional commits format.

3
Push feature branch
git push origin feat/variational-volume-oi

Push to your fork’s feature branch, not master.

4
Create PR from feature branch

Open a pull request from your-fork/feat/variational-volume-oi to upstream/main.

This allows maintainers to easily checkout and collaborate on your code.

b. Protocol data readiness

My assumption: Open interest data is stable and ready to track

Reality: Protocol was “still working on the oi”

Lesson: Even if API returns data, check if:

  • The data is finalized
  • The protocol considers it production-ready
  • There might be changes coming

c. Dual-sided OI calculation

Looking at the commented code, the maintainer would have used:

const dualSidedOpenInterestAtEnd = openInterestAtEnd * 2;

My approach: long OI + short OI

Their approach: (long OI + short OI) * 2

Why dual-sided? In derivatives, both sides of a trade count:

  • Buyer’s long position = open interest
  • Seller’s short position = open interest
  • Total OI = both sides = multiply by 2

I didn’t know this derivatives convention!

6. What I learned

a. Git workflow

  • Always use feature branches for PRs
  • Never work directly on master
  • Format: feat/feature-name or fix/issue-name
  • Makes collaboration easier

b. Protocol communication

  • Check data readiness: Just because API returns data doesn’t mean it’s production-ready
  • Ask about stability: “Is this metric finalized or still in development?”
  • Monitor protocol updates: Features might be work-in-progress

c. Derivatives metrics

  • Open Interest: Position-based, not trade-based
  • Dual-sided counting: Both long and short count as OI
  • Volume: Trade-based, already counted correctly

d. What went right

Despite the issues:

  • Code structure was good (“your approach was good”)
  • API integration worked correctly
  • Tests passed
  • Volume tracking was accurate
  • Applied SimpleAdapter patterns correctly

7. The outcome

a. PR closed, but code used

My PR was closed, but the maintainer:

  • Took my implementation
  • Fixed the git workflow issue
  • Omitted OI (not ready yet)
  • Merged it in PR #5359

This is still a success! My code was used, just needed workflow fixes.

b. What got merged

// Volume tracking - MY CODE
const dailyVolume = Number(data?.total_volume_24h || 0);

// OI tracking - COMMENTED FOR NOW
// (Will be enabled when protocol finalizes it)

8. Comparison with previous contributions

a. Growth progression:

  1. Taiko

    • Event-based chain fees
    • Reviewer helped with calculation
    • Merged
  2. Bitfi

    • Multi-protocol complexity
    • Closed, research used
    • Learned protocol slugs
  3. Solomon

    • API-based, revenue taxonomy
    • Fixed after review
    • Merged
  4. Variational

    • API-based derivatives
    • Git workflow issue
    • Code used (closed PR, but merged in new one)

b. New lesson: git workflow

This was the first time the issue wasn’t about code quality or concepts. It was about collaboration workflow.

9. Resources

10. Key takeaways

a. What I did right:

  • Code structure was sound
  • API integration worked
  • Tests passed
  • Applied previous learnings (SimpleAdapter, API patterns)
  • Asked for feedback proactively

b. What I learned:

  • Git workflow matters: Always use feature branches
  • Check data readiness: API data ≠ production-ready
  • Derivatives conventions: Dual-sided OI counting
  • File locations: dexs/ vs open-interest/ folder structure

c. For next time:

# Proper workflow
git checkout -b feat/protocol-name
# ... make changes ...
git push origin feat/protocol-name
# Create PR from feature branch

d. The positive:

Even though my PR was closed:

  • My code was good enough to use
  • Maintainer said “your approach was good”
  • Just needed workflow fixes
  • Code is in production

This shows progress. Code quality is improving. I just need to refine the process!

Command Palette
Search for a command to run...