Skip to content

feat: Add Lets Get HAI fees adapter#7875

Draft
uwezukwechibuzor wants to merge 1 commit into
DefiLlama:masterfrom
uwezukwechibuzor:lets-get-hai
Draft

feat: Add Lets Get HAI fees adapter#7875
uwezukwechibuzor wants to merge 1 commit into
DefiLlama:masterfrom
uwezukwechibuzor:lets-get-hai

Conversation

@uwezukwechibuzor

Copy link
Copy Markdown
Contributor

Summary

  • WIP

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Summary by CodeRabbit

  • New Features
    • Added support for the Optimism “Lets Get HAI” CDP, with daily fee and revenue tracking.
    • Improved reporting for liquidation activity, including realized penalties and liquidation yield/discounts.
    • Expanded historical metric coverage with a detailed breakdown of fee and revenue components.

Walkthrough

Adds a new fee adapter fees/lets-get-hai.ts for the Lets Get HAI multi-collateral CDP protocol on Optimism. The adapter computes daily stability fees and liquidation penalties/yield by querying on-chain TaxCollector, SAFEEngine, and auction house contracts, then exports a SimpleAdapter configuration.

Changes

Lets Get HAI Fee Adapter

Layer / File(s) Summary
Constants, ABIs, and adapter config
fees/lets-get-hai.ts
Defines contract addresses, RAY/WAD units, key helpers, ABI entries for collateral listing, tax outcome, SAFEEngine cData, liquidation params, and BuyCollateral/Liquidate events; exports SimpleAdapter with Optimism chain, start date, and methodology strings.
Core fetch implementation
fees/lets-get-hai.ts
Implements fetch: queries stability/tax accrual per collateral type for dailyFees/dailyRevenue, derives per-auction liquidation penalties and debt from Liquidate logs, processes BuyCollateral logs to realize penalties from cumulative proceeds, and computes liquidation yield via timestamped getPrices calls (collateral value − HAI paid) credited to dailySupplySideRevenue.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

new-adapter, methodology

Suggested reviewers

  • bheluga
🚥 Pre-merge checks | ✅ 10 | ❌ 5

❌ Failed checks (5 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is related to the change, but it does not follow the required format '[type] protocol-name - description'. Rewrite the title to match the required pattern, for example: '[feat] Lets Get HAI - add fees adapter'.
Description check ⚠️ Warning The description is just 'WIP' and omits the template's required protocol listing and methodology details. Fill in the template sections or replace them with a clear implementation summary, including name, chain, links, and methodology.
Income Statement Compliance ⚠️ Warning �]8;;�\ _raisedAmount is multiplied by RAY even though it is already rad, so liquidation yield/cost-of-funds is miscomputed and gross profit isn’t a true net of fees. Use _raisedAmount as rad (no extra RAY multiplication) and recompute liquidation penalty/discount so dailySupplySideRevenue properly offsets dailyFees.
Pullhourly Required For Version 2 ⚠️ Warning fees/lets-get-hai.ts is version 2 but only has // pullHourly: true commented out, so it does not explicitly set the required key. Uncomment and set pullHourly: true (or false with an inline reason if hourly pulls are impossible for this adapter).
Efficiency And Error Handling ⚠️ Warning Hardcoded addresses and START_BLOCK lack source comments, and several multiCalls use permitFailure with silent continues on missing data. Add source comments/links for each hardcoded constant and START_BLOCK, and stop swallowing failed collateral reads—log or rethrow instead of continuing.
✅ Passed checks (10 passed)
Check name Status Explanation
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.
Breakdown Methodology Check ✅ Passed All .add labels (BORROW_INTEREST, LIQUIDATION_FEES, LIQUIDATION_YIELD) appear in breakdownMethodology, and the adapter exports that object.
Version 2 Required ✅ Passed fees/lets-get-hai.ts is a new adapter and explicitly exports version: 2 at line 143; it’s an EVM/log-based adapter, not a daily-aggregate API case.
Dune Adapters Are Version 1 ✅ Passed lets-get-hai.ts uses no Dune helpers; it’s a non-Dune v2 on-chain adapter, so the version-1 rule doesn’t apply.
Income Statement Balance ✅ Passed The adapter adds liquidation yield to dailySupplySideRevenue and keeps stability fees/liquidation penalties in dailyRevenue, so dailyFees balances as dailyRevenue + dailySupplySideRevenue.
Fetchoptions Usage ✅ Passed fetch(options) is used, it relies on getFromBlock/getToBlock, and the result returns no timestamp field or startOfDay-based windowing.
Adapter Shape ✅ Passed It uses the simplest shape: a single-chain SimpleAdapter with chains:[CHAIN.OPTIMISM] and one shared start, with no per-chain adapter/config map.
Methodology Keys ✅ Passed Methodology uses display-name keys (Fees, Revenue, ProtocolRevenue, SupplySideRevenue), not code names like dailyFees.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

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.

@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: 6

🤖 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 `@fees/lets-get-hai.ts`:
- Around line 98-100: The `_raisedAmount` value in `lets-get-hai.ts` is already
in rad, so the extra `RAY` conversion is causing inflated payment and discount
calculations. Update the `BuyCollateral` handling to treat
`log.args._raisedAmount` as rad directly in the `raisedWad`/`raisedRad` flow,
and then ensure the downstream uses in the `paidByAuction` map,
liquidation-penalty check, and `discountUSD` math all consume the unscaled
amount consistently.
- Around line 9-14: The hardcoded deployment addresses in lets-get-hai.ts need
verifier-friendly source comments, and START_BLOCK should be documented as well.
Add concise comments next to SAFE_ENGINE, TAX_COLLECTOR, LIQUIDATION_ENGINE,
COLLATERAL_JOIN_FACTORY, HAI, ADDRESS_ZERO, and START_BLOCK that cite the
deployment source or explorer/docs link and explain why each value is correct.
Use the existing constants in lets-get-hai.ts as the anchor points so
maintainers can verify these addresses and the first block later.
- Around line 46-54: The fee calculation path in lets-get-hai is silently
skipping failed collateral reads because the multiCall calls use permitFailure:
true and the loop in the fee aggregation logic continues without any signal.
Update the get-fees flow around the multiCall results and the cTypes iteration
to either throw on core TAX_COLLECTOR, SAFE_ENGINE, LIQUIDATION_ENGINE, or
COLLATERAL_JOIN_FACTORY read failures, or at minimum log the failing cType
before returning partial data so failures are never swallowed silently.
- Around line 142-145: The v2 adapter in `lets-get-hai.ts` is missing an
explicit `pullHourly` setting because it is currently commented out. Update the
`adapter` object in `SimpleAdapter` to set `pullHourly: true` directly alongside
`version: 2` and `fetch` so `pullHourly` is explicitly enabled for this adapter.
- Line 124: The historical price fetch in the uniqueTs loop is unbounded because
Promise.all fires one getPrices() request per timestamp at once. Replace this in
lets-get-hai.ts with the repo’s PromisePool concurrency pattern for non-EVM
calls, keeping the same pricesByTs assignment while limiting parallel requests
and preserving the existing key(HAI) plus collateralKeys lookup.
- Around line 59-60: `dailyRevenue` is currently capturing the full
stability-fee amount, so the KITE-holder share never gets split out and
`dailyProtocolRevenue` stays equal to total revenue. Update the logic in
`lets-get-hai.ts` around `dailyFees.add`, `dailyRevenue.add`, and the
`dailyProtocolRevenue`/`dailySupplySideRevenue` calculations so the
stability-fee revenue is divided into the protocol’s share and the KITE-holder
distribution before building the statement. Use the existing
`dailySupplySideRevenue` handling as the place to account for the holder share,
and keep the `dailyRevenue` total consistent with the new split.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 39fcad1b-2877-41fd-afbd-f84e8898b077

📥 Commits

Reviewing files that changed from the base of the PR and between 0bf4225 and 3c90c3e.

📒 Files selected for processing (1)
  • fees/lets-get-hai.ts

Comment thread fees/lets-get-hai.ts
Comment on lines +9 to +14
const SAFE_ENGINE = "0x9Ff826860689483181C5FAc9628fd2F70275A700";
const TAX_COLLECTOR = "0x62B82ccE08f8F2D808348409E9418c65EB1973C3";
const LIQUIDATION_ENGINE = "0x8Be588895BE9B75F9a9dAee185e0c2ad89891b56";
const COLLATERAL_JOIN_FACTORY = "0xfE7987b1Ee45a8d592B15e8E924d50BFC8536143";
const HAI = "0x10398AbC267496E49106B07dd6BE13364D10dC71";
const ADDRESS_ZERO = "0x0000000000000000000000000000000000000000";

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add source comments for deployment addresses and START_BLOCK.

These hardcoded addresses and the magic start block need verifier-friendly source comments, ideally deployment docs/explorer links and why Line 29 is the correct first block. As per coding guidelines, “Add a comment (and a source link where possible) for any hardcoded rate, address, or magic number so maintainers can verify it later.”

Also applies to: 29-29

🤖 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 `@fees/lets-get-hai.ts` around lines 9 - 14, The hardcoded deployment addresses
in lets-get-hai.ts need verifier-friendly source comments, and START_BLOCK
should be documented as well. Add concise comments next to SAFE_ENGINE,
TAX_COLLECTOR, LIQUIDATION_ENGINE, COLLATERAL_JOIN_FACTORY, HAI, ADDRESS_ZERO,
and START_BLOCK that cite the deployment source or explorer/docs link and
explain why each value is correct. Use the existing constants in lets-get-hai.ts
as the anchor points so maintainers can verify these addresses and the first
block later.

Source: Coding guidelines

Comment thread fees/lets-get-hai.ts
Comment on lines +46 to +54
fromApi.multiCall({ target: TAX_COLLECTOR, abi: ABI.taxSingleOutcome, calls: cTypes, permitFailure: true }),
toApi.multiCall({ target: TAX_COLLECTOR, abi: ABI.taxSingleOutcome, calls: cTypes, permitFailure: true }),
toApi.multiCall({ target: SAFE_ENGINE, abi: ABI.cData, calls: cTypes, permitFailure: true }),
toApi.multiCall({ target: LIQUIDATION_ENGINE, abi: ABI.liqCParams, calls: cTypes, permitFailure: true }),
toApi.multiCall({ target: COLLATERAL_JOIN_FACTORY, abi: ABI.collateralJoins, calls: cTypes, permitFailure: true }),
]);

for (let i = 0; i < cTypes.length; i++) {
if (!startOutcome[i] || !endOutcome[i] || !debtData[i]) continue;

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Do not silently skip failed collateral reads.

permitFailure: true plus continue underreports fees/liquidations with no signal. Since this is a single-chain adapter, core tax/debt/liquidation config failures should throw, or at minimum log the cType before returning partial data. As per coding guidelines, “Never swallow errors silently.”

Also applies to: 69-77

🤖 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 `@fees/lets-get-hai.ts` around lines 46 - 54, The fee calculation path in
lets-get-hai is silently skipping failed collateral reads because the multiCall
calls use permitFailure: true and the loop in the fee aggregation logic
continues without any signal. Update the get-fees flow around the multiCall
results and the cTypes iteration to either throw on core TAX_COLLECTOR,
SAFE_ENGINE, LIQUIDATION_ENGINE, or COLLATERAL_JOIN_FACTORY read failures, or at
minimum log the failing cType before returning partial data so failures are
never swallowed silently.

Source: Coding guidelines

Comment thread fees/lets-get-hai.ts
Comment on lines +59 to +60
dailyFees.add(HAI, fee, METRIC.BORROW_INTEREST);
dailyRevenue.add(HAI, fee, METRIC.BORROW_INTEREST);

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.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline fees/lets-get-hai.ts --view expanded
printf '\n--- FILE SIZES ---\n'
wc -l fees/lets-get-hai.ts
printf '\n--- TARGETED READS ---\n'
sed -n '1,240p' fees/lets-get-hai.ts

Repository: DefiLlama/dimension-adapters

Length of output: 10775


🌐 Web query:

Lets Get HAI KITE staking stability fees liquidation profits distribution protocol fees stakers share docs.letsgethai.com

💡 Result:

In the HAI protocol, the KITE token serves as the governance token, and staking it allows users to earn a share of protocol revenue and boost specific incentives [1][2][3]. Protocol Fee Distribution Stability fees collected from vault owners in HAI are primarily directed toward covering the protocol's operational costs (such as oracles and keepers) and building a surplus buffer to protect against bad debt [4][5]. Once this surplus buffer is sufficiently filled, excess HAI can be auctioned in exchange for KITE, which is then burned to reduce the circulating supply [4][5]. Additionally, a portion of the protocol's stability fee revenue is allocated as a share to KITE stakers [6]. Liquidation Profits and Mechanics The HAI protocol manages liquidations through a Stability Pool and various auction mechanisms [1][4][5]. 1. Stability Pool: Users deposit HAI into the Stability Pool to help the protocol automatically liquidate undercollateralized vaults [1][3]. In return, these depositors receive liquidated collateral at a discount and may earn additional incentives [1][3]. 2. Auction Houses: If positions cannot be cleared through the Stability Pool, the protocol utilizes collateral auctions to sell seized assets for HAI to cover debt [4][5]. In extreme cases where system debt exceeds recoverable amounts, the protocol may conduct debt auctions, where newly minted KITE is sold for HAI [4][5]. KITE Staking Benefits Staking KITE provides several key advantages to users: - Revenue Sharing: Stakers earn a share of protocol fees, typically paid in HAI [1][3][6]. - Boosted Incentives: Staking boosts minting incentives for certain collateral types and increases rewards for haiVELO deposits [1][6]. Note: The HAI protocol (docs.letsgethai.com) should not be confused with the unrelated Kite AI network (gokite.ai), which has its own distinct tokenomics [7][8]. Ensure you are referencing the correct documentation when managing assets.

Citations:


Split the KITE-holder share out of dailyRevenue (fees/lets-get-hai.ts:59-60, 149-153). The stability-pool discount is already modeled in dailySupplySideRevenue, but the docs also describe KITE stakers receiving a share of stability-fee revenue. dailyProtocolRevenue is currently set equal to dailyRevenue, so that holder distribution never appears in the statement.

🤖 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 `@fees/lets-get-hai.ts` around lines 59 - 60, `dailyRevenue` is currently
capturing the full stability-fee amount, so the KITE-holder share never gets
split out and `dailyProtocolRevenue` stays equal to total revenue. Update the
logic in `lets-get-hai.ts` around `dailyFees.add`, `dailyRevenue.add`, and the
`dailyProtocolRevenue`/`dailySupplySideRevenue` calculations so the
stability-fee revenue is divided into the protocol’s share and the KITE-holder
distribution before building the statement. Use the existing
`dailySupplySideRevenue` handling as the place to account for the holder share,
and keep the `dailyRevenue` total consistent with the new split.

Source: Path instructions

Comment thread fees/lets-get-hai.ts
Comment on lines +98 to +100
const raisedWad = BigInt(log.args._raisedAmount);
const raisedRad = raisedWad * RAY;
const before = paidByAuction.get(akey) || 0n;

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.

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Fix _raisedAmount units; it is already rad.

BuyCollateral._raisedAmount is documented as “Amount of coins raised in the bid [rad]”; multiplying it by RAY makes paidByAuction, liquidation-penalty detection, and discountUSD overstate HAI paid by 1e27. (optimistic.etherscan.io)

🐛 Proposed fix
-    const raisedWad = BigInt(log.args._raisedAmount);
-    const raisedRad = raisedWad * RAY;
+    const raisedRad = BigInt(log.args._raisedAmount);
+    const raisedWad = raisedRad / RAY;

Also applies to: 117-132

🤖 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 `@fees/lets-get-hai.ts` around lines 98 - 100, The `_raisedAmount` value in
`lets-get-hai.ts` is already in rad, so the extra `RAY` conversion is causing
inflated payment and discount calculations. Update the `BuyCollateral` handling
to treat `log.args._raisedAmount` as rad directly in the `raisedWad`/`raisedRad`
flow, and then ensure the downstream uses in the `paidByAuction` map,
liquidation-penalty check, and `discountUSD` math all consume the unscaled
amount consistently.

Comment thread fees/lets-get-hai.ts
const collateralKeys = [...new Set(buys.map((b) => b.token).filter(Boolean))].map((t) => key(t!));
const uniqueTs = [...new Set(buys.map((b) => b.ts))];
const pricesByTs: Record<number, any> = {};
await Promise.all(uniqueTs.map(async (ts) => { pricesByTs[ts] = await getPrices([key(HAI), ...collateralKeys], ts); }));

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.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the existing PromisePool import/pattern used in this repository.
rg -n "PromisePool" --type=ts -C2

Repository: DefiLlama/dimension-adapters

Length of output: 166


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the adapter around the flagged line.
wc -l fees/lets-get-hai.ts
sed -n '1,220p' fees/lets-get-hai.ts

# Look for any concurrency helper patterns used in the repo.
rg -n "PromisePool|p-limit|limit\(|concurrency" --type=ts .

Repository: DefiLlama/dimension-adapters

Length of output: 23558


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the price helper and its call pattern.
wc -l utils/prices.ts
sed -n '1,260p' utils/prices.ts

# Show a few existing bounded-concurrency patterns around non-EVM calls.
rg -n "PromisePool\.withConcurrency|withConcurrency\(" fees dexs open-interest helpers --type=ts -C2 | sed -n '1,220p'

Repository: DefiLlama/dimension-adapters

Length of output: 14625


Bound the historical price fetches. getPrices() still makes one HTTP request per unique timestamp, so Promise.all can spike request volume on liquidation-heavy days. Switch this loop to the repo’s PromisePool concurrency pattern for non-EVM calls.

🤖 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 `@fees/lets-get-hai.ts` at line 124, The historical price fetch in the uniqueTs
loop is unbounded because Promise.all fires one getPrices() request per
timestamp at once. Replace this in lets-get-hai.ts with the repo’s PromisePool
concurrency pattern for non-EVM calls, keeping the same pricesByTs assignment
while limiting parallel requests and preserving the existing key(HAI) plus
collateralKeys lookup.

Source: Coding guidelines

Comment thread fees/lets-get-hai.ts
Comment on lines +142 to +145
const adapter: SimpleAdapter = {
version: 2,
// pullHourly: true,
fetch,

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.

🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Set pullHourly: true explicitly.

This v2 adapter uses EVM logs/on-chain calls, but pullHourly is commented out. Enable it so hourly runs produce granular data and avoid recomputing full-day windows. As per coding guidelines, “Every version: 2 adapter must explicitly set the pullHourly key.”

⚙️ Proposed fix
 const adapter: SimpleAdapter = {
   version: 2,
-  // pullHourly: true,
+  pullHourly: true,
   fetch,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const adapter: SimpleAdapter = {
version: 2,
// pullHourly: true,
fetch,
const adapter: SimpleAdapter = {
version: 2,
pullHourly: true,
fetch,
🤖 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 `@fees/lets-get-hai.ts` around lines 142 - 145, The v2 adapter in
`lets-get-hai.ts` is missing an explicit `pullHourly` setting because it is
currently commented out. Update the `adapter` object in `SimpleAdapter` to set
`pullHourly: true` directly alongside `version: 2` and `fetch` so `pullHourly`
is explicitly enabled for this adapter.

Source: Coding guidelines

@github-actions

Copy link
Copy Markdown

The lets-get-hai.ts adapter exports:

> adapters@1.0.0 test
> ts-node --transpile-only cli/testAdapter.ts fees lets-get-hai.ts

🦙 Running LETS-GET-HAI.TS adapter 🦙
---------------------------------------------------
Start Date:	Sun, 28 Jun 2026 07:55:03 GMT
End Date:	Mon, 29 Jun 2026 07:55:03 GMT
---------------------------------------------------

OPTIMISM 👇
Backfill start time: 20/2/2024
Daily fees: 39.00
Daily revenue: 39.00
Daily protocol revenue: 39.00
Daily supply side revenue: 0.00
End timestamp: 1782719702 (2026-06-29T07:55:02.000Z)




FEES BREAKDOWN 👇

label           | Daily fees | Daily revenue
---             | ---        | ---          
Borrow Interest | 39         | 39           

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant