Skip to content

Update APY calculation for Kintsu#2655

Open
realnimish wants to merge 1 commit into
DefiLlama:masterfrom
WaterCoolerStudiosInc:feat/kintsu-restructure-apy-calculation
Open

Update APY calculation for Kintsu#2655
realnimish wants to merge 1 commit into
DefiLlama:masterfrom
WaterCoolerStudiosInc:feat/kintsu-restructure-apy-calculation

Conversation

@realnimish
Copy link
Copy Markdown

@realnimish realnimish commented May 6, 2026

  • Switch APY calculation based on 1 day window to 7 days window
  • Add inception APY
  • Refactor code

Summary by CodeRabbit

  • New Features

    • Inception-based APY calculations now included in results
    • Added price-per-share metrics and underlying token metadata
    • Enhanced TVL calculations with USD denomination
  • Refactor

    • Redesigned multi-chain configuration framework for improved flexibility and modularity

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

📝 Walkthrough

Walkthrough

The Kintsu adaptor refactors from single-chain hardcoded constants to a modular multi-chain configuration with new utility functions for block resolution, price retrieval, and inception-based APY computation using share value calculations.

Changes

Kintsu Adaptor Refactoring

Layer / File(s) Summary
Constants & Data Structure
src/adaptors/kintsu/index.js (lines 3–16)
SCALE and WMON constants introduced; legacy SECONDS_PER_DAY and DAYS_PER_YEAR removed. New chains object replaces simple vault/priceId layout with multi-chain monad configuration including vault address, priceId, and inception block/timestamp.
Helper Utilities
src/adaptors/kintsu/index.js (lines 17–46)
New functions added: call (ABI wrapper), getBlock (resolve block height by timestamp), getUsdPrice (fetch price by ID), getShareValue (per-share calculation with scale), and annualize (convert changes to APR).
Core APY Logic
src/adaptors/kintsu/index.js (lines 48–75)
New chainApy function orchestrates per-chain calculation: fetches current and 7-day block data, computes share values, retrieves symbol and USD price, calculates TVL, and returns structured result including inception-based APY, pricePerShare, and underlying token metadata.
Public API Export
src/adaptors/kintsu/index.js (lines 93–98)
Module exports remain apy and url; surface API unchanged but internal implementation now multi-chain and metrics-enriched.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A burrow of code, refactored with care,
From single-chain chains to structures most fair,
With helpers and metrics, inception so deep,
Share values and APY calculations we reap! 📊✨

🚥 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 'Update APY calculation for Kintsu' directly relates to the main change: refactoring APY calculations with new 7-day window and inception-based metrics for the Kintsu adaptor.
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

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

The kintsu adapter exports pools:

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

Nb of pools: 1
 

Sample pools:
┌─────────┬──────────────────────────────────────────────┬─────────┬──────────┬────────┬────────────────────┬───────────────────┬───────────────────┬────────────────────┬────────────────────┬──────────────────────────────────────────────────┬──────────────────────────────────────────────┬───────────────────┐
│ (index) │ pool                                         │ chain   │ project  │ symbol │ tvlUsd             │ apyBase           │ apyBase7d         │ apyBaseInception   │ pricePerShare      │ underlyingTokens                                 │ searchTokenOverride                          │ isIntrinsicSource │
├─────────┼──────────────────────────────────────────────┼─────────┼──────────┼────────┼────────────────────┼───────────────────┼───────────────────┼────────────────────┼────────────────────┼──────────────────────────────────────────────────┼──────────────────────────────────────────────┼───────────────────┤
│ 0       │ '0xa3227c5969757783154c60bf0bc1944180ed81b9' │ 'monad' │ 'kintsu' │ 'sMON' │ 3425345.9710319135 │ 14.40205608951144 │ 14.40205608951144 │ 13.940773528106586 │ 1.0628829668562976 │ [ '0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A' ] │ '0xA3227C5969757783154C60bF0bC1944180ed81B9' │ true              │
└─────────┴──────────────────────────────────────────────┴─────────┴──────────┴────────┴────────────────────┴───────────────────┴───────────────────┴────────────────────┴────────────────────┴──────────────────────────────────────────────────┴──────────────────────────────────────────────┴───────────────────┘

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/adaptors/kintsu/index.js (2)

92-92: 💤 Low value

Consider Promise.allSettled once more chains are added.

With a single chain today this is fine, but Promise.all rejects the entire adaptor if any one chain's RPC/price call fails. As soon as a second entry is added to chains, a transient failure on one chain will hide healthy data from the other. Promise.allSettled + filtering fulfilled results would isolate per-chain failures.

🤖 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/kintsu/index.js` at line 92, The apy function currently uses
Promise.all which will reject the whole adaptor if any chainApy promise fails;
change apy to use Promise.allSettled on Object.keys(chains).map(chainApy), then
filter the settled results for status === "fulfilled" and extract their values
(ignoring or logging rejected ones) so per-chain failures don't break successful
chain results; reference functions/idents: apy, chainApy, chains.

61-69: 💤 Low value

Redundant on-chain reads for totalPooled / totalSupply at blockNow.

getShareValue(chain, vault, blockNow) (line 65) already fetches totalPooled and totalSupply at blockNow, but you also issue a standalone totalPooled call on line 63. That doubles RPC traffic for the current-block reads. Consider returning totalPooled from getShareValue (or refactoring to fetch the raw values once and derive both svNow and tvlUsd from them), so each block is queried only once.

♻️ Sketch
-const getShareValue = async (chain, vault, block) => {
-  const [totalPooled, totalSupply] = await Promise.all([
-    call(chain, vault, 'function totalPooled() view returns (uint96)', block),
-    call(chain, vault, 'erc20:totalSupply', block),
-  ]);
-  if (BigInt(totalSupply) === 0n) {
-    throw new Error(`RPC issue: zero totalSupply at block ${block}`);
-  }
-  return (BigInt(totalPooled) * SCALE) / BigInt(totalSupply);
-};
+const getVaultState = async (chain, vault, block) => {
+  const [totalPooled, totalSupply] = await Promise.all([
+    call(chain, vault, 'function totalPooled() view returns (uint96)', block),
+    call(chain, vault, 'erc20:totalSupply', block),
+  ]);
+  if (BigInt(totalSupply) === 0n) {
+    throw new Error(`RPC issue: zero totalSupply at block ${block}`);
+  }
+  const shareValue = (BigInt(totalPooled) * SCALE) / BigInt(totalSupply);
+  return { totalPooled, totalSupply, shareValue };
+};

Then reuse totalPooled from the blockNow result instead of calling it again on line 63.

🤖 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/kintsu/index.js` around lines 61 - 69, The code is making a
redundant on-chain read: you call call(..., 'function totalPooled()') to
populate totalPooledNow while getShareValue(chain, vault, blockNow) already
returns totalPooled/totalSupply; update getShareValue to include/return the raw
totalPooled (and totalSupply) for the requested block and then change the
Promise.all usage to remove the separate call(...) for totalPooledNow and
instead extract totalPooledNow from the svNow result (or from the new
getShareValue return shape), ensuring all places that used
totalPooledNow/totalSupply now read those values from the svNow object and
eliminating the duplicate RPC.
🤖 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/kintsu/index.js`:
- Around line 25-28: The getUsdPrice function currently assumes
r.data.coins[priceId] exists and will throw a vague TypeError when it's missing;
update getUsdPrice to validate the response after the axios.get call (inspect
r.data and r.data.coins[priceId]) and throw or reject with a clear, actionable
Error (including the requested priceId and whether coins or the specific entry
was missing) so callers can log/handle an explicit failure instead of a runtime
TypeError.

---

Nitpick comments:
In `@src/adaptors/kintsu/index.js`:
- Line 92: The apy function currently uses Promise.all which will reject the
whole adaptor if any chainApy promise fails; change apy to use
Promise.allSettled on Object.keys(chains).map(chainApy), then filter the settled
results for status === "fulfilled" and extract their values (ignoring or logging
rejected ones) so per-chain failures don't break successful chain results;
reference functions/idents: apy, chainApy, chains.
- Around line 61-69: The code is making a redundant on-chain read: you call
call(..., 'function totalPooled()') to populate totalPooledNow while
getShareValue(chain, vault, blockNow) already returns totalPooled/totalSupply;
update getShareValue to include/return the raw totalPooled (and totalSupply) for
the requested block and then change the Promise.all usage to remove the separate
call(...) for totalPooledNow and instead extract totalPooledNow from the svNow
result (or from the new getShareValue return shape), ensuring all places that
used totalPooledNow/totalSupply now read those values from the svNow object and
eliminating the duplicate RPC.
🪄 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: 370df34e-3e2a-43db-b15a-e04188745149

📥 Commits

Reviewing files that changed from the base of the PR and between 6c0cba5 and aa3235e.

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

Comment on lines +25 to +28
const getUsdPrice = (priceId) =>
axios
.get(`https://coins.llama.fi/prices/current/${priceId}`)
.then((r) => r.data.coins[priceId].price);
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against missing price in getUsdPrice response.

If the coin is unknown to coins.llama.fi (typo, new chain not yet indexed, transient lookup miss), r.data.coins[priceId] will be undefined and .price will throw a TypeError with a non-actionable message. Validate the response and surface a clear error so adaptor failures are easier to diagnose.

🛡️ Proposed fix
-const getUsdPrice = (priceId) =>
-  axios
-    .get(`https://coins.llama.fi/prices/current/${priceId}`)
-    .then((r) => r.data.coins[priceId].price);
+const getUsdPrice = (priceId) =>
+  axios
+    .get(`https://coins.llama.fi/prices/current/${priceId}`)
+    .then((r) => {
+      const coin = r.data?.coins?.[priceId];
+      if (!coin || typeof coin.price !== 'number') {
+        throw new Error(`Price not available for ${priceId}`);
+      }
+      return coin.price;
+    });
🤖 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/kintsu/index.js` around lines 25 - 28, The getUsdPrice function
currently assumes r.data.coins[priceId] exists and will throw a vague TypeError
when it's missing; update getUsdPrice to validate the response after the
axios.get call (inspect r.data and r.data.coins[priceId]) and throw or reject
with a clear, actionable Error (including the requested priceId and whether
coins or the specific entry was missing) so callers can log/handle an explicit
failure instead of a runtime TypeError.

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