feat: Add Lets Get HAI fees adapter#7875
Conversation
Summary by CodeRabbit
WalkthroughAdds a new fee adapter ChangesLets Get HAI Fee Adapter
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 10 | ❌ 5❌ Failed checks (5 warnings)
✅ Passed checks (10 passed)
✨ Finishing Touches✨ Simplify 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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
fees/lets-get-hai.ts
| const SAFE_ENGINE = "0x9Ff826860689483181C5FAc9628fd2F70275A700"; | ||
| const TAX_COLLECTOR = "0x62B82ccE08f8F2D808348409E9418c65EB1973C3"; | ||
| const LIQUIDATION_ENGINE = "0x8Be588895BE9B75F9a9dAee185e0c2ad89891b56"; | ||
| const COLLATERAL_JOIN_FACTORY = "0xfE7987b1Ee45a8d592B15e8E924d50BFC8536143"; | ||
| const HAI = "0x10398AbC267496E49106B07dd6BE13364D10dC71"; | ||
| const ADDRESS_ZERO = "0x0000000000000000000000000000000000000000"; |
There was a problem hiding this comment.
📐 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
| 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; |
There was a problem hiding this comment.
🗄️ 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
| dailyFees.add(HAI, fee, METRIC.BORROW_INTEREST); | ||
| dailyRevenue.add(HAI, fee, METRIC.BORROW_INTEREST); |
There was a problem hiding this comment.
🗄️ 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.tsRepository: 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:
- 1: https://docs.letsgethai.com/faq
- 2: https://docs.letsgethai.com/index.html
- 3: https://www.lbank.com/price/let-s-get-hai
- 4: https://medium.com/hai-finance/hai-protocol-a-simple-overview-319fe53a8a0e
- 5: https://everythingblockchain.medium.com/hai-times-rolling-with-stability-fce3d5fb05d2
- 6: https://alearesearch.io/reports/deep-dives/hai
- 7: https://docs.gokite.ai/get-started-why-kite/tokenomics
- 8: https://kite.foundation/tokenomics
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
| const raisedWad = BigInt(log.args._raisedAmount); | ||
| const raisedRad = raisedWad * RAY; | ||
| const before = paidByAuction.get(akey) || 0n; |
There was a problem hiding this comment.
🗄️ 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.
| 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); })); |
There was a problem hiding this comment.
🚀 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 -C2Repository: 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
| const adapter: SimpleAdapter = { | ||
| version: 2, | ||
| // pullHourly: true, | ||
| fetch, |
There was a problem hiding this comment.
🚀 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.
| 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
|
The lets-get-hai.ts adapter exports: |
Summary