Skip to content

Add Apyx Protocol yield adapter#2544

Open
dead-pool-aka-wilson wants to merge 3 commits intoDefiLlama:masterfrom
dead-pool-aka-wilson:add-apyx-yield-adapter
Open

Add Apyx Protocol yield adapter#2544
dead-pool-aka-wilson wants to merge 3 commits intoDefiLlama:masterfrom
dead-pool-aka-wilson:add-apyx-yield-adapter

Conversation

@dead-pool-aka-wilson
Copy link
Copy Markdown

@dead-pool-aka-wilson dead-pool-aka-wilson commented Apr 2, 2026

Summary

Adding yield tracking for Apyx Protocol's apyUSD vault.

apyUSD is an ERC-4626 vault where users deposit apxUSD and earn yield on the underlying collateral backing apxUSD. Yield is vested linearly over a configurable period.

Pool Details

  • Vault: 0x38EEb52F0771140d10c4E9A9a72349A329Fe8a6A (apyUSD)
  • Underlying: 0x98A878b1Cd98131B271883B390f68D2c90674665 (apxUSD)
  • Chain: Ethereum
  • APY Source: On-chain ApyUSDRateView contract at 0xCABa36EDE2C08e16F3602e8688a8bE94c1B4e484
  • Yield Source: Preferred share dividends from digital asset treasury companies

Changes from prior PR #2428

  • Added token field (recommended, becoming required)
  • Slug apyx-protocol aligns with TVL adapter PR (DefiLlama-Adapters #18617)

Links

Note: This supersedes PR #2428 (by @ddaws), which has been unresponsive since Feb 26. This version adds the token field and aligns with the resubmitted TVL adapter.

Summary by CodeRabbit

  • New Features
    • Added APYX Protocol integration: provides a single vault pool with live APY and TVL calculated from current token price, includes chain/project/symbol metadata, pool metadata ("apyUSD vault"), and a direct link to https://apyx.fi.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

Added a new adaptor at src/adaptors/apyx-protocol/index.js that disables timetravel, sets url to https://apyx.fi, and exports an async apy() which concurrently queries totalAssets() (vault), apy() (rate view), and APXUSD token price, then computes and returns a single pool descriptor with tvlUsd and apyBase.

Changes

Cohort / File(s) Summary
APYX Protocol Adaptor
src/adaptors/apyx-protocol/index.js
Adds new adaptor exporting timetravel: false, url: 'https://apyx.fi', and an async apy() that concurrently calls totalAssets() (APYUSD_VAULT) and apy() (RATE_VIEW), fetches APXUSD_TOKEN price, validates price, computes tvlUsd (18-decimal -> USD) and apyBase (raw * 1e16), and returns one pool descriptor with token/underlying set to APXUSD_TOKEN, poolMeta: 'apyUSD vault', and deterministic lowercased pool address.

Sequence Diagram(s)

sequenceDiagram
  participant Adapter as apyx-protocol (index.js)
  participant Vault as APYUSD_VAULT (contract)
  participant RateView as RATE_VIEW (contract)
  participant PriceSvc as PriceAPI (utils.getPrices)
  participant Return as PoolArray

  Adapter->>Vault: call totalAssets()
  Adapter->>RateView: call apy()
  Adapter->>PriceSvc: fetch APXUSD token price
  Vault-->>Adapter: totalAssets (18-decimals)
  RateView-->>Adapter: raw apy value
  PriceSvc-->>Adapter: token price (USD)
  Adapter->>Adapter: validate price
  Adapter->>Adapter: compute tvlUsd = totalAssets * price
  Adapter->>Adapter: compute apyBase = rawApy * 1e16
  Adapter-->>Return: return [ { pool descriptor with tvlUsd, apyBase, metadata, url } ]
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 New code hops into the light,
Vaults and rates all readjusted right,
I fetched the price and crunched the sums,
A single pool now proudly hums,
Hop, adapt, and blossom bright!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add Apyx Protocol yield adapter' accurately and concisely describes the main change: adding a new yield adapter for the Apyx Protocol, which aligns with the changeset of creating a new adaptor file.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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.

@llamatester
Copy link
Copy Markdown

Error while running apyx-protocol adapter:

Test Suites: 1 failed, 1 total
Tests: 1 failed, 10 passed, 11 total
Snapshots: 0 total
Time: 0.27 s
Ran all test suites.

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬─────────────────┬──────────┬───────────────────┬──────────────────┬──────────────────────────────────────────────┬──────────────────────────────────────────────────┬────────────────┬───────────────────┐
│ (index) │ pool                                                  │ chain      │ project         │ symbol   │ tvlUsd            │ apyBase          │ token                                        │ underlyingTokens                                 │ poolMeta       │ url               │
├─────────┼───────────────────────────────────────────────────────┼────────────┼─────────────────┼──────────┼───────────────────┼──────────────────┼──────────────────────────────────────────────┼──────────────────────────────────────────────────┼────────────────┼───────────────────┤
│ 0       │ '0x38eeb52f0771140d10c4e9a9a72349a329fe8a6a-ethereum' │ 'Ethereum' │ 'apyx-protocol' │ 'apxUSD' │ 40019757.76087803 │ 9.22366317241189 │ '0x98A878b1Cd98131B271883B390f68D2c90674665' │ [ '0x98A878b1Cd98131B271883B390f68D2c90674665' ] │ 'apyUSD vault' │ 'https://apyx.fi' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴─────────────────┴──────────┴───────────────────┴──────────────────┴──────────────────────────────────────────────┴──────────────────────────────────────────────────┴────────────────┴───────────────────┘

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.

🧹 Nitpick comments (2)
src/adaptors/apyx-protocol/index.js (2)

9-23: Parallelize independent SDK calls for better performance.

The two contract calls are independent and can be executed concurrently using Promise.all.

♻️ Proposed refactor to parallelize calls
-  const totalAssets = (
-    await sdk.api.abi.call({
+  const [totalAssetsResult, apyResultData] = await Promise.all([
+    sdk.api.abi.call({
       target: APYUSD_VAULT,
       abi: 'function totalAssets() view returns (uint256)',
       chain: 'ethereum',
-    })
-  ).output;
-
-  const apyResult = (
-    await sdk.api.abi.call({
+    }),
+    sdk.api.abi.call({
       target: RATE_VIEW,
       abi: 'function apy() view returns (uint256)',
       chain: 'ethereum',
-    })
-  ).output;
+    }),
+  ]);
+
+  const totalAssets = totalAssetsResult.output;
+  const apyResult = apyResultData.output;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adaptors/apyx-protocol/index.js` around lines 9 - 23, The two independent
SDK calls to sdk.api.abi.call (targeting APYUSD_VAULT -> totalAssets and
RATE_VIEW -> apyResult) should be run concurrently to improve performance;
update the code to call both sdk.api.abi.call invocations inside a Promise.all
and then destructure the results into totalAssets and apyResult (preserving the
existing .output extraction) so behavior is unchanged but latency is reduced.

25-26: Add token price lookup for TVL calculation.

The TVL calculation divides by 1e18 without multiplying by apxUSD price. While the 18-decimal assumption is correct, apxUSD is a synthetic stablecoin backed by dividend-bearing preferred shares (RWA), designed to maintain ~$1 peg but not guaranteed. A depeg would result in inaccurate TVL reporting.

Other ERC-4626 vault adapters (avon-megavault, treehouse-protocol, falcon-finance) fetch and multiply by token price regardless of stablecoin status. Consider either:

  1. Adding price lookup to follow the safer pattern, or
  2. Documenting the ~1:1 peg assumption in a code comment explaining why price is omitted
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adaptors/apyx-protocol/index.js` around lines 25 - 26, The TVL
calculation uses tvlUsd = totalAssets / 1e18 but omits multiplying by the apxUSD
token price, which can misreport TVL if apxUSD depegs; update the adapter to
fetch the apxUSD/token price (reuse your existing price lookup utility or add a
getTokenPrice call) and compute tvlUsd = (totalAssets / 1e18) * tokenPrice;
ensure you reference the totalAssets and tvlUsd variables and preserve apyBase =
apyResult / 1e16, and add a brief comment explaining the price multiplication so
future readers know we intentionally include token price for safety.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/adaptors/apyx-protocol/index.js`:
- Around line 9-23: The two independent SDK calls to sdk.api.abi.call (targeting
APYUSD_VAULT -> totalAssets and RATE_VIEW -> apyResult) should be run
concurrently to improve performance; update the code to call both
sdk.api.abi.call invocations inside a Promise.all and then destructure the
results into totalAssets and apyResult (preserving the existing .output
extraction) so behavior is unchanged but latency is reduced.
- Around line 25-26: The TVL calculation uses tvlUsd = totalAssets / 1e18 but
omits multiplying by the apxUSD token price, which can misreport TVL if apxUSD
depegs; update the adapter to fetch the apxUSD/token price (reuse your existing
price lookup utility or add a getTokenPrice call) and compute tvlUsd =
(totalAssets / 1e18) * tokenPrice; ensure you reference the totalAssets and
tvlUsd variables and preserve apyBase = apyResult / 1e16, and add a brief
comment explaining the price multiplication so future readers know we
intentionally include token price for safety.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 40542071-303e-4acb-adc6-c15e8d83a412

📥 Commits

Reviewing files that changed from the base of the PR and between afa0b8c and ce930ea.

📒 Files selected for processing (1)
  • src/adaptors/apyx-protocol/index.js

@llamatester
Copy link
Copy Markdown

Error while running apyx-protocol adapter:

Test Suites: 1 failed, 1 total
Tests: 1 failed, 10 passed, 11 total
Snapshots: 0 total
Time: 0.279 s
Ran all test suites.

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬─────────────────┬──────────┬───────────────────┬───────────────────┬──────────────────────────────────────────────┬──────────────────────────────────────────────────┬────────────────┬───────────────────┐
│ (index) │ pool                                                  │ chain      │ project         │ symbol   │ tvlUsd            │ apyBase           │ token                                        │ underlyingTokens                                 │ poolMeta       │ url               │
├─────────┼───────────────────────────────────────────────────────┼────────────┼─────────────────┼──────────┼───────────────────┼───────────────────┼──────────────────────────────────────────────┼──────────────────────────────────────────────────┼────────────────┼───────────────────┤
│ 0       │ '0x38eeb52f0771140d10c4e9a9a72349a329fe8a6a-ethereum' │ 'Ethereum' │ 'apyx-protocol' │ 'apxUSD' │ 40062160.31173158 │ 9.223651526162076 │ '0x98A878b1Cd98131B271883B390f68D2c90674665' │ [ '0x98A878b1Cd98131B271883B390f68D2c90674665' ] │ 'apyUSD vault' │ 'https://apyx.fi' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴─────────────────┴──────────┴───────────────────┴───────────────────┴──────────────────────────────────────────────┴──────────────────────────────────────────────────┴────────────────┴───────────────────┘

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 (1)
src/adaptors/apyx-protocol/index.js (1)

8-21: Consider adding error handling for contract calls.

The concurrent contract calls could fail due to RPC errors or network issues. Adding try-catch or validation would make the adapter more robust.

🔧 Suggested error handling pattern
  const [totalAssetsRes, apyRes, { pricesByAddress }] = await Promise.all([
    sdk.api.abi.call({
      target: APYUSD_VAULT,
      abi: 'function totalAssets() view returns (uint256)',
      chain: 'ethereum',
    }),
    sdk.api.abi.call({
      target: RATE_VIEW,
      abi: 'function apy() view returns (uint256)',
      chain: 'ethereum',
    }),
    utils.getPrices([APXUSD_TOKEN], 'ethereum'),
  ]);

+ if (!totalAssetsRes?.output || !apyRes?.output) {
+   throw new Error('Failed to fetch contract data');
+ }
+
  const totalAssets = totalAssetsRes.output;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adaptors/apyx-protocol/index.js` around lines 8 - 21, Wrap the concurrent
calls inside the apy function (the await Promise.all that calls sdk.api.abi.call
for APYUSD_VAULT and RATE_VIEW and utils.getPrices for APXUSD_TOKEN) in a
try-catch, catch RPC/network errors from sdk.api.abi.call and utils.getPrices,
log or throw a clear error including the target/abi or token, and validate the
returned objects (e.g., ensure totalAssetsRes, apyRes and pricesByAddress exist
and have expected properties) before using them so the adapter fails gracefully
or returns a safe fallback instead of crashing on undefined fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/adaptors/apyx-protocol/index.js`:
- Around line 23-28: The code computes tvlUsd using tokenPrice from
pricesByAddress which can be undefined; add a guard before computing tvlUsd:
check tokenPrice (from pricesByAddress[APXUSD_TOKEN.toLowerCase()]) and if it's
undefined or not a finite number, handle it (e.g., log a warning and set tvlUsd
= 0 or skip/mark the pool as invalid) rather than letting (totalAssets / 1e18) *
tokenPrice produce NaN; update the block around totalAssets, apyResult,
tokenPrice and tvlUsd to validate tokenPrice and take the safe fallback path.

---

Nitpick comments:
In `@src/adaptors/apyx-protocol/index.js`:
- Around line 8-21: Wrap the concurrent calls inside the apy function (the await
Promise.all that calls sdk.api.abi.call for APYUSD_VAULT and RATE_VIEW and
utils.getPrices for APXUSD_TOKEN) in a try-catch, catch RPC/network errors from
sdk.api.abi.call and utils.getPrices, log or throw a clear error including the
target/abi or token, and validate the returned objects (e.g., ensure
totalAssetsRes, apyRes and pricesByAddress exist and have expected properties)
before using them so the adapter fails gracefully or returns a safe fallback
instead of crashing on undefined fields.
🪄 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: 9cdfef5e-f7bb-40d2-bd61-3968d7ecbcae

📥 Commits

Reviewing files that changed from the base of the PR and between ce930ea and c476546.

📒 Files selected for processing (1)
  • src/adaptors/apyx-protocol/index.js

@llamatester
Copy link
Copy Markdown

Error while running apyx-protocol adapter:

Test Suites: 1 failed, 1 total
Tests: 1 failed, 10 passed, 11 total
Snapshots: 0 total
Time: 0.276 s
Ran all test suites.

Nb of pools: 1
 

Sample pools:
┌─────────┬───────────────────────────────────────────────────────┬────────────┬─────────────────┬──────────┬───────────────────┬───────────────────┬──────────────────────────────────────────────┬──────────────────────────────────────────────────┬────────────────┬───────────────────┐
│ (index) │ pool                                                  │ chain      │ project         │ symbol   │ tvlUsd            │ apyBase           │ token                                        │ underlyingTokens                                 │ poolMeta       │ url               │
├─────────┼───────────────────────────────────────────────────────┼────────────┼─────────────────┼──────────┼───────────────────┼───────────────────┼──────────────────────────────────────────────┼──────────────────────────────────────────────────┼────────────────┼───────────────────┤
│ 0       │ '0x38eeb52f0771140d10c4e9a9a72349a329fe8a6a-ethereum' │ 'Ethereum' │ 'apyx-protocol' │ 'apxUSD' │ 40091937.24741567 │ 9.205335969496627 │ '0x98A878b1Cd98131B271883B390f68D2c90674665' │ [ '0x98A878b1Cd98131B271883B390f68D2c90674665' ] │ 'apyUSD vault' │ 'https://apyx.fi' │
└─────────┴───────────────────────────────────────────────────────┴────────────┴─────────────────┴──────────┴───────────────────┴───────────────────┴──────────────────────────────────────────────┴──────────────────────────────────────────────────┴────────────────┴───────────────────┘

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/adaptors/apyx-protocol/index.js`:
- Around line 26-28: The adapter currently throws an Error when tokenPrice is
not finite (the block checking Number.isFinite(tokenPrice)), which hard-fails
ingestion; change this to a resilient fallback by logging a warning (e.g., using
processLogger.warn or console.warn) that price is unavailable and
returning/continuing in a way that skips processing this pool for this run (for
example return null or undefined from the function that computes pool metrics or
skip adding the pool to results) instead of throwing; ensure all callers of that
function handle the null/undefined result so a transient pricing outage does not
crash the adapter.
🪄 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: 3c9f290c-0051-4c12-b28f-63d1cf75e7a3

📥 Commits

Reviewing files that changed from the base of the PR and between c476546 and 75fbeb0.

📒 Files selected for processing (1)
  • src/adaptors/apyx-protocol/index.js

Comment on lines +26 to +28
if (!Number.isFinite(tokenPrice)) {
throw new Error(`apxUSD price unavailable from coins.llama.fi`);
}
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 | 🟠 Major

Avoid hard-failing the adapter when token price is temporarily missing

At Line 27, throwing here makes the whole adapter fail on transient pricing outages. Prefer a safe fallback (skip pool for this run) so ingestion remains resilient.

💡 Proposed resilient fallback
   const tokenPrice = pricesByAddress[APXUSD_TOKEN.toLowerCase()];
   if (!Number.isFinite(tokenPrice)) {
-    throw new Error(`apxUSD price unavailable from coins.llama.fi`);
+    return [];
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/adaptors/apyx-protocol/index.js` around lines 26 - 28, The adapter
currently throws an Error when tokenPrice is not finite (the block checking
Number.isFinite(tokenPrice)), which hard-fails ingestion; change this to a
resilient fallback by logging a warning (e.g., using processLogger.warn or
console.warn) that price is unavailable and returning/continuing in a way that
skips processing this pool for this run (for example return null or undefined
from the function that computes pool metrics or skip adding the pool to results)
instead of throwing; ensure all callers of that function handle the
null/undefined result so a transient pricing outage does not crash the adapter.

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.

2 participants