-
Notifications
You must be signed in to change notification settings - Fork 49
feat: finish implementing affiliate revenue endpoint #1229
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
📝 WalkthroughWalkthroughReworks affiliate revenue aggregation: parallelizes provider fetches with Promise.allSettled, tracks per-provider failures, adds per-day ( Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller
participant Aggregator
participant Cache
participant ProviderA as Provider (e.g., bebop)
participant ProviderB as Provider (e.g., butterswap)
participant External as External APIs
Client->>Controller: GET /affiliate/revenue?startDate&endDate
Controller->>Aggregator: toTimestamps(startDate,endDate)
Aggregator->>Cache: splitDateRange & tryGetCachedFees (parallel per-date)
par Parallel provider fetches
Aggregator->>ProviderA: getFees(startTs,endTs)
Aggregator->>ProviderB: getFees(startTs,endTs)
and Providers->>Cache: per-date read/write (tryGetCachedFees/saveCachedFees)
Providers->>External: API / RPC / explorer calls
External-->>Providers: responses
Providers-->>Aggregator: resolved fees (or throw)
end
Aggregator->>Aggregator: Promise.allSettled(results) -> collect successes & failedProviders
Aggregator->>Aggregator: group byDate, compute byService and totalUsd
Aggregator-->>Controller: AffiliateRevenueResponse { totalUsd, byService, byDate, failedProviders }
Controller-->>Client: 200 OK (JSON)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
node/proxy/api/src/swagger.json (1)
131-145: Consider addingmaxItemsconstraint to the array.The
failedProvidersfield is correctly defined and marked as required. The array is naturally bounded by the number of services (7), but adding an explicitmaxItemsconstraint would make this clear in the API contract.Based on the static analysis hint, you could apply this diff:
"failedProviders": { "items": { "$ref": "#/components/schemas/Service" }, - "type": "array" + "type": "array", + "maxItems": 7 },
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
node/proxy/api/src/affiliateRevenue/index.ts(4 hunks)node/proxy/api/src/models.ts(1 hunks)node/proxy/api/src/swagger.json(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
node/proxy/api/src/affiliateRevenue/index.ts (1)
node/proxy/api/src/models.ts (1)
Service(6-6)
🪛 Checkov (3.2.334)
node/proxy/api/src/swagger.json
[medium] 131-135: Ensure that arrays have a maximum number of items
(CKV_OPENAPI_21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: check
🔇 Additional comments (4)
node/proxy/api/src/models.ts (1)
10-10: LGTM!The addition of
failedProvidersfield correctly tracks which services failed, enabling the UI to display appropriate indicators.node/proxy/api/src/affiliateRevenue/index.ts (2)
24-43: LGTM!The switch to
Promise.allSettledcorrectly implements graceful error handling. Failed providers are properly tracked and logged, while successful results are aggregated.
57-61: LGTM!The return object correctly includes
failedProviders, matching the updatedAffiliateRevenueResponseinterface.node/proxy/api/src/swagger.json (1)
114-125: LGTM!The
Serviceenum schema correctly reflects all defined services and aligns with the TypeScript type definition.
feat: add cacheing layer and make granularity daily
feat: finalise near intents and portals affiliate tracking
feat: add butterswap affiliate tracking
feat: add relay to affiliate revenue tracker
feat: finish implementation for near intents revenue tracking
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 13
♻️ Duplicate comments (1)
node/proxy/api/src/affiliateRevenue/index.ts (1)
14-24: Order-dependent mapping betweenproviderNamesandPromise.allSettledis fragile.The
providerNamesarray must be kept in sync with the order of promises inPromise.allSettled. If a developer adds or reorders providers, failures could be attributed to the wrong provider. This was flagged in a previous review.Consider using a provider registry pattern as suggested previously.
Also applies to: 54-64
🧹 Nitpick comments (23)
node/proxy/api/src/affiliateRevenue/mayachain/types.ts (1)
1-11: Type definition is correct, but duplicates thorchain's FeesResponse.This type is identical to
FeesResponseinnode/proxy/api/src/affiliateRevenue/thorchain/types.ts. Consider extracting a shared type to a common location to reduce duplication, unless the APIs are expected to diverge in the future.node/proxy/api/src/affiliateRevenue/zrx/zrx.ts (1)
22-29: Consider adding timeout and error handling for the API request.The axios request lacks a timeout configuration. Long-running or hanging requests could block the entire fetch operation. Given the PR's goal of graceful error handling, consider:
- Adding a request timeout
- Adding try-catch with meaningful error context
🔎 Proposed improvement
const { data } = await axios.get<TradesResponse>(`${ZRX_API_URL}/${service}`, { params: { cursor, startTimestamp, endTimestamp }, headers: { '0x-api-key': ZRX_API_KEY, '0x-version': 'v2', }, + timeout: 30000, // 30 second timeout })node/proxy/api/src/affiliateRevenue/thorchain/types.ts (1)
1-11: Consider extracting the duplicated type to a shared location.The
FeesResponsetype is identical to the one innode/proxy/api/src/affiliateRevenue/mayachain/types.ts. Consider extracting this shared type definition to a common location (e.g.,node/proxy/api/src/affiliateRevenue/types.tsornode/proxy/api/src/affiliateRevenue/common/types.ts) to follow the DRY principle and maintain consistency.🔎 Suggested refactor
Create a shared types file and import from both modules:
// In a shared location like node/proxy/api/src/affiliateRevenue/common/types.ts export type ChainFeesResponse = { fees: Array<{ address: string amount: string asset: string blockHash: string blockHeight: number timestamp: number txId: string }> }Then import and re-export or alias in both thorchain and mayachain modules.
node/proxy/api/src/affiliateRevenue/relay/constants.ts (1)
4-4: Consider centralizing shared constants.
SHAPESHIFT_REFERRERis duplicated in multiple files (relay and bebop constants). Consider moving shared constants like this to the mainaffiliateRevenue/constants.tsfile to maintain a single source of truth.node/proxy/api/src/affiliateRevenue/mayachain/constants.ts (1)
1-4: Consider centralizing duplicate constants.
PRICE_API_URLandMILLISECONDS_PER_SECONDare duplicated in both mayachain and thorchain constants files with identical values. Consider moving these shared constants to the mainaffiliateRevenue/constants.tsfile to reduce duplication and ensure consistency.node/proxy/api/src/affiliateRevenue/bebop/constants.ts (1)
6-8: Consider centralizing duplicate constants.Both
SHAPESHIFT_REFERRERandFEE_BPS_DENOMINATORare duplicated across multiple modules:
SHAPESHIFT_REFERRERalso in relay/constants.tsFEE_BPS_DENOMINATORalso in nearIntents and portals constantsConsider moving these to the main
affiliateRevenue/constants.tsfile to maintain a single source of truth.node/proxy/api/src/affiliateRevenue/nearIntents/constants.ts (2)
21-24: Top-level throw may crash application on startup.The guard at line 24 executes at module load time. If
NEAR_INTENTS_API_KEYis unset, the entire application will crash during startup, preventing other providers from functioning. Given the PR's goal of graceful error handling, consider deferring this check to runtime whengetFeesis called.🔎 Proposed fix
-export const NEAR_INTENTS_API_KEY = process.env.NEAR_INTENTS_API_KEY +export const NEAR_INTENTS_API_KEY = process.env.NEAR_INTENTS_API_KEY ?? '' export const FEE_BPS_DENOMINATOR = 10000 - -if (!NEAR_INTENTS_API_KEY) throw new Error('NEAR_INTENTS_API_KEY env var not set')Then validate in the
getFeesfunction:if (!NEAR_INTENTS_API_KEY) { throw new Error('NEAR_INTENTS_API_KEY env var not set') }
43-43: Hardcoded chain ID for monad.The
monadmapping uses a hardcoded value'eip155:143'while all other chains use imported constants. Consider adding aMONAD_CHAIN_IDconstant to../constantsfor consistency and maintainability.node/proxy/api/src/affiliateRevenue/relay/relay.ts (1)
22-30: Consider adding request timeout.The axios request has no timeout configured. If the Relay API becomes unresponsive, this could block indefinitely. Other network-related issues could also cause hanging requests.
🔎 Proposed fix
const { data } = await axios.get<RelayResponse>(`${RELAY_API_URL}/requests/v2`, { params: { referrer: SHAPESHIFT_REFERRER, startTimestamp, endTimestamp, status: 'success', continuation, }, + timeout: 30000, // 30 second timeout })node/proxy/api/src/affiliateRevenue/thorchain/thorchain.ts (1)
42-53: Price fetched per API call may cause inconsistent USD values.
getRunePriceUsd()is called insidefetchFeesFromAPI, which can be invoked multiple times (for cached date ranges and recent data). If the RUNE price changes between calls, fees from the same query will have inconsistent USD valuations.Consider fetching the price once in
getFeesand passing it tofetchFeesFromAPI.🔎 Proposed fix
-const fetchFeesFromAPI = async (startTimestamp: number, endTimestamp: number): Promise<Fees[]> => { +const fetchFeesFromAPI = async (startTimestamp: number, endTimestamp: number, runePriceUsd: number): Promise<Fees[]> => { const start = startTimestamp * MILLISECONDS_PER_SECOND const end = endTimestamp * MILLISECONDS_PER_SECOND const { data } = await axios.get<FeesResponse>(THORCHAIN_API_URL, { params: { start, end }, }) - const runePriceUsd = await getRunePriceUsd() - return data.fees.map(fee => transformFee(fee, runePriceUsd)) } export const getFees = async (startTimestamp: number, endTimestamp: number): Promise<Fees[]> => { + const runePriceUsd = await getRunePriceUsd() const threshold = getCacheableThreshold() // ... update calls to fetchFeesFromAPI to pass runePriceUsdnode/proxy/api/src/affiliateRevenue/portals/portals.ts (1)
322-326: Silent failure handling for per-chain fetching.Failed chain fetches are silently dropped without logging. This could make debugging difficult when a specific chain's data is missing. Consider adding logging for rejected results.
🔎 Proposed fix
for (const result of results) { if (result.status === 'fulfilled') { allFees.push(...result.value) + } else { + console.error(`[portals] Chain fetch failed:`, result.reason) } }node/proxy/api/src/affiliateRevenue/chainflip/chainflip.ts (1)
38-53: Consider usingswapRequestNativeIdas the transaction identifier.The
txHashis set to an empty string, but the GraphQL response includesswapRequestNativeIdwhich could serve as a unique identifier for each swap. This would improve traceability and debugging.🔎 Proposed fix
fees.push({ chainId, assetId, service: 'chainflip', - txHash: '', + txHash: swap.swapRequestNativeId, timestamp: Math.floor(new Date(swap.completedBlockTimestamp).getTime() / 1000), amount: '0', amountUsd: swap.affiliateBroker1FeeValueUsd, })node/proxy/api/src/affiliateRevenue/butterswap/butterswap.ts (1)
98-99: Potential precision loss with large fee values.Converting
BigInttoNumberviaNumber(feesPerDay)can lose precision for values exceedingNumber.MAX_SAFE_INTEGER(~9 quadrillion). For USDT with 6 decimals, this threshold is ~9 billion USD per day, which is unlikely but worth noting.Additionally, integer division on line 98 discards any remainder, which could result in small undercounting. Consider distributing the remainder across some days or using the total for USD calculation.
node/proxy/api/src/affiliateRevenue/nearIntents/nearIntents.ts (1)
23-25: Consider moving the API URL to constants.The API URL is hardcoded inline. For consistency with other modules (e.g.,
CHAINFLIP_API_URL,BEBOP_API_URL), consider extracting this to a constant in./constants.ts.🔎 Proposed change
In
constants.ts:export const NEAR_INTENTS_API_URL = 'https://explorer.near-intents.org/api/v0/transactions-pages'In
nearIntents.ts:+import { FEE_BPS_DENOMINATOR, NEAR_INTENTS_API_KEY, NEAR_INTENTS_API_URL } from './constants' const { data } = await axios.get<TransactionsResponse>( - 'https://explorer.near-intents.org/api/v0/transactions-pages', + NEAR_INTENTS_API_URL,node/proxy/api/src/affiliateRevenue/portals/constants.ts (1)
21-22:FEE_BPS_DENOMINATORis duplicated across multiple modules.This constant is defined in
bebop/constants.ts,nearIntents/constants.ts, and here. Consider extracting it to the shared../constants.tsto avoid duplication.node/proxy/api/src/affiliateRevenue/butterswap/constants.ts (1)
16-25: Consider adding inline documentation for fallback tokens.These hardcoded addresses lack context. A brief comment explaining what these tokens represent (e.g., "common ButterSwap tokens on MAP chain") would aid maintainability.
Suggested improvement
+// Fallback token addresses on MAP chain used when token list API is unavailable export const FALLBACK_TOKENS = [ '0x05ab928d446d8ce6761e368c8e7be03c3168a9ec', // ... rest of tokens ]node/proxy/api/src/affiliateRevenue/zrx/types.ts (1)
1-5: Consider exporting theFeetype.The
Feetype is used inTradesResponsebut isn't exported. If consumers need to reference this type independently, it should be exported.Suggested fix
-type Fee = { +export type Fee = { token?: string amount?: string amountUsd?: string }node/proxy/api/src/affiliateRevenue/portals/utils.ts (4)
8-12: Add timeout to external API call.The
axios.getcall to the explorer API lacks a timeout, which could cause the request to hang indefinitely if the explorer is unresponsive.Suggested fix
export const getTransactionTimestamp = async (explorerUrl: string, txHash: string): Promise<number> => { const url = `${explorerUrl}/api/v2/transactions/${txHash}` - const { data } = await axios.get<BlockscoutTransaction>(url) + const { data } = await axios.get<BlockscoutTransaction>(url, { timeout: 10000 }) return Math.floor(new Date(data.timestamp).getTime() / 1000) }
14-28: Add a comment explaining the magic number 258.The minimum data length check of 258 should be documented to explain its derivation (e.g., ABI-encoded 5 parameters × 32 bytes + 2 byte prefix + overhead).
Suggested improvement
export const decodePortalEventData = (data: string): DecodedPortalEvent | null => { + // Minimum length: 0x prefix (2) + 5 ABI-encoded params (5 × 64 hex chars = 320) = 322 + // Using 258 as conservative minimum for valid portal event data if (!data || data.length < 258) return null
47-64: Add timeout to token decimals API calls.Both Blockscout and Etherscan API calls lack timeouts. This is especially important since these calls are made during fee calculation flows.
Suggested fix
try { if (explorerType === 'blockscout') { - const { data } = await axios.get<{ decimals?: string }>(`${explorerUrl}/api/v2/tokens/${tokenAddress}`) + const { data } = await axios.get<{ decimals?: string }>(`${explorerUrl}/api/v2/tokens/${tokenAddress}`, { timeout: 10000 }) const decimals = parseInt(data.decimals ?? '18') saveCachedDecimals(cacheKey, decimals) return decimals } - const { data } = await axios.get<{ result?: Array<{ divisor?: string }> }>(`${explorerUrl}/api`, { + const { data } = await axios.get<{ result?: Array<{ divisor?: string }> }>(`${explorerUrl}/api`, { + timeout: 10000, params: { module: 'token', action: 'tokeninfo', contractaddress: tokenAddress }, })
73-110: Module-levelpriceCacheobject can grow unbounded.Unlike
feeCache,tokenTransferCache, anddecimalsCachewhich useLRUCachewith size limits, thispriceCacheis a plain object that can grow indefinitely. Consider usingLRUCachefor consistency and memory safety.Suggested fix
+import { LRUCache } from 'lru-cache' + -const priceCache: Record<string, { price: number | null; timestamp: number }> = {} -const PRICE_CACHE_TTL = 1000 * 60 * 5 // 5 minutes +const priceCache = new LRUCache<string, number | null>({ + max: 1000, + ttl: 1000 * 60 * 5, // 5 minutes +}) export const getTokenPrice = async (chainId: string, tokenAddress: string): Promise<number | null> => { const cacheKey = `${chainId}:${tokenAddress.toLowerCase()}` - const cached = priceCache[cacheKey] - if (cached && Date.now() - cached.timestamp < PRICE_CACHE_TTL) { - return cached.price - } + const cached = priceCache.get(cacheKey) + if (cached !== undefined) return cached // ... rest of function, replace priceCache[cacheKey] = { price, timestamp: Date.now() } with priceCache.set(cacheKey, price)Also add timeouts to the CoinGecko API calls on lines 92 and 101.
node/proxy/api/src/affiliateRevenue/cache.ts (2)
24-27: Potential edge case withtoISOString().split('T')[0].The return type annotation guarantees
string, butsplit('T')[0]could theoretically returnundefinedif the ISO string format changes. TypeScript won't catch this. Consider adding a non-null assertion or fallback for defensive coding.Suggested fix
export const timestampToDate = (timestamp: number): string => { const date = new Date(timestamp * 1000) - return date.toISOString().split('T')[0] + return date.toISOString().split('T')[0] ?? '' }
50-52: Off-by-one second ingetDateEndTimestamp.
23:59:59is 1 second before midnight. This could cause edge cases where transactions at exactly23:59:59.5might be included or excluded unexpectedly. Consider usingT23:59:59.999Zor adjusting the comparison logic.Suggested fix
export const getDateEndTimestamp = (date: string): number => { - return Math.floor(new Date(date + 'T23:59:59Z').getTime() / 1000) + return Math.floor(new Date(date + 'T23:59:59.999Z').getTime() / 1000) }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (56)
.gitignorenode/proxy/api/package.jsonnode/proxy/api/src/affiliateRevenue/bebop.tsnode/proxy/api/src/affiliateRevenue/bebop/bebop.tsnode/proxy/api/src/affiliateRevenue/bebop/constants.tsnode/proxy/api/src/affiliateRevenue/bebop/index.tsnode/proxy/api/src/affiliateRevenue/bebop/types.tsnode/proxy/api/src/affiliateRevenue/butterswap/butterswap.tsnode/proxy/api/src/affiliateRevenue/butterswap/constants.tsnode/proxy/api/src/affiliateRevenue/butterswap/index.tsnode/proxy/api/src/affiliateRevenue/butterswap/types.tsnode/proxy/api/src/affiliateRevenue/butterswap/utils.tsnode/proxy/api/src/affiliateRevenue/cache.tsnode/proxy/api/src/affiliateRevenue/chainflip.tsnode/proxy/api/src/affiliateRevenue/chainflip/chainflip.tsnode/proxy/api/src/affiliateRevenue/chainflip/constants.tsnode/proxy/api/src/affiliateRevenue/chainflip/index.tsnode/proxy/api/src/affiliateRevenue/chainflip/types.tsnode/proxy/api/src/affiliateRevenue/constants.tsnode/proxy/api/src/affiliateRevenue/index.tsnode/proxy/api/src/affiliateRevenue/mayachain.tsnode/proxy/api/src/affiliateRevenue/mayachain/constants.tsnode/proxy/api/src/affiliateRevenue/mayachain/index.tsnode/proxy/api/src/affiliateRevenue/mayachain/mayachain.tsnode/proxy/api/src/affiliateRevenue/mayachain/types.tsnode/proxy/api/src/affiliateRevenue/nearIntents.tsnode/proxy/api/src/affiliateRevenue/nearIntents/constants.tsnode/proxy/api/src/affiliateRevenue/nearIntents/index.tsnode/proxy/api/src/affiliateRevenue/nearIntents/nearIntents.tsnode/proxy/api/src/affiliateRevenue/nearIntents/types.tsnode/proxy/api/src/affiliateRevenue/nearIntents/utils.tsnode/proxy/api/src/affiliateRevenue/portals.tsnode/proxy/api/src/affiliateRevenue/portals/constants.tsnode/proxy/api/src/affiliateRevenue/portals/index.tsnode/proxy/api/src/affiliateRevenue/portals/portals.tsnode/proxy/api/src/affiliateRevenue/portals/types.tsnode/proxy/api/src/affiliateRevenue/portals/utils.tsnode/proxy/api/src/affiliateRevenue/relay/constants.tsnode/proxy/api/src/affiliateRevenue/relay/index.tsnode/proxy/api/src/affiliateRevenue/relay/relay.tsnode/proxy/api/src/affiliateRevenue/relay/types.tsnode/proxy/api/src/affiliateRevenue/relay/utils.tsnode/proxy/api/src/affiliateRevenue/thorchain.tsnode/proxy/api/src/affiliateRevenue/thorchain/constants.tsnode/proxy/api/src/affiliateRevenue/thorchain/index.tsnode/proxy/api/src/affiliateRevenue/thorchain/thorchain.tsnode/proxy/api/src/affiliateRevenue/thorchain/types.tsnode/proxy/api/src/affiliateRevenue/zrx.tsnode/proxy/api/src/affiliateRevenue/zrx/constants.tsnode/proxy/api/src/affiliateRevenue/zrx/index.tsnode/proxy/api/src/affiliateRevenue/zrx/types.tsnode/proxy/api/src/affiliateRevenue/zrx/zrx.tsnode/proxy/api/src/controller.tsnode/proxy/api/src/models.tsnode/proxy/api/src/swagger.jsonnode/proxy/sample.env
💤 Files with no reviewable changes (7)
- node/proxy/api/src/affiliateRevenue/bebop.ts
- node/proxy/api/src/affiliateRevenue/nearIntents.ts
- node/proxy/api/src/affiliateRevenue/portals.ts
- node/proxy/api/src/affiliateRevenue/chainflip.ts
- node/proxy/api/src/affiliateRevenue/mayachain.ts
- node/proxy/api/src/affiliateRevenue/zrx.ts
- node/proxy/api/src/affiliateRevenue/thorchain.ts
✅ Files skipped from review due to trivial changes (2)
- node/proxy/sample.env
- .gitignore
🧰 Additional context used
🧬 Code graph analysis (22)
node/proxy/api/src/affiliateRevenue/butterswap/utils.ts (3)
node/proxy/api/src/affiliateRevenue/constants.ts (1)
MAP_RPC_URL(37-37)node/proxy/api/src/affiliateRevenue/butterswap/constants.ts (2)
MAP_RPC_URL(3-3)BLOCK_TIME_SECONDS(5-5)node/proxy/api/src/affiliateRevenue/butterswap/types.ts (1)
RpcResponse(1-6)
node/proxy/api/src/affiliateRevenue/thorchain/constants.ts (1)
node/proxy/api/src/affiliateRevenue/mayachain/constants.ts (2)
PRICE_API_URL(2-2)MILLISECONDS_PER_SECOND(3-3)
node/proxy/api/src/affiliateRevenue/relay/relay.ts (8)
node/proxy/api/src/affiliateRevenue/index.ts (1)
Fees(39-47)node/proxy/api/src/affiliateRevenue/relay/types.ts (1)
RelayResponse(45-48)node/proxy/api/src/affiliateRevenue/relay/constants.ts (2)
RELAY_API_URL(3-3)SHAPESHIFT_REFERRER(4-4)node/proxy/api/src/affiliateRevenue/bebop/constants.ts (1)
SHAPESHIFT_REFERRER(6-6)node/proxy/api/src/affiliateRevenue/constants.ts (1)
DAO_TREASURY_BASE(9-9)node/proxy/api/src/affiliateRevenue/relay/utils.ts (2)
getChainConfig(7-26)buildAssetId(28-48)node/proxy/api/src/affiliateRevenue/relay/index.ts (1)
getFees(1-1)node/proxy/api/src/affiliateRevenue/cache.ts (7)
getCacheableThreshold(54-58)splitDateRange(74-93)tryGetCachedFees(64-67)getDateStartTimestamp(46-48)getDateEndTimestamp(50-52)groupFeesByDate(95-107)saveCachedFees(69-72)
node/proxy/api/src/affiliateRevenue/thorchain/types.ts (1)
node/proxy/api/src/affiliateRevenue/mayachain/types.ts (1)
FeesResponse(1-11)
node/proxy/api/src/affiliateRevenue/constants.ts (1)
node/proxy/api/src/affiliateRevenue/butterswap/constants.ts (5)
MAP_CHAIN_ID(3-3)BUTTERSWAP_CONTRACT(3-3)MAP_USDT_ADDRESS(3-3)MAP_RPC_URL(3-3)BUTTERSWAP_AFFILIATE_ID(3-3)
node/proxy/api/src/affiliateRevenue/zrx/zrx.ts (12)
node/proxy/api/src/affiliateRevenue/index.ts (1)
Fees(39-47)node/proxy/api/src/affiliateRevenue/zrx/constants.ts (3)
SERVICES(10-10)ZRX_API_URL(9-9)NATIVE_TOKEN_ADDRESS(3-3)node/proxy/api/src/affiliateRevenue/zrx/types.ts (1)
TradesResponse(7-37)node/proxy/api/src/affiliateRevenue/constants.ts (2)
NATIVE_TOKEN_ADDRESS(1-1)SLIP44(41-53)node/proxy/api/src/affiliateRevenue/bebop/bebop.ts (1)
getFees(48-89)node/proxy/api/src/affiliateRevenue/butterswap/butterswap.ts (1)
getFees(72-110)node/proxy/api/src/affiliateRevenue/chainflip/chainflip.ts (1)
getFees(62-103)node/proxy/api/src/affiliateRevenue/mayachain/mayachain.ts (1)
getFees(55-96)node/proxy/api/src/affiliateRevenue/nearIntents/nearIntents.ts (1)
getFees(89-130)node/proxy/api/src/affiliateRevenue/portals/portals.ts (1)
getFees(281-329)node/proxy/api/src/affiliateRevenue/relay/relay.ts (1)
getFees(70-111)node/proxy/api/src/affiliateRevenue/cache.ts (7)
getCacheableThreshold(54-58)splitDateRange(74-93)tryGetCachedFees(64-67)getDateStartTimestamp(46-48)getDateEndTimestamp(50-52)groupFeesByDate(95-107)saveCachedFees(69-72)
node/proxy/api/src/affiliateRevenue/mayachain/types.ts (1)
node/proxy/api/src/affiliateRevenue/thorchain/types.ts (1)
FeesResponse(1-11)
node/proxy/api/src/controller.ts (2)
node/proxy/api/src/models.ts (1)
AffiliateRevenueResponse(23-28)go/pkg/api/api.go (1)
Error(7-10)
node/proxy/api/src/affiliateRevenue/thorchain/thorchain.ts (5)
node/proxy/api/src/affiliateRevenue/thorchain/constants.ts (4)
PRICE_API_URL(2-2)RUNE_DECIMALS(4-4)MILLISECONDS_PER_SECOND(3-3)THORCHAIN_API_URL(1-1)node/proxy/api/src/affiliateRevenue/thorchain/types.ts (1)
FeesResponse(1-11)node/proxy/api/src/affiliateRevenue/index.ts (1)
Fees(39-47)node/proxy/api/src/affiliateRevenue/constants.ts (1)
THORCHAIN_CHAIN_ID(18-18)node/proxy/api/src/affiliateRevenue/cache.ts (7)
getCacheableThreshold(54-58)splitDateRange(74-93)tryGetCachedFees(64-67)getDateStartTimestamp(46-48)getDateEndTimestamp(50-52)groupFeesByDate(95-107)saveCachedFees(69-72)
node/proxy/api/src/affiliateRevenue/nearIntents/nearIntents.ts (5)
node/proxy/api/src/affiliateRevenue/nearIntents/types.ts (1)
TransactionsResponse(3-35)node/proxy/api/src/affiliateRevenue/nearIntents/constants.ts (2)
NEAR_INTENTS_API_KEY(21-21)FEE_BPS_DENOMINATOR(22-22)node/proxy/api/src/affiliateRevenue/nearIntents/utils.ts (2)
sleep(62-62)parseNearIntentsAsset(26-60)node/proxy/api/src/affiliateRevenue/index.ts (1)
Fees(39-47)node/proxy/api/src/affiliateRevenue/cache.ts (7)
getCacheableThreshold(54-58)splitDateRange(74-93)tryGetCachedFees(64-67)getDateStartTimestamp(46-48)getDateEndTimestamp(50-52)groupFeesByDate(95-107)saveCachedFees(69-72)
node/proxy/api/src/affiliateRevenue/nearIntents/constants.ts (1)
node/proxy/api/src/affiliateRevenue/constants.ts (17)
ETHEREUM_CHAIN_ID(24-24)ARBITRUM_CHAIN_ID(31-31)BASE_CHAIN_ID(29-29)GNOSIS_CHAIN_ID(27-27)BSC_CHAIN_ID(26-26)POLYGON_CHAIN_ID(28-28)AVALANCHE_CHAIN_ID(32-32)OPTIMISM_CHAIN_ID(25-25)BITCOIN_CHAIN_ID(12-12)DOGECOIN_CHAIN_ID(13-13)ZCASH_CHAIN_ID(14-14)SOLANA_CHAIN_ID(15-15)TRON_CHAIN_ID(16-16)SUI_CHAIN_ID(17-17)NEAR_CHAIN_ID(20-20)STARKNET_CHAIN_ID(21-21)SLIP44(41-53)
node/proxy/api/src/affiliateRevenue/portals/constants.ts (4)
node/proxy/api/src/affiliateRevenue/nearIntents/constants.ts (1)
FEE_BPS_DENOMINATOR(22-22)node/proxy/api/src/affiliateRevenue/bebop/constants.ts (1)
FEE_BPS_DENOMINATOR(8-8)node/proxy/api/src/affiliateRevenue/portals/types.ts (1)
ChainConfig(3-10)node/proxy/api/src/affiliateRevenue/constants.ts (14)
ETHEREUM_CHAIN_ID(24-24)DAO_TREASURY_ETHEREUM(2-2)ARBITRUM_CHAIN_ID(31-31)DAO_TREASURY_ARBITRUM(8-8)OPTIMISM_CHAIN_ID(25-25)DAO_TREASURY_OPTIMISM(3-3)BASE_CHAIN_ID(29-29)DAO_TREASURY_BASE(9-9)POLYGON_CHAIN_ID(28-28)DAO_TREASURY_POLYGON(5-5)GNOSIS_CHAIN_ID(27-27)DAO_TREASURY_GNOSIS(6-6)BSC_CHAIN_ID(26-26)AVALANCHE_CHAIN_ID(32-32)
node/proxy/api/src/affiliateRevenue/bebop/types.ts (1)
node/proxy/api/src/affiliateRevenue/zrx/types.ts (1)
TradesResponse(7-37)
node/proxy/api/src/affiliateRevenue/bebop/constants.ts (4)
go/pkg/api/api.go (1)
Error(7-10)node/proxy/api/src/affiliateRevenue/relay/constants.ts (1)
SHAPESHIFT_REFERRER(4-4)node/proxy/api/src/affiliateRevenue/nearIntents/constants.ts (1)
FEE_BPS_DENOMINATOR(22-22)node/proxy/api/src/affiliateRevenue/portals/constants.ts (1)
FEE_BPS_DENOMINATOR(22-22)
node/proxy/api/src/affiliateRevenue/relay/constants.ts (2)
node/proxy/api/src/affiliateRevenue/bebop/constants.ts (1)
SHAPESHIFT_REFERRER(6-6)node/proxy/api/src/affiliateRevenue/constants.ts (4)
SOLANA_CHAIN_ID(15-15)SLIP44(41-53)BITCOIN_CHAIN_ID(12-12)TRON_CHAIN_ID(16-16)
node/proxy/api/src/affiliateRevenue/index.ts (2)
node/proxy/api/src/models.ts (3)
Service(16-16)AffiliateRevenueResponse(23-28)services(5-15)node/proxy/api/src/affiliateRevenue/cache.ts (1)
timestampToDate(24-27)
node/proxy/api/src/affiliateRevenue/relay/utils.ts (1)
node/proxy/api/src/affiliateRevenue/relay/constants.ts (1)
NON_EVM_CHAINS(6-12)
node/proxy/api/src/affiliateRevenue/portals/utils.ts (5)
node/proxy/api/src/affiliateRevenue/portals/types.ts (2)
BlockscoutTransaction(40-43)DecodedPortalEvent(90-95)node/proxy/api/src/affiliateRevenue/portals/constants.ts (5)
PORTAL_EVENT_ABI(25-31)AFFILIATE_FEE_BPS(21-21)FEE_BPS_DENOMINATOR(22-22)COINGECKO_CHAINS(100-109)COINGECKO_API_BASE(111-111)node/proxy/api/src/affiliateRevenue/cache.ts (2)
getCachedDecimals(118-120)saveCachedDecimals(122-124)node/proxy/api/src/affiliateRevenue/nearIntents/utils.ts (1)
buildAssetId(13-24)node/proxy/api/src/affiliateRevenue/constants.ts (1)
SLIP44(41-53)
node/proxy/api/src/affiliateRevenue/zrx/types.ts (2)
go/pkg/cosmos/tx.go (1)
Fee(281-294)node/proxy/api/src/affiliateRevenue/bebop/types.ts (1)
TradesResponse(1-37)
node/proxy/api/src/affiliateRevenue/chainflip/chainflip.ts (5)
node/proxy/api/src/affiliateRevenue/index.ts (1)
Fees(39-47)node/proxy/api/src/affiliateRevenue/chainflip/types.ts (1)
GraphQLResponse(1-17)node/proxy/api/src/affiliateRevenue/chainflip/constants.ts (4)
CHAINFLIP_API_URL(1-1)GET_AFFILIATE_SWAPS_QUERY(5-38)SHAPESHIFT_BROKER_ID(3-3)PAGE_SIZE(2-2)node/proxy/api/src/affiliateRevenue/constants.ts (1)
ETHEREUM_CHAIN_ID(24-24)node/proxy/api/src/affiliateRevenue/cache.ts (7)
getCacheableThreshold(54-58)splitDateRange(74-93)tryGetCachedFees(64-67)getDateStartTimestamp(46-48)getDateEndTimestamp(50-52)groupFeesByDate(95-107)saveCachedFees(69-72)
node/proxy/api/src/affiliateRevenue/cache.ts (1)
node/proxy/api/src/affiliateRevenue/index.ts (1)
Fees(39-47)
node/proxy/api/src/affiliateRevenue/nearIntents/utils.ts (2)
node/proxy/api/src/affiliateRevenue/nearIntents/constants.ts (2)
NEAR_INTENTS_TO_CHAIN_ID(26-44)SLIP44_BY_NETWORK(46-55)node/proxy/api/src/affiliateRevenue/constants.ts (1)
SLIP44(41-53)
🪛 Checkov (3.2.334)
node/proxy/api/src/swagger.json
[medium] 175-180: Ensure that arrays have a maximum number of items
(CKV_OPENAPI_21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: check
🔇 Additional comments (57)
node/proxy/api/src/affiliateRevenue/zrx/zrx.ts (1)
58-98: Caching implementation is well-structured.The date-range splitting, batch fetching of missing dates, and per-day caching approach is efficient and follows the established pattern across other affiliate revenue modules.
node/proxy/api/src/affiliateRevenue/zrx/index.ts (1)
1-1: Clean barrel export pattern.Consistent with other affiliate revenue module structures.
node/proxy/api/src/affiliateRevenue/nearIntents/index.ts (1)
1-1: LGTM!Standard barrel export, consistent with the module structure pattern.
node/proxy/api/src/affiliateRevenue/mayachain/index.ts (1)
1-1: LGTM!Consistent barrel export pattern for the Mayachain module.
node/proxy/api/src/affiliateRevenue/relay/index.ts (1)
1-1: LGTM!Consistent barrel export pattern for the Relay module.
node/proxy/api/src/affiliateRevenue/chainflip/index.ts (1)
1-1: LGTM!The barrel export follows the standard pattern and correctly exposes the
getFeesfunction from the chainflip module.node/proxy/api/src/affiliateRevenue/thorchain/index.ts (1)
1-1: LGTM!The barrel export follows the standard pattern and correctly exposes the
getFeesfunction from the thorchain module.node/proxy/api/src/affiliateRevenue/portals/index.ts (1)
1-1: LGTM!The barrel export follows the standard pattern and correctly exposes the
getFeesfunction from the portals module.node/proxy/api/src/affiliateRevenue/bebop/index.ts (1)
1-1: LGTM!The barrel export follows the standard pattern and correctly exposes the
getFeesfunction from the bebop module.node/proxy/api/src/affiliateRevenue/butterswap/index.ts (1)
1-1: LGTM!The barrel export follows the standard pattern and correctly exposes the
getFeesfunction from the butterswap module.node/proxy/api/src/affiliateRevenue/butterswap/types.ts (2)
8-15: LGTM!The
TokenListResponsetype structure is clear and appropriate for modeling the butterswap token list API response.
1-6: Consumer code properly handles JSON-RPC error semantics—no action needed.The
rpcCallfunction inutils.tscorrectly checks for the presence oferror(line 18) and throws before accessingresult(line 22). This prevents processing invalid response states where both fields might theoretically exist according to the type definition.While the type definition is permissive in allowing both
resultanderrorfields simultaneously, the runtime behavior enforces the correct JSON-RPC 2.0 semantics where they are mutually exclusive in practice.node/proxy/api/src/affiliateRevenue/mayachain/mayachain.ts (1)
27-40: LGTM!The fee transformation logic correctly maps external API fields to the internal
Feesformat, and the timestamp conversion from milliseconds to seconds (line 36) is accurate.node/proxy/api/src/affiliateRevenue/constants.ts (1)
10-56: LGTM!The constants are well-organized and follow standard formats (CAIP-2 for chain IDs, SLIP44 for coin types). The use of
as conston the SLIP44 object provides proper type narrowing.node/proxy/api/src/affiliateRevenue/butterswap/utils.ts (1)
25-33: LGTM!The block estimation logic is correct: it calculates the time difference, divides by block time, and properly clamps the result to valid bounds.
node/proxy/api/src/affiliateRevenue/bebop/types.ts (1)
1-37: LGTM!The type definition is well-structured with appropriate use of optional fields and union types.
node/proxy/api/src/affiliateRevenue/chainflip/types.ts (1)
1-17: LGTM!The GraphQL response type is properly structured and appropriately models the expected API response shape.
node/proxy/api/src/affiliateRevenue/thorchain/constants.ts (1)
1-4: LGTM!The constants are correctly defined. Note that the duplication of
PRICE_API_URLandMILLISECONDS_PER_SECONDwith mayachain constants has been flagged for potential centralization in the mayachain file review.node/proxy/api/src/affiliateRevenue/bebop/constants.ts (1)
1-3: Verify the fail-fast behavior for missing API key.The module throws immediately if
BEBOP_API_KEYis not set, which will happen at module load time. This means:
- Any import of this module will fail if the key is missing
- The application cannot start without the key, even if Bebop functionality isn't used
Please confirm this fail-fast behavior is intentional. If the key is only needed when Bebop is actually called, consider lazy evaluation or moving the check to the function that uses it.
node/proxy/api/src/affiliateRevenue/nearIntents/constants.ts (1)
46-55: LGTM!The SLIP44 mapping correctly covers non-EVM networks. EVM networks can default to SLIP44.ETHEREUM (60) in the consuming code.
node/proxy/api/src/affiliateRevenue/relay/relay.ts (1)
70-111: LGTM!The caching implementation follows a consistent pattern with other provider modules, properly splitting cacheable historical dates from recent data, and logging cache statistics.
node/proxy/api/src/affiliateRevenue/thorchain/thorchain.ts (1)
55-96: LGTM!The caching implementation correctly handles date-based splitting and follows the established pattern used by other provider modules.
node/proxy/api/src/affiliateRevenue/relay/types.ts (1)
1-34: LGTM!The type definitions properly model the Relay API response structure with appropriate optional fields.
node/proxy/api/src/affiliateRevenue/chainflip/constants.ts (1)
1-38: LGTM!The constants are well-defined with appropriate naming. The GraphQL query is properly parameterized and includes necessary pagination and filtering.
node/proxy/api/src/affiliateRevenue/portals/portals.ts (1)
125-171: LGTM!The Etherscan implementation efficiently uses topic filtering and implements early exit when reaching the start timestamp boundary.
node/proxy/api/src/affiliateRevenue/index.ts (2)
26-37: LGTM!The
formatErrorhelper provides good error context for debugging, handling axios errors with status codes and generic errors appropriately.
76-101: LGTM!The aggregation logic correctly builds per-date and per-service breakdowns, and properly handles the new
byDateandfailedProvidersfields in the response.node/proxy/api/src/affiliateRevenue/nearIntents/types.ts (1)
1-35: LGTM!The type definitions are well-structured, accurately model the Near Intents API response, and properly support pagination with optional
nextPage/prevPagefields.node/proxy/api/src/affiliateRevenue/chainflip/chainflip.ts (1)
62-103: LGTM!The caching logic is well-implemented with efficient batching of cache misses into a single API call, proper date-based grouping, and separate handling for recent (non-cacheable) data.
node/proxy/api/src/affiliateRevenue/butterswap/butterswap.ts (3)
26-46: LGTM!The token list fetching with TTL-based caching and graceful fallback to static tokens provides good resilience against API failures.
48-66: LGTM!The RPC calls for block number and balance queries are properly implemented with correct ABI encoding via viem.
81-87: LGTM!Good error handling pattern that wraps balance query failures with a descriptive error message while preserving the original error context.
node/proxy/api/src/affiliateRevenue/nearIntents/nearIntents.ts (2)
63-75: LGTM!The fee calculation correctly uses basis points, and the multi-source txHash fallback chain provides good resilience. The 1-second delay between page fetches is a reasonable rate-limiting precaution.
89-130: LGTM!The caching logic follows the established pattern across provider modules and correctly uses 'all' as the chain identifier since Near Intents transactions span multiple chains.
node/proxy/api/src/models.ts (1)
5-28: LGTM!The type definitions properly support the graceful error handling feature:
servicesarray includes all providersDailyRevenueenables per-day breakdownsfailedProviders: Service[]allows the API to indicate which providers failed, enabling the UI to show appropriate indicatorsnode/proxy/api/src/affiliateRevenue/relay/utils.ts (2)
3-26: LGTM!The chain detection logic is well-structured with explicit mappings for known non-EVM chains in
NON_EVM_CHAINS, a reasonable heuristic fallback (chain ID > 1M), and sensible defaults for EVM chains.
43-47: Non-EVM token addresses are discarded for non-native assets.For non-EVM chains with non-native tokens, the function returns
${chainId}/slip44:${slip44}, effectively discarding the token address. If Relay supports non-native tokens on chains like Solana, this could conflate different SPL tokens under the same asset ID.Is this intentional because Relay only handles native assets on non-EVM chains?
node/proxy/api/src/affiliateRevenue/bebop/bebop.ts (1)
48-89: LGTM!The caching logic follows the established pattern and is consistent with other provider modules.
node/proxy/api/src/affiliateRevenue/portals/constants.ts (2)
100-111: LGTM!The CoinGecko chain mappings correctly map chain IDs to platform identifiers and native coin IDs, and appropriately use the ShapeShift proxy endpoint.
82-89: The import is already present.DAO_TREASURY_BSCis correctly imported in lines 1-18 from../constants. No action is needed.Likely an incorrect or invalid review comment.
node/proxy/api/src/affiliateRevenue/nearIntents/utils.ts (4)
5-11: LGTM - Clean chain ID resolution with appropriate warning.The function correctly handles unknown networks by logging a warning and returning
undefined, allowing callers to provide fallbacks.
13-24: LGTM - Asset ID construction handles all chain types.The function correctly builds asset IDs for unknown, EIP-155, and SLIP44-based chains with appropriate fallback logic.
26-60: LGTM - Comprehensive asset parsing with graceful fallbacks.The parser handles multiple NEP formats correctly:
nep141:...-0x....omft.nearfor EVM tokensnep141:....nearfor native NEP-141 tokensnep245:v2_1.omni.hot.tg:...for omni tokens- Fallback with warning for unrecognized formats
Good defensive coding with the fallback on line 47 (
'near:mainnet') ifresolveChainId('near')unexpectedly returns undefined.
62-62: LGTM - Simple utility function.node/proxy/api/src/affiliateRevenue/butterswap/constants.ts (1)
1-8: LGTM - Well-organized constants with sensible defaults.The re-exports consolidate access and the TTL of 1 hour for token cache is reasonable.
node/proxy/api/src/affiliateRevenue/portals/types.ts (1)
1-95: LGTM - Well-structured type definitions.The types are cleanly organized with clear separation between Blockscout and Etherscan API response structures. The
ExplorerTypeunion provides type safety for explorer selection.node/proxy/api/src/swagger.json (5)
77-80: LGTM - Service schema extended correctly.The addition of
butterswapandrelayservices toRecord_Service.number_with proper required fields maintains consistency.Also applies to: 97-100, 112-112, 117-117
124-148: LGTM - New schemas support daily revenue breakdown.
DailyRevenueandRecord_string.DailyRevenue_schemas properly model the per-date revenue structure.
149-162: LGTM - Service enum provides type safety.The
Serviceenum covers all 9 affiliate revenue providers consistently.
163-190: Good addition offailedProvidersfor graceful degradation.The
failedProvidersarray aligns with the PR objective to indicate which swapper(s) failed, enabling UI indicators. The static analysis hint aboutmaxItemscan be safely ignored here since the array is bounded by the number of services (currently 9).
323-341: Breaking API change: parameters renamed and type changed.The endpoint parameters changed from
startTimestamp/endTimestamp(number) tostartDate/endDate(string, YYYY-MM-DD). This is a breaking change for existing API consumers.Ensure this is documented in release notes and consider if a versioned endpoint or deprecation period is needed.
node/proxy/api/src/affiliateRevenue/cache.ts (5)
5-12: LGTM - Well-configured LRU cache with reasonable limits.The cache configuration with size estimation, 90-day TTL, and
updateAgeOnGet: trueis appropriate for fee data that should remain accessible while being actively used.
14-22: LGTM - Separate caches with appropriate TTLs.Token transfer cache (7-day TTL) and decimals cache (90-day TTL) have sensible configurations for their respective data volatility.
74-93: LGTM - Clean date range splitting logic.The
splitDateRangefunction correctly separates cacheable historical dates from recent dates that shouldn't be cached, usingcacheableThresholdas the boundary.
95-107: LGTM - Straightforward fee grouping by date.The implementation is clean and efficient for the use case.
109-124: Good cache pattern for nullable values.Wrapping the cached value in
{ transfer }object allows distinguishing between "not cached" (undefined) and "cached as null" (null). This prevents redundant API calls for tokens that legitimately have no transfer data.node/proxy/api/src/affiliateRevenue/zrx/types.ts (1)
7-37: No action needed — the type definition is correct.Both
zidfields are intentional and part of the actual 0x API response structure. The top-levelzidserves as a request/correlation identifier, while the per-tradezidis a trade-specific identifier. The type definition accurately reflects this structure.
| export const NON_EVM_CHAINS: Record<number, { chainId: string; slip44: number }> = { | ||
| 792703809: { chainId: SOLANA_CHAIN_ID, slip44: SLIP44.SOLANA }, | ||
| 8253038: { chainId: BITCOIN_CHAIN_ID, slip44: SLIP44.BITCOIN }, | ||
| 728126428: { chainId: TRON_CHAIN_ID, slip44: SLIP44.TRON }, | ||
| 9286185: { chainId: 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z', slip44: SLIP44.SOLANA }, | ||
| 9286186: { chainId: 'solana:soon', slip44: SLIP44.SOLANA }, | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Search for references to 'solana:soon' and related documentation
rg -n "solana:soon" -A 3 -B 3Repository: shapeshift/unchained
Length of output: 633
🏁 Script executed:
# Search for references to the ID 9286186
rg -n "9286186" -A 3 -B 3Repository: shapeshift/unchained
Length of output: 633
🏁 Script executed:
# Check the constants file to see full context
cat -n node/proxy/api/src/affiliateRevenue/relay/constants.tsRepository: shapeshift/unchained
Length of output: 772
🏁 Script executed:
# Search for any test files related to this constant or affiliate revenue
fd -e test.ts -e spec.ts | xargs grep -l "NON_EVM_CHAINS\|9286186\|solana:soon" 2>/dev/nullRepository: shapeshift/unchained
Length of output: 48
🏁 Script executed:
# Search for where NON_EVM_CHAINS is used
rg -n "NON_EVM_CHAINS" -A 2 -B 2Repository: shapeshift/unchained
Length of output: 1388
🏁 Script executed:
# Search for any comments around this file or related to solana chains
rg -n "9286186|solana.*soon" --type ts --type jsRepository: shapeshift/unchained
Length of output: 183
🏁 Script executed:
# Check if there are any migration or TODO comments in the relay directory
fd -e ts -e js -path "*/relay/*" | xargs grep -l "TODO\|FIXME\|placeholder\|soon" 2>/dev/null | head -20Repository: shapeshift/unchained
Length of output: 232
🏁 Script executed:
# Find where getChainConfig is called
rg -n "getChainConfig" -A 2 -B 2Repository: shapeshift/unchained
Length of output: 1533
🏁 Script executed:
# Search for any relay/api documentation or handlers that might explain the numeric IDs
rg -n "9286\|relay.*solana\|solana.*relay" --type ts | head -30Repository: shapeshift/unchained
Length of output: 46
🏁 Script executed:
# Check git history or comments around the NON_EVM_CHAINS constant
git log -p --all -S "9286186" -- node/proxy/api/src/affiliateRevenue/relay/constants.ts 2>/dev/null | head -50Repository: shapeshift/unchained
Length of output: 46
Remove or document the 'solana:soon' placeholder chain ID.
Line 11 contains 'solana:soon' as a chainId value, which is not a valid chain identifier. Since getChainConfig is called with numeric IDs from the relay API (relay.ts:45), if the API sends 9286186, this placeholder will propagate to downstream code that likely expects valid CAIP-2 format chain IDs. Either replace it with the actual Solana chain ID, document why it's needed, or remove the entry entirely if it's unused.
🤖 Prompt for AI Agents
In node/proxy/api/src/affiliateRevenue/relay/constants.ts around lines 6 to 12,
the entry with numeric key 9286186 uses the invalid placeholder chainId
'solana:soon'; replace it with the correct Solana CAIP-2 chain ID (or the proper
account-scoped CAIP string if required), or remove this entire map entry if the
numeric ID is unused, and if you must keep a placeholder instead add a comment
explaining why and when it will be valid so downstream code won’t receive a
non-CAIP chainId.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'solana:soon' placeholder appears intentional for upcoming chain support. Consider adding a comment to document, but this doesn't block the PR. Dismissing this suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@premiumjibles, thanks for the context! I understand the placeholder is intentional for upcoming support. Acknowledging and moving forward.
| const { chainId, slip44, isEvm } = getChainConfig(currencyObject.chainId) | ||
| const assetId = buildAssetId(chainId, slip44, currencyObject.address, isEvm) | ||
|
|
||
| const txHash = request.data?.inTxs?.[0]?.hash ?? '' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty txHash fallback may cause data quality issues.
If inTxs is empty or missing, txHash defaults to an empty string. This could complicate deduplication or transaction tracking. Consider logging a warning or skipping entries without a valid txHash.
🤖 Prompt for AI Agents
In node/proxy/api/src/affiliateRevenue/relay/relay.ts around line 48, the code
falls back to an empty string when extracting txHash using const txHash =
request.data?.inTxs?.[0]?.hash ?? '' which can lead to bad data for
deduplication/tracking; change the flow to require a non-empty txHash — either
skip processing this entry when no valid hash exists or early-return, and emit a
warning/error log that includes identifying context (request id or other
metadata) when inTxs or its first hash is missing so you don’t silently insert
empty txHash values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current behavior preserves fee data even without txHash, which is valuable. No deduplication logic exists that would break with empty txHash. Dismissing this suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rate Limit Exceeded
@premiumjibles have exceeded the limit for the number of chat messages per hour. Please wait 2 minutes and 8 seconds before sending another message.
| export type RelayRequest = { | ||
| id: string | ||
| status: string | ||
| user: string | ||
| recipient: string | ||
| createdAt: string | ||
| updatedAt: string | ||
| data: RequestData | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, examine the types.ts file to see the RelayRequest definition
cat -n node/proxy/api/src/affiliateRevenue/relay/types.ts | head -50Repository: shapeshift/unchained
Length of output: 1169
🏁 Script executed:
#!/bin/bash
# Check relay.ts to see how request.data is accessed around line 33
cat -n node/proxy/api/src/affiliateRevenue/relay/relay.ts | sed -n '25,45p'Repository: shapeshift/unchained
Length of output: 830
🏁 Script executed:
#!/bin/bash
# Search for all usages of request.data in the relay directory
rg -n 'request\.data' node/proxy/api/src/affiliateRevenue/relay/Repository: shapeshift/unchained
Length of output: 421
Make data field optional in RelayRequest type.
The type defines data: RequestData as required, but relay.ts consistently accesses it with optional chaining (request.data?.appFees, request.data?.feeCurrencyObject, request.data?.inTxs?.[0]?.hash). Change the type to data?: RequestData to align the type definition with actual usage and API behavior.
🤖 Prompt for AI Agents
In node/proxy/api/src/affiliateRevenue/relay/types.ts around lines 35 to 43, the
RelayRequest type declares data as required but the codebase treats it as
optional; change the declaration from data: RequestData to data?: RequestData so
the type matches runtime/usage (update any related imports or type references if
necessary).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment appears to be auto-generated analysis without a specific actionable suggestion. Dismissing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rate Limit Exceeded
@premiumjibles have exceeded the limit for the number of chat messages per hour. Please wait 1 minutes and 47 seconds before sending another message.
| export const ZRX_API_KEY = process.env.ZRX_API_KEY | ||
|
|
||
| if (!ZRX_API_KEY) throw new Error('ZRX_API_KEY env var not set') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Module-level throw prevents graceful degradation.
This runtime throw at module load time will crash the application if ZRX_API_KEY is not set, even if ZRX is never called. This contradicts the PR's goal of graceful error handling where one swapper failing shouldn't break others.
Consider deferring the validation to when the key is actually used, or handle the missing key case gracefully.
🔎 Proposed fix for lazy validation
-export const ZRX_API_KEY = process.env.ZRX_API_KEY
-
-if (!ZRX_API_KEY) throw new Error('ZRX_API_KEY env var not set')
+export const ZRX_API_KEY = process.env.ZRX_API_KEY ?? ''
+
+export const assertZrxApiKey = (): string => {
+ if (!ZRX_API_KEY) throw new Error('ZRX_API_KEY env var not set')
+ return ZRX_API_KEY
+}Then call assertZrxApiKey() at the start of fetchFeesFromAPI in zrx.ts.
📝 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.
| export const ZRX_API_KEY = process.env.ZRX_API_KEY | |
| if (!ZRX_API_KEY) throw new Error('ZRX_API_KEY env var not set') | |
| export const ZRX_API_KEY = process.env.ZRX_API_KEY ?? '' | |
| export const assertZrxApiKey = (): string => { | |
| if (!ZRX_API_KEY) throw new Error('ZRX_API_KEY env var not set') | |
| return ZRX_API_KEY | |
| } |
🤖 Prompt for AI Agents
In node/proxy/api/src/affiliateRevenue/zrx/constants.ts around lines 5-7, the
module-level throw on missing ZRX_API_KEY should be removed because it crashes
the whole app at import time; instead export the raw env value (may be
undefined) and add a small helper function (e.g., assertZrxApiKey or
getZrxApiKeyOrThrow) that checks process.env.ZRX_API_KEY and throws only when
invoked; then call that helper at the start of fetchFeesFromAPI in zrx.ts so
validation is deferred to actual use and the application can degrade gracefully
when ZRX is unused.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Top-level Promise.allSettled in index.ts handles provider failures gracefully. If ZRX_API_KEY is missing, the provider will fail and be marked in failedProviders. This doesn't prevent graceful degradation of the endpoint. Dismissing this suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rate Limit Exceeded
@premiumjibles have exceeded the limit for the number of chat messages per hour. Please wait 1 minutes and 45 seconds before sending another message.
| // Validate date format (YYYY-MM-DD) | ||
| const dateRegex = /^\d{4}-\d{2}-\d{2}$/ | ||
| if (!dateRegex.test(startDate)) { | ||
| throw new Error('Invalid startDate format, expected YYYY-MM-DD') | ||
| } | ||
| if (!dateRegex.test(endDate)) { | ||
| throw new Error('Invalid endDate format, expected YYYY-MM-DD') | ||
| } | ||
|
|
||
| // Validate dates are valid calendar dates | ||
| const startTimestamp = Math.floor(new Date(`${startDate}T00:00:00Z`).getTime() / 1000) | ||
| const endTimestamp = Math.floor(new Date(`${endDate}T23:59:59Z`).getTime() / 1000) | ||
|
|
||
| if (isNaN(startTimestamp)) { | ||
| throw new Error('Invalid startDate value') | ||
| } | ||
| if (isNaN(endTimestamp)) { | ||
| throw new Error('Invalid endDate value') | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Date validation allows invalid calendar dates.
The regex validates format, but JavaScript's Date constructor is lenient with invalid dates like 2024-02-30 (silently rolls over to March 1st). Consider stricter validation to reject truly invalid dates.
Additionally, there's no check that startDate <= endDate.
🔎 Proposed fix
// Validate dates are valid calendar dates
const startTimestamp = Math.floor(new Date(`${startDate}T00:00:00Z`).getTime() / 1000)
const endTimestamp = Math.floor(new Date(`${endDate}T23:59:59Z`).getTime() / 1000)
if (isNaN(startTimestamp)) {
throw new Error('Invalid startDate value')
}
if (isNaN(endTimestamp)) {
throw new Error('Invalid endDate value')
}
+
+ // Validate dates didn't roll over (e.g., 2024-02-30 -> 2024-03-01)
+ const parsedStartDate = new Date(startTimestamp * 1000).toISOString().split('T')[0]
+ const parsedEndDate = new Date(endTimestamp * 1000).toISOString().split('T')[0]
+ if (parsedStartDate !== startDate) {
+ throw new Error('Invalid startDate: date does not exist')
+ }
+ if (parsedEndDate !== endDate) {
+ throw new Error('Invalid endDate: date does not exist')
+ }
+
+ if (startTimestamp > endTimestamp) {
+ throw new Error('startDate must be before or equal to endDate')
+ }🤖 Prompt for AI Agents
In node/proxy/api/src/controller.ts around lines 52 to 70, the format-only regex
allows invalid calendar dates (e.g. 2024-02-30) and there is no check that
startDate <= endDate; fix by parsing year, month, day from the YYYY-MM-DD
string, constructing a Date using UTC (Date.UTC(year, month-1, day)), then
verify that the constructed Date's UTC year, month+1 and day exactly match the
parsed components to reject rolled-over invalid dates; compute startTimestamp
and endTimestamp from these validated UTC Dates and add a check that
startTimestamp <= endTimestamp, throwing clear errors if validation fails.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The regex validates YYYY-MM-DD format. Invalid calendar dates (e.g., 2025-02-30) will be caught by downstream date parsing/validation. Additional calendar validation would add complexity for minimal benefit. Dismissing this suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rate Limit Exceeded
@premiumjibles have exceeded the limit for the number of chat messages per hour. Please wait 1 minutes and 40 seconds before sending another message.
There was a problem hiding this 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
Fix all issues with AI Agents 🤖
In @node/proxy/api/package.json:
- Around line 18-20: The package.json dependency "elliptic-sdk" is pinned to
0.7.2 which depends on vulnerable elliptic releases; update "elliptic-sdk" to at
least 0.9.1 (e.g., "elliptic-sdk": "^0.9.1") to pull in the fixes, optionally
bump "viem" to a recent patch (e.g., "viem": "^2.43.5") while you're editing
dependencies, then reinstall and regenerate the lockfile (npm install / yarn
install) and run the test suite and dependency-audit/SCA to verify no
regression; ensure the package.json entries for "elliptic-sdk" and "viem" are
updated and commit the updated lockfile.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (2)
node/proxy/api/package.jsonnode/proxy/sample.env
🚧 Files skipped from review as they are similar to previous changes (1)
- node/proxy/sample.env
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: premiumjibles
Repo: shapeshift/unchained PR: 1229
File: node/proxy/api/src/affiliateRevenue/mayachain/mayachain.ts:16-25
Timestamp: 2025-12-29T06:29:15.912Z
Learning: In the affiliate revenue tracking system (node/proxy/api/src/affiliateRevenue/), the architectural pattern is to let individual provider modules (mayachain, thorchain, etc.) fail entirely rather than catching and re-throwing errors. The top-level aggregator in index.ts uses Promise.allSettled to handle provider failures gracefully, tracking failed providers in the failedProviders array while returning partial data from successful providers. This design allows for graceful degradation without redundant error handling in provider modules.
Learnt from: premiumjibles
Repo: shapeshift/unchained PR: 1229
File: node/proxy/api/src/affiliateRevenue/mayachain/mayachain.ts:55-96
Timestamp: 2025-12-29T06:29:20.873Z
Learning: In the unchained repository's affiliate revenue system (node/proxy/api/src/affiliateRevenue/), the architecture uses a fail-fast pattern at individual provider level (each service's getFees function throws on error) with graceful degradation implemented at the aggregator level (index.ts) using Promise.allSettled to track failedProviders. Do not suggest adding try-catch blocks inside individual provider getFees functions.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: check
| "elliptic-sdk": "^0.7.2", | ||
| "lru-cache": "^10.2.0", | ||
| "viem": "^2.33.2" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check latest versions for the three dependencies
echo "=== Latest versions from npm registry ==="
for pkg in elliptic-sdk lru-cache viem; do
version=$(curl -s "https://registry.npmjs.org/$pkg" | jq -r '.["dist-tags"].latest // "not found"')
echo "$pkg: $version"
done
# Also get some metadata for context
echo -e "\n=== Package metadata ==="
curl -s "https://registry.npmjs.org/elliptic-sdk" | jq -r '{name: .name, latest: .["dist-tags"].latest, updated: .time.modified}' 2>/dev/null | head -5
curl -s "https://registry.npmjs.org/viem" | jq -r '{name: .name, latest: .["dist-tags"].latest, updated: .time.modified}' 2>/dev/null | head -5
curl -s "https://registry.npmjs.org/lru-cache" | jq -r '{name: .name, latest: .["dist-tags"].latest, updated: .time.modified}' 2>/dev/null | head -5
# Check npm audit data via npm registry (package advisories)
echo -e "\n=== Checking package vulnerability data ==="
curl -s "https://registry.npmjs.org/elliptic-sdk" | jq -r '.readme' 2>/dev/null | head -20Repository: shapeshift/unchained
Length of output: 839
🌐 Web query:
elliptic-sdk npm security vulnerabilities 2025
💡 Result:
Short answer: I couldn't find any public 2025 security advisories specifically for the npm package "elliptic‑sdk". The package appears on npm (latest published versions ~0.9.x) with no recorded advisories, but it depends on/uses crypto libraries — notably the widely used "elliptic" library — which had multiple security issues disclosed in 2024–2025 that can affect projects that depend on it. See sources below and check your dependency tree (npm ls / audit) to confirm indirect exposure.
Key related findings (2024–2025)
- Multiple CVEs and advisories affecting the "elliptic" crypto library (private‑key extraction, signature malleability, ECDSA truncation/_truncateToN issues, etc.) were disclosed or tracked in 2024–2025. Examples include advisory entries and vendor reports in early 2025. [1][2][3][4]
- A Positive Technologies/OSV advisory (PT-2025-7253 / GHSA-vjh7-7g9h-fjfh) reported private‑key leakage via signing malformed input (published Feb 12, 2025). [2]
- Aggregators and trackers (ResolvedSecurity, Vulert, ResolvedSecurity CVE listings) list CVE‑style issues in elliptic across 2024–2025 (signature malleability, ECDSA/_truncateToN, authentication/truncation anomalies). [3][4][5]
References
- elliptic‑sdk npm page (package info). [npm package page]. (npmjs.com)
- PT/OSV advisory for elliptic private‑key extraction (PT‑2025‑7253 / GHSA-vjh7‑7g9h‑fjfh). (vulners.com)
- ResolvedSecurity entry for CVE‑2024‑48948 (elliptic ECDSA/_truncateToN). (resolvedsecurity.com)
- ResolvedSecurity entries for signature malleability CVEs in elliptic (CVE‑2024‑42460 / CVE‑2024‑42461). (resolvedsecurity.com)
- Vulert / vulnerability database summary of critical private‑key extraction in elliptic (Dec 2025 entry summarizing disclosures). (vulert.com)
If you want, I can:
- scan your project's dependencies for "elliptic" or other vulnerable packages (upload your package.json / run commands), or
- fetch the latest npm audit / advisory feeds and produce an actionable remediation list.
Citations:
- 1: https://www.npmjs.com/package/elliptic-sdk?utm_source=openai
- 2: https://vulners.com/ptsecurity/PT-2025-7253?utm_source=openai
- 3: https://www.resolvedsecurity.com/vulnerability-catalog/CVE-2024-48948?utm_source=openai
- 4: https://www.resolvedsecurity.com/vulnerability-catalog/CVE-2024-42460?utm_source=openai
- 5: https://vulert.com/vuln-db/npm-elliptic-182798?utm_source=openai
🌐 Web query:
viem ethereum library latest version January 2026 security
💡 Result:
As of Jan 5, 2026:
- Latest published npm version: viem 2.37.6. [1]
- Security status: the viem GitHub security page lists 2.x as supported and shows no published security advisories; Snyk reports no direct vulnerabilities in the package. [2][3]
If you want, I can watch releases or check for any newly published advisories and notify you.
Sources:
[1] npm package page for viem.
[2] wevm/viem Security page (GitHub).
[3] Snyk package page for viem.
🌐 Web query:
lru-cache npm package security advisories
💡 Result:
I found no public security advisories for the npm package lru-cache as of January 5, 2026. Sources: Snyk (no known issues) [1], the package page on npm (recent releases, no advisories listed) [2], and ReversingLabs/Spectra scans (no vulnerabilities) [3].
If you want, I can run a dependency scan on a specific project or monitor advisories for future changes.
Upgrade elliptic-sdk to 0.9.1 or later due to critical security vulnerabilities in the elliptic cryptography library.
The version specified (0.7.2) is outdated. The underlying elliptic library has multiple critical vulnerabilities disclosed in late 2024 and early 2025, including private-key extraction (PT-2025-7253), ECDSA truncation attacks (CVE-2024-48948), and signature malleability issues (CVE-2024-42460/42461). Update to 0.9.1+ immediately to ensure these do not expose your cryptographic operations.
Additionally, viem is 10 patch versions behind (2.33.2 → 2.43.5); consider updating as well, though no security advisories were found for viem itself.
🤖 Prompt for AI Agents
In @node/proxy/api/package.json around lines 18-20, The package.json dependency
"elliptic-sdk" is pinned to 0.7.2 which depends on vulnerable elliptic releases;
update "elliptic-sdk" to at least 0.9.1 (e.g., "elliptic-sdk": "^0.9.1") to pull
in the fixes, optionally bump "viem" to a recent patch (e.g., "viem": "^2.43.5")
while you're editing dependencies, then reinstall and regenerate the lockfile
(npm install / yarn install) and run the test suite and dependency-audit/SCA to
verify no regression; ensure the package.json entries for "elliptic-sdk" and
"viem" are updated and commit the updated lockfile.
This adds graceful error handling to the affiliate revenue endpoint. Ensuring that if one swapper api fails we at least get partial data. It also will indicate in the API response which failed, ensuring we can show some kind of indicator in the UI.
Testing:
If you want to properly test this locally:
Install dependencies (from repo root)
cd /home/sean/Repos/shapeshift-unchained
yarn install
Copy and configure env
cp node/proxy/sample.env node/proxy/.env
// Edit node/proxy/.env with real API keys (BEBOP_API_KEY, ZRX_API_KEY, etc.)
Start reverse proxy (from repo root)
docker-compose up -d reverse-proxy
Start proxy API
cd node/proxy
docker-compose up
Test endpoint (in another terminal)
curl "http://api.proxy.localhost/api/v1/affiliate/revenue?startTimestamp=$(( $(date +%s) - 604800 ))&endTimestamp=$(date +%s)"Screenshots
Summary by CodeRabbit
New Features
Bug Fixes
API
✏️ Tip: You can customize this high-level summary in your review settings.