Skip to content

Add OroSwap yield adapter#2745

Open
permapod-rsk wants to merge 3 commits into
DefiLlama:masterfrom
permapod:oroswap-yield
Open

Add OroSwap yield adapter#2745
permapod-rsk wants to merge 3 commits into
DefiLlama:masterfrom
permapod:oroswap-yield

Conversation

@permapod-rsk

@permapod-rsk permapod-rsk commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Oroswap protocol integration: Added support for active mainnet pools with yield analytics, including TVL (USD), base APY, pool symbol and metadata, and underlying token denominations. The adapter gathers pool data via contract pagination, resolves pricing to compute TVL, and estimates APY from sampled recent swap fees, then formats results consistently and sorts by highest TVL.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

A new src/adaptors/oroswap/index.js file implements a yield adaptor for Oroswap that discovers pools via factory contract pagination, queries individual pool contracts to extract asset composition, resolves token prices via API and on-chain derivation, calculates pool TVL, and computes APY by sampling swap events from the blockchain to estimate annualized fees.

Changes

Oroswap Fee-Based Yield Adaptor

Layer / File(s) Summary
Configuration constants and utility functions
src/adaptors/oroswap/index.js
Defines RPC/LCD endpoints, contract addresses, whitelisted pool contracts, pagination/concurrency settings, token denomination metadata, and generic helpers for sleep, retry with backoff, array chunking, and concurrency-limited request mapping.
Factory pagination and pool contract queries
src/adaptors/oroswap/index.js
Implements CosmWasm contract query wrappers and paginates the factory contract using start_after/limit to enumerate pair contracts, filters to whitelisted addresses, applies request delays, and fetches individual pool contracts to extract asset denoms and amounts.
Token normalization and price resolution
src/adaptors/oroswap/index.js
Maps denoms to token objects with symbol/decimals, derives symbols for coin./ibc/ denoms, formats pool display symbols, fetches token prices in batches from the project API, conditionally resolves stZIG pricing via staker-contract query, and iteratively infers missing prices across pools when one asset side is priced. Computes TVL from normalized amounts and resolved prices.
On-chain swap-event sampling and fee aggregation
src/adaptors/oroswap/index.js
Queries RPC tx_search for recent swap events, parses transaction attributes to accumulate USD fees and volumes using token prices and fee-share values, aggregates sampled statistics, scales partial samples to total transaction counts, and normalizes fees over 1-day windows to produce per-pool fee statistics.
Yield computation and module exports
src/adaptors/oroswap/index.js
Orchestrates the full pipeline with retry tolerance, resolves/derives prices, filters to finite positive TVL, computes annualized apyBase from scaled fee stats, formats yield objects with pool, chain, project, formatted symbol, underlyingTokens, URL, optional volumeUsd1d and poolMeta, sorts by descending TVL, and exports protocolId, apy, timetravel: false, and app URL.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • 0xkr3p

Poem

🐇 Oroswap's pools bloom bright and true,
CosmWasm queries hop through and through,
Swap events sampled, fees distilled to APY,
A 500-line journey through the blockchain sky,
The bunny's adaptor hops forth with cheer,
Another yield source crystal clear! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a new OroSwap yield adapter to the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/adaptors/oroswap/index.js`:
- Around line 56-58: The fallback value for total on line 56 in the OROSwap
adaptor is set to pools.length when data.total is unavailable, but pools.length
only represents the current page size, not the total count of all pages. This
causes the pagination loop to exit prematurely after the first full page since
offset will exceed the fallback total value. Replace the fallback from
pools.length to Infinity (or another sentinel value that won't prematurely
terminate pagination) to ensure that pagination continues based on the
page.length < PAGE_SIZE condition rather than relying on an inaccurate total
count.
- Around line 47-51: The fetchPools() function has two error handling gaps that
cause instability. First, the axios.get() call within the while loop at lines
47-51 lacks error handling; wrap this call and the entire loop in a try-catch
block to gracefully handle 403 and network errors instead of letting them
propagate up to the apy() function. Second, the fallback pagination logic at
line 56 where total is set to pools.length causes premature loop termination
when the first API response contains exactly PAGE_SIZE items (e.g., 150 items),
making the while condition false on the next iteration; fix this by either
continuing pagination when the current batch equals PAGE_SIZE, or implementing a
fallback that doesn't assume pagination has completed based on the current pool
count. Ensure both the error handling and pagination logic properly account for
incomplete data responses.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ec1790d3-60b8-48f5-a995-48f4aceb0c20

📥 Commits

Reviewing files that changed from the base of the PR and between fa55861 and 92ebe5b.

📒 Files selected for processing (1)
  • src/adaptors/oroswap/index.js

Comment thread src/adaptors/oroswap/index.js Outdated
Comment thread src/adaptors/oroswap/index.js Outdated
@github-actions

Copy link
Copy Markdown

The oroswap adapter exports pools:

Test Suites: 1 passed, 1 total
Tests: 41 passed, 41 total
Snapshots: 0 total
Time: 0.234 s
Ran all test suites.

Nb of pools: 6
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────────────────────────┬────────────┬───────────┬───────────────┬───────────────────┬───────────────────────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────┬────────────────────┬──────────┐
│ (index) │ pool                                                                      │ chain      │ project   │ symbol        │ tvlUsd            │ apyBase               │ underlyingTokens                                                                                                                                   │ url                                                                                                 │ volumeUsd1d        │ poolMeta │
├─────────┼───────────────────────────────────────────────────────────────────────────┼────────────┼───────────┼───────────────┼───────────────────┼───────────────────────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────┼────────────────────┼──────────┤
│ 0       │ 'zig1h72z8ptvcdqvuvy2lqanupwtextjmjmktj2ejgne2padxk0z8zds48shzq-zigchain' │ 'ZIGChain' │ 'oroswap' │ 'STZIG-ZIG'   │ 4015155.948965622 │ 0.7751544116570342    │ [ 'coin.zig109f7g2rzl2aqee7z6gffn8kfe9cpqx0mjkk7ethmx8m2hq4xpe9snmaam2.stzig', 'uzig' ]                                                            │ 'https://app.oroswap.org/pools?pool=zig1h72z8ptvcdqvuvy2lqanupwtextjmjmktj2ejgne2padxk0z8zds48shzq' │ 1065899.007102561  │ 'XYK'    │
│ 1       │ 'zig186ucx5mtdq6ams8rsvvcu7yfw5lhtxue8ykdkyqvlnk3gpc77lasw8373h-zigchain' │ 'ZIGChain' │ 'oroswap' │ 'USDC-ZIG'    │ 384497.4780791671 │ 3.0658023229345948    │ [ 'ibc/6490A7EAB61059BFC1CDDEB05917DD70BDF3A611654162A1A47DB930D40D8AF4', 'uzig' ]                                                                 │ 'https://app.oroswap.org/pools?pool=zig186ucx5mtdq6ams8rsvvcu7yfw5lhtxue8ykdkyqvlnk3gpc77lasw8373h' │ 16094.579770994595 │ 'XYK_25' │
│ 2       │ 'zig15sllxl4qevqdfpfuzdelvqldd3qsxjx9u2lrwkkrsh8hpuu97rqs63fsl7-zigchain' │ 'ZIGChain' │ 'oroswap' │ 'STATOM-ATOM' │ 185211.4288224611 │ 0.0006568044503545401 │ [ 'ibc/729AE368985B0F7B35648D17BA74A3D0A1C833FBAE8C3B49D75C6A2B578B9930', 'ibc/EF48E6B1A1A19F47ECAEA62F5670C37C0580E86A9E88498B7E393EB6F49F33C0' ] │ 'https://app.oroswap.org/pools?pool=zig15sllxl4qevqdfpfuzdelvqldd3qsxjx9u2lrwkkrsh8hpuu97rqs63fsl7' │ 4.168758502113719  │ 'XYK_10' │
│ 3       │ 'zig1ucwnlul7t97fx4xr83kzxt76pdgk86gmz36adl8mzk890frduwuslq09t6-zigchain' │ 'ZIGChain' │ 'oroswap' │ 'USDT-ZIG'    │ 48769.05383929409 │ 0.538548968642365     │ [ 'ibc/630F28419AFD118B9EA8B96AE9D280CFDA4EB9FAB3108F1CA9E7DC00F396B4F9', 'uzig' ]                                                                 │ 'https://app.oroswap.org/pools?pool=zig1ucwnlul7t97fx4xr83kzxt76pdgk86gmz36adl8mzk890frduwuslq09t6' │ 884.63187266609    │ 'XYK_10' │
│ 4       │ 'zig12uxfwsdsl7jrjrmuunflz4avqc34myaqtv8s84mcfryl49kc6qeqtdz5me-zigchain' │ 'ZIGChain' │ 'oroswap' │ 'WBTC-ZIG'    │ 44539.66954710145 │ 0.04143154818265344   │ [ 'ibc/7B02D9C0746D2D6551CD358D4C7ABAC6EE94527D613BACFDEE122559CEB41AA4', 'uzig' ]                                                                 │ 'https://app.oroswap.org/pools?pool=zig12uxfwsdsl7jrjrmuunflz4avqc34myaqtv8s84mcfryl49kc6qeqtdz5me' │ 652.1401889561738  │ 'XYK'    │
│ 5       │ 'zig1wvqk2tm38dn3yp9ry7h5zzjqv3tytzpwpkkg55w3j58cdhvqrnfq78fe45-zigchain' │ 'ZIGChain' │ 'oroswap' │ 'ZIG-ATOM'    │ 8367.169471669911 │ 0.10609344102010845   │ [ 'uzig', 'ibc/EF48E6B1A1A19F47ECAEA62F5670C37C0580E86A9E88498B7E393EB6F49F33C0' ]                                                                 │ 'https://app.oroswap.org/pools?pool=zig1wvqk2tm38dn3yp9ry7h5zzjqv3tytzpwpkkg55w3j58cdhvqrnfq78fe45' │ 303.87518621214707 │ 'XYK'    │
└─────────┴───────────────────────────────────────────────────────────────────────────┴────────────┴───────────┴───────────────┴───────────────────┴───────────────────────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────┴────────────────────┴──────────┘
This adapter contains some pools with <10k TVL, these pools won't be shown in DefiLlama

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
src/adaptors/oroswap/index.js (4)

67-67: 💤 Low value

Inconsistent object key quoting may cause issues.

The IBC hash CC5268F89C752A4BDCBDAA574AF0A381786FCC839104E077DA9A9145176BF8ED is unquoted while all other IBC hashes are quoted strings. JavaScript allows this because it starts with letters, but it's inconsistent and could lead to confusion or bugs if someone copies this pattern for a hash starting with digits.

🔧 Suggested fix for consistency
-    CC5268F89C752A4BDCBDAA574AF0A381786FCC839104E077DA9A9145176BF8ED: {
+    'CC5268F89C752A4BDCBDAA574AF0A381786FCC839104E077DA9A9145176BF8ED': {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adaptors/oroswap/index.js` at line 67, The object key with the IBC hash
CC5268F89C752A4BDCBDAA574AF0A381786FCC839104E077DA9A9145176BF8ED is unquoted
while all other IBC hash keys in the object are quoted strings. Add quotes
around this hash key to make it consistent with the rest of the object
properties and follow JavaScript object literal conventions.

420-431: 💤 Low value

Silent error swallowing may hide persistent RPC issues.

The catch block swallows errors without any logging. While this keeps the adapter functional when individual pools fail, it makes it difficult to detect persistent RPC problems or misconfigured pools.

💡 Optional: Add minimal error logging
     } catch (e) {
-      // Keep the adapter usable if one RPC tx_search call times out.
+      // Keep the adapter usable if one RPC tx_search call times out.
+      console.warn(`[oroswap] Fee stats failed for ${pool.pair.contract_addr}: ${e.message}`);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adaptors/oroswap/index.js` around lines 420 - 431, The catch block in the
mapLimit function that processes pools is silently swallowing errors without any
logging, making it difficult to detect persistent RPC issues or misconfigured
pools. Add minimal error logging within the empty catch block to log the error
details along with the pool identifier (pool.pair.contract_addr) so that RPC
timeouts and other failures can be monitored and debugged while still
maintaining adapter functionality when individual pools fail.

479-479: 💤 Low value

Variable denoms shadows outer scope declaration.

The const denoms on this line shadows the denoms variable declared on line 461. While the outer variable is not used after the price fetching, this shadowing can cause confusion during maintenance.

🔧 Consider renaming for clarity
-      const denoms = assets.map((asset) => asset.denom);
+      const poolDenoms = assets.map((asset) => asset.denom);
       const stats = feeStats.get(pair.contract_addr);
       const apyBase =
         stats?.day?.feesUsd && tvlUsd > 0
           ? (stats.day.feesUsd * 365 * 100) / tvlUsd
           : 0;
       const mapped = {
         pool: `${pair.contract_addr}-${CHAIN}`.toLowerCase(),
         chain: CHAIN,
         project: PROJECT,
-        symbol: utils.formatSymbol(getPoolSymbol(denoms)),
+        symbol: utils.formatSymbol(getPoolSymbol(poolDenoms)),
         tvlUsd,
         apyBase,
-        underlyingTokens: denoms,
+        underlyingTokens: poolDenoms,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adaptors/oroswap/index.js` at line 479, The const denoms variable
declared on line 479 that maps asset objects to their denom properties shadows
the outer denoms variable declared on line 461, which can cause confusion during
code maintenance. Rename the inner denoms variable on line 479 (the one created
from the assets.map call) to a more descriptive name that clarifies its purpose,
such as assetDenoms or denominationList, to eliminate the shadowing and improve
code clarity.

109-122: ⚡ Quick win

Custom withRetry retries all errors, unlike utils.withRetry.

The repository's utils.withRetry (shown in relevant snippets) only retries transient failures (HTTP 429 and 5xx), failing fast on permanent 4xx errors. This local implementation retries all errors unconditionally, which could:

  1. Waste time retrying permanent failures (400, 404, etc.)
  2. Mask configuration issues that should fail fast

Consider using utils.withRetry for consistency, or add status-code filtering if the broader retry behavior is intentional.

♻️ Option: Use the shared utility
-const withRetry = async (fn, attempts = 3) => {
-  let lastError;
-
-  for (let i = 0; i < attempts; i++) {
-    try {
-      return await fn();
-    } catch (error) {
-      lastError = error;
-      if (i < attempts - 1) await sleep(500 * (i + 1));
-    }
-  }
-
-  throw lastError;
-};
+const withRetry = (fn) => utils.withRetry(fn, { retries: 3, delayMs: 500 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/adaptors/oroswap/index.js` around lines 109 - 122, Replace the custom
withRetry function with the shared utils.withRetry utility to ensure consistent
retry behavior across the codebase. The local implementation unconditionally
retries all errors including permanent failures like 4xx status codes, whereas
utils.withRetry correctly filters to only retry transient failures such as HTTP
429 and 5xx errors. Update all calls to the local withRetry to use
utils.withRetry instead, and remove the custom implementation from this file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/adaptors/oroswap/index.js`:
- Line 67: The object key with the IBC hash
CC5268F89C752A4BDCBDAA574AF0A381786FCC839104E077DA9A9145176BF8ED is unquoted
while all other IBC hash keys in the object are quoted strings. Add quotes
around this hash key to make it consistent with the rest of the object
properties and follow JavaScript object literal conventions.
- Around line 420-431: The catch block in the mapLimit function that processes
pools is silently swallowing errors without any logging, making it difficult to
detect persistent RPC issues or misconfigured pools. Add minimal error logging
within the empty catch block to log the error details along with the pool
identifier (pool.pair.contract_addr) so that RPC timeouts and other failures can
be monitored and debugged while still maintaining adapter functionality when
individual pools fail.
- Line 479: The const denoms variable declared on line 479 that maps asset
objects to their denom properties shadows the outer denoms variable declared on
line 461, which can cause confusion during code maintenance. Rename the inner
denoms variable on line 479 (the one created from the assets.map call) to a more
descriptive name that clarifies its purpose, such as assetDenoms or
denominationList, to eliminate the shadowing and improve code clarity.
- Around line 109-122: Replace the custom withRetry function with the shared
utils.withRetry utility to ensure consistent retry behavior across the codebase.
The local implementation unconditionally retries all errors including permanent
failures like 4xx status codes, whereas utils.withRetry correctly filters to
only retry transient failures such as HTTP 429 and 5xx errors. Update all calls
to the local withRetry to use utils.withRetry instead, and remove the custom
implementation from this file.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 980d1010-fae7-4c7a-8a8c-a5df5d0748b0

📥 Commits

Reviewing files that changed from the base of the PR and between 420bddb and facea26.

📒 Files selected for processing (1)
  • src/adaptors/oroswap/index.js

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