Skip to content

Conversation

@Kefancao
Copy link
Contributor

@Kefancao Kefancao commented Oct 2, 2025

Changelist

Verify against the amount skip quotes for eth swaps and set the amount to be the higher of the two. Additionally, specify a tolerance percent that is anchored to max($100, 0.001 * amount).

Test Plan

[Describe how this PR was tested (if applicable)]

Author/Reviewer Checklist

  • If this PR has changes that result in a different app state given the same prior state and transaction list, manually add the state-breaking label.
  • If the PR has breaking postgres changes to the indexer add the indexer-postgres-breaking label.
  • If this PR isn't state-breaking but has changes that modify behavior in PrepareProposal or ProcessProposal, manually add the label proposal-breaking.
  • If this PR is one of many that implement a specific feature, manually label them all feature:[feature-name].
  • If you wish to for mergify-bot to automatically create a PR to backport your change to a release branch, manually add the label backport/[branch-name].
  • Manually add any of the following labels: refactor, chore, bug.

Summary by CodeRabbit

  • New Features

    • Dynamic slippage calculation based on expected output for more predictable swaps.
    • New USDC-based slippage cap to limit slippage in absolute USDC.
    • Increased maximum USDC bridge limit to support larger transfers.
  • Chores

    • Compute slippage at runtime and remove extra debug logging.
  • Tests

    • Added tests validating selection between percentage-based and USDC-based slippage caps.

@Kefancao Kefancao requested a review from a team as a code owner October 2, 2025 20:32
@linear
Copy link

linear bot commented Oct 2, 2025

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 2, 2025

Walkthrough

Compute slippage tolerance at runtime (min of USDC absolute cap and percentage cap), remove a top-level slippage constant and an extraneous log, add SKIP_SLIPPAGE_TOLERANCE_USDC and raise MAXIMUM_BRIDGE_AMOUNT_USDC, and add unit tests for the slippage helper.

Changes

Cohort / File(s) Summary of changes
Skip helper: runtime slippage & API change
indexer/services/comlink/src/helpers/skip-helper.ts
Removed top-level slippageTolerancePercent constant and an extra logging block; added exported getSlippageTolerancePercent(estAmountOut: string): string; compute slippageTolerancePercent at runtime (min of USDC-based absolute cap and percentage cap) immediately before building messages; preserved route resolution and request flow.
Config: USDC slippage cap & max bridge bump
indexer/services/comlink/src/config.ts
Added SKIP_SLIPPAGE_TOLERANCE_USDC: parseInteger({ default: 100 }) (absolute USDC cap) and increased MAXIMUM_BRIDGE_AMOUNT_USDC default from 99_900 to 999_000.
Tests: unit coverage for slippage logic
indexer/services/comlink/__tests__/helpers/skip-helper.test.ts
Added tests that mock config and assert getSlippageTolerancePercent returns the smaller of USDC-based tolerance and percentage-based tolerance for large and small estimated outputs; imported the new function.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller
  participant SkipHelper
  participant RouteService

  Caller->>SkipHelper: request route (chainId, amount, denom)
  SkipHelper->>RouteService: resolve route -> routeResult (estimatedAmountOut)
  SkipHelper->>SkipHelper: expectedAmountOut = estimatedAmountOut
  SkipHelper->>SkipHelper: call getSlippageTolerancePercent(expectedAmountOut)
  Note right of SkipHelper #dff0d8: compute min(USDC_cap/expectedOut, percent_cap)
  SkipHelper-->>Caller: return messages payload including slippageTolerancePercent
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • shrenujb
  • UnbornAztecKing

Poem

I nibble numbers, choose the gentlest hop,
I trim the slippage so balances don't drop,
I dropped an old log, and added a test,
I compute on the hop to keep things blessed,
A tiny rabbit's codebound hop. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The description includes the required template headings, but the Test Plan section remains a placeholder without any details on how the changes were tested, leaving the description incomplete. Please provide a concrete Test Plan with details on how the slippage tolerance logic and related configuration changes were validated, such as specific test scenarios, commands executed, or manual verification steps.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title clearly identifies the introduction of a safe slippage tolerance feature and focuses on the primary change implemented in the PR, making it easy for a teammate to understand the main update.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch kefan/safe-slippage

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9dd0e2 and da52621.

📒 Files selected for processing (1)
  • indexer/services/comlink/__tests__/helpers/skip-helper.test.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • indexer/services/comlink/tests/helpers/skip-helper.test.ts
⏰ 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). (29)
  • GitHub Check: test / run_command
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: call-build-ecs-service-roundtable / (roundtable) Check docker image build
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: call-build-ecs-service-vulcan / (vulcan) Check docker image build
  • GitHub Check: call-build-ecs-service-socks / (socks) Check docker image build
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: call-build-ecs-service-comlink / (comlink) Check docker image build
  • GitHub Check: call-build-ecs-service-ender / (ender) Check docker image build
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: check-build-auxo
  • GitHub Check: check-build-bazooka
  • GitHub Check: run_command
  • GitHub Check: build-and-push-testnet
  • GitHub Check: lint
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Summary

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
indexer/services/comlink/src/helpers/skip-helper.ts (1)

15-15: Remove unnecessary lodash max import

After the above fixes, max is no longer needed. Keep min or inline native Math.min as desired.

-import { max, min } from 'lodash';
+import { min } from 'lodash';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7be3a40 and 8bf1083.

📒 Files selected for processing (1)
  • indexer/services/comlink/src/helpers/skip-helper.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
indexer/services/comlink/src/helpers/skip-helper.ts (2)
indexer/services/comlink/src/lib/smart-contract-constants.ts (5)
  • usdcAddressByChainId (12-20)
  • dydxChainId (10-10)
  • ethDenomByChainId (22-27)
  • ETH_USDC_QUANTUM (31-31)
  • ETH_WEI_QUANTUM (32-32)
indexer/services/comlink/src/helpers/alchemy-helpers.ts (1)
  • getETHPrice (309-342)
⏰ 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). (29)
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: call-build-ecs-service-socks / (socks) Check docker image build
  • GitHub Check: call-build-ecs-service-roundtable / (roundtable) Check docker image build
  • GitHub Check: call-build-ecs-service-vulcan / (vulcan) Check docker image build
  • GitHub Check: call-build-ecs-service-comlink / (comlink) Check docker image build
  • GitHub Check: check-build-bazooka
  • GitHub Check: call-build-ecs-service-ender / (ender) Check docker image build
  • GitHub Check: check-build-auxo
  • GitHub Check: lint
  • GitHub Check: run_command
  • GitHub Check: test / run_command
  • GitHub Check: build-and-push-testnet
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: Summary
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)

Comment on lines 62 to 64
if (amount.startsWith('0x')) {
amountToUse = parseInt(amount, 16).toString();
}
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 2, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Hex amount parsing loses precision for large values

parseInt on 256-bit hex can overflow Number. Use BigInt for exact conversion.

-  if (amount.startsWith('0x')) {
-    amountToUse = parseInt(amount, 16).toString();
-  }
+  if (amount.startsWith('0x')) {
+    amountToUse = BigInt(amount).toString(); // exact conversion for large values
+  }
📝 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
if (amount.startsWith('0x')) {
amountToUse = parseInt(amount, 16).toString();
}
if (amount.startsWith('0x')) {
amountToUse = BigInt(amount).toString(); // exact conversion for large values
}
🤖 Prompt for AI Agents
In indexer/services/comlink/src/helpers/skip-helper.ts around lines 62 to 64,
the code uses parseInt(amount, 16) which can overflow for 256-bit hex values;
replace parseInt with BigInt by parsing the hex string with BigInt (e.g.
BigInt(amount) or BigInt('0x...')) and then call toString() to produce an exact
decimal string; ensure you handle the optional "0x" prefix consistently and
maintain the same variable assignment to amountToUse.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The amountToUse will have enough precision since we limit the amount beforehand.

Copy link
Contributor

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
# Find all callers of getSkipCallData to verify if limitAmount is called first
rg -nP --type=ts -C10 'getSkipCallData\s*\(' -g '!**/skip-helper.ts' -g '!**/__tests__/**'

Length of output: 2413


Comment on lines 93 to 108
trueEstimateAmountOut = await estimateAmountOutSwap(
routeResult?.sourceAssetChainId,
routeResult?.amountIn,
routeResult?.sourceAssetDenom,
);
toleranceMet = verifySkipEstimation(routeResult.estimatedAmountOut, trueEstimateAmountOut);
// estimatedAmountOut from skip needs to be appropriate.
if (!toleranceMet) {
logger.warning({
at: 'skip-helper#getSkipCallData',
message: 'estimated amount out is less than our tolerance. Retrying...',
estimatedAmountOut: routeResult.estimatedAmountOut,
actualAmount: trueEstimateAmountOut,
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Verify “true” estimate only for ETH and fix logger level; consider backoff

  • Only run market-rate verification for ETH inputs; else use Skip’s estimate.
  • logger.warning → logger.warn.
  • Optional: add jitter/backoff between retries.
-    trueEstimateAmountOut = await estimateAmountOutSwap(
-      routeResult?.sourceAssetChainId,
-      routeResult?.amountIn,
-      routeResult?.sourceAssetDenom,
-    );
-    toleranceMet = verifySkipEstimation(routeResult.estimatedAmountOut, trueEstimateAmountOut);
+    if (sourceAssetDenom.toLowerCase() === ethDenomByChainId[chainId]?.toLowerCase()) {
+      trueEstimateAmountOut = await estimateAmountOutSwap(
+        routeResult.sourceAssetChainId,
+        routeResult.amountIn,
+        routeResult.sourceAssetDenom,
+      );
+    } else {
+      // For non-ETH, defer to Skip's estimate.
+      trueEstimateAmountOut = routeResult.estimatedAmountOut;
+    }
+    toleranceMet = verifySkipEstimation(routeResult.estimatedAmountOut, trueEstimateAmountOut);
@@
-      logger.warning({
+      logger.warn({
         at: 'skip-helper#getSkipCallData',
         message: 'estimated amount out is less than our tolerance. Retrying...',
         estimatedAmountOut: routeResult.estimatedAmountOut,
         actualAmount: trueEstimateAmountOut,
       });

Optional backoff snippet (illustrative, not required):

// await new Promise(r => setTimeout(r, 200 * (i + 1))); // linear backoff
🤖 Prompt for AI Agents
In indexer/services/comlink/src/helpers/skip-helper.ts around lines 93 to 108,
the code currently always recalculates a "true" market-rate estimate and uses
logger.warning; update it to only perform the market-rate verification when the
input asset is ETH (check sourceAssetDenom or chain-specific ETH identifier),
otherwise accept Skip's estimatedAmountOut without re-estimation, change
logger.warning to logger.warn, and (optionally) implement a small jitter/backoff
between retry attempts to avoid tight retry loops.

Comment on lines 146 to 151
const expectedAmountOut = max([routeResult.estimatedAmountOut, trueEstimateAmountOut]);
const slippageTolerancePercent = min([
100 / parseInt(expectedAmountOut!, 10),
parseFloat(config.SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE),
])!.toString();
const response = await messages({
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Numeric max on string amounts and wrong slippage scaling

  • max on strings compares lexicographically → wrong pick.
  • Slippage for $100 anchor must account for USDC quanta and percent scaling.
-  const expectedAmountOut = max([routeResult.estimatedAmountOut, trueEstimateAmountOut]);
-  const slippageTolerancePercent = min([
-    100 / parseInt(expectedAmountOut!, 10),
-    parseFloat(config.SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE),
-  ])!.toString();
+  const estimatedOut = BigInt(routeResult.estimatedAmountOut);
+  const trueOut = BigInt(trueEstimateAmountOut!);
+  const expectedAmountOutBig = estimatedOut > trueOut ? estimatedOut : trueOut;
+  const expectedAmountOut = expectedAmountOutBig.toString();
+
+  // Anchor slippage to max($100, 0.1% of expected)
+  const hundredUsdMicro = BigInt(100) * BigInt(ETH_USDC_QUANTUM);
+  const tenthPercentDelta = expectedAmountOutBig / BigInt(1000); // 0.1%
+  const maxDelta = hundredUsdMicro > tenthPercentDelta ? hundredUsdMicro : tenthPercentDelta;
+  // Convert to percent (be mindful of Number limits; typical ranges are safe)
+  const anchorPercent = Number(maxDelta) / Number(expectedAmountOutBig) * 100;
+  const slippageTolerancePercent = Math.min(
+    anchorPercent,
+    parseFloat(config.SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE),
+  ).toString();
@@
-    amountOut: expectedAmountOut,
+    amountOut: expectedAmountOut,

Also applies to: 154-154

🤖 Prompt for AI Agents
In indexer/services/comlink/src/helpers/skip-helper.ts around lines 146-151
(also applies to line 154), the code is using max/min on string amounts which
performs lexicographic comparison and incorrectly computes slippage because it
ignores token quanta/decimals; convert the string amounts to a numeric/bignumber
type (e.g., BigNumber, bigint, or Decimal) before comparing so you pick the true
numeric max between routeResult.estimatedAmountOut and trueEstimateAmountOut,
then compute slippage scaling using the anchor token's decimals (e.g., scale
expectedAmountOut to token quanta: expectedAmountOutScaled = expectedAmountOut *
10^anchorDecimals) and derive slippageTolerancePercent as the smaller of
configured percent and (100 * tokenQuanta / expectedAmountOutScaled) or
equivalent formula in the chosen numeric type; ensure all operations use the
same numeric type and handle parse errors/nulls consistently.

Comment on lines 431 to 441
chainId: string,
amount: string,
sourceAssetDenom: string,
): Promise<string> {
if (sourceAssetDenom === ethDenomByChainId[chainId]) {
const ethPrice = await getETHPrice();
// ethPrice multiplied by the amount will give us the rough amount in usdc.
return (ethPrice * parseInt(amount, 10) * (ETH_USDC_QUANTUM / ETH_WEI_QUANTUM)).toString();
}
return (parseInt(amount, 10) * ETH_USDC_QUANTUM).toString();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

estimateAmountOutSwap: unit bug (non‑ETH) and float precision; return integer micro‑USDC

  • For ETH, compute USDC micro using BigInt with price in micro‑USD.
  • For USDC inputs, return amount as-is (already micro‑USDC).
  • Avoid parseInt/float math to prevent precision loss.
 export async function estimateAmountOutSwap(
   chainId: string,
   amount: string,
   sourceAssetDenom: string,
 ): Promise<string> {
   if (sourceAssetDenom === ethDenomByChainId[chainId]) {
-    const ethPrice = await getETHPrice();
-    // ethPrice multiplied by the amount will give us the rough amount in usdc.
-    return (ethPrice * parseInt(amount, 10) * (ETH_USDC_QUANTUM / ETH_WEI_QUANTUM)).toString();
+    const ethPrice = await getETHPrice();
+    const amountWei = BigInt(amount);
+    const priceMicroUsd = BigInt(Math.floor(ethPrice * 1_000_000)); // $ in micro units
+    // (wei * microUSD_per_ETH) / wei_per_ETH => micro‑USDC (integer)
+    const outMicroUsdc = (amountWei * priceMicroUsd) / BigInt(ETH_WEI_QUANTUM);
+    return outMicroUsdc.toString();
   }
-  return (parseInt(amount, 10) * ETH_USDC_QUANTUM).toString();
+  // If the input is already USDC on source chain, amount is micro‑USDC
+  if (
+    Object.values(usdcAddressByChainId).some(
+      (x) => x.toLowerCase() === sourceAssetDenom.toLowerCase(),
+    )
+  ) {
+    return amount;
+  }
+  // Fallback: do not scale unknown assets; caller should gate usage to ETH.
+  return amount;
 }
📝 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
chainId: string,
amount: string,
sourceAssetDenom: string,
): Promise<string> {
if (sourceAssetDenom === ethDenomByChainId[chainId]) {
const ethPrice = await getETHPrice();
// ethPrice multiplied by the amount will give us the rough amount in usdc.
return (ethPrice * parseInt(amount, 10) * (ETH_USDC_QUANTUM / ETH_WEI_QUANTUM)).toString();
}
return (parseInt(amount, 10) * ETH_USDC_QUANTUM).toString();
}
export async function estimateAmountOutSwap(
chainId: string,
amount: string,
sourceAssetDenom: string,
): Promise<string> {
if (sourceAssetDenom === ethDenomByChainId[chainId]) {
const ethPrice = await getETHPrice();
const amountWei = BigInt(amount);
const priceMicroUsd = BigInt(Math.floor(ethPrice * 1_000_000)); // $ in micro units
// (wei * microUSD_per_ETH) / wei_per_ETH => micro-USDC (integer)
const outMicroUsdc = (amountWei * priceMicroUsd) / BigInt(ETH_WEI_QUANTUM);
return outMicroUsdc.toString();
}
// If the input is already USDC on source chain, amount is micro-USDC
if (
Object.values(usdcAddressByChainId).some(
(x) => x.toLowerCase() === sourceAssetDenom.toLowerCase(),
)
) {
return amount;
}
// Fallback: do not scale unknown assets; caller should gate usage to ETH.
return amount;
}
🤖 Prompt for AI Agents
indexer/services/comlink/src/helpers/skip-helper.ts around lines 431 to 441: the
current implementation uses parseInt and floating math which causes precision
loss and incorrectly handles non‑ETH (USDC) inputs; change logic to use BigInt
throughout and price expressed in micro‑USD (integer) so all math is
integer-only, for ETH compute micro‑USDC as BigInt(amountInWei) *
BigInt(ethPriceInMicroUSD) * BigInt(ETH_USDC_QUANTUM) / BigInt(ETH_WEI_QUANTUM)
and for USDC (sourceAssetDenom === USDC denom) simply return the input amount
unchanged (already micro‑USDC); remove parseInt/float operations and ensure the
function returns a stringified BigInt result.

Comment on lines 450 to 455
export function verifySkipEstimation(skipEstimateAmount: string, actualAmount: string): boolean {
const skipEstimateAmountNum = parseInt(skipEstimateAmount, 10);
const actualAmountNum = parseInt(actualAmount, 10);
const anchor = max([actualAmountNum * (1 - 100 / actualAmountNum), 0.999 * actualAmountNum]);
return skipEstimateAmountNum >= anchor!;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

verifySkipEstimation: $100 anchor not scaled; float math and lodash max

Use BigInt and scale $100 to micro‑USDC. Allow max($100, 0.1% of amount).

-export function verifySkipEstimation(skipEstimateAmount: string, actualAmount: string): boolean {
-  const skipEstimateAmountNum = parseInt(skipEstimateAmount, 10);
-  const actualAmountNum = parseInt(actualAmount, 10);
-  const anchor = max([actualAmountNum * (1 - 100 / actualAmountNum), 0.999 * actualAmountNum]);
-  return skipEstimateAmountNum >= anchor!;
-}
+export function verifySkipEstimation(skipEstimateAmount: string, actualAmount: string): boolean {
+  const skip = BigInt(skipEstimateAmount);
+  const actual = BigInt(actualAmount);
+  if (actual === BigInt(0)) return false;
+  const hundredUsd = BigInt(100) * BigInt(ETH_USDC_QUANTUM); // $100 in micro‑USDC
+  const tenthPercent = actual / BigInt(1000); // 0.1%
+  const maxDelta = hundredUsd > tenthPercent ? hundredUsd : tenthPercent;
+  const anchor = actual > maxDelta ? actual - maxDelta : BigInt(0);
+  return skip >= anchor;
+}
📝 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
export function verifySkipEstimation(skipEstimateAmount: string, actualAmount: string): boolean {
const skipEstimateAmountNum = parseInt(skipEstimateAmount, 10);
const actualAmountNum = parseInt(actualAmount, 10);
const anchor = max([actualAmountNum * (1 - 100 / actualAmountNum), 0.999 * actualAmountNum]);
return skipEstimateAmountNum >= anchor!;
}
export function verifySkipEstimation(skipEstimateAmount: string, actualAmount: string): boolean {
const skip = BigInt(skipEstimateAmount);
const actual = BigInt(actualAmount);
if (actual === BigInt(0)) return false;
// $100 in micro-USDC
const hundredUsd = BigInt(100) * BigInt(ETH_USDC_QUANTUM);
// 0.1% of the actual amount
const tenthPercent = actual / BigInt(1000);
// pick the larger delta
const maxDelta = hundredUsd > tenthPercent ? hundredUsd : tenthPercent;
// anchor = actual minus that delta, floor at zero
const anchor = actual > maxDelta ? actual - maxDelta : BigInt(0);
return skip >= anchor;
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
indexer/services/comlink/src/helpers/skip-helper.ts (1)

392-400: Consider BigInt for consistency and handle null assertion safely.

While limitAmount ensures amounts stay within safe bounds, using BigInt would be more robust and consistent with other numeric operations in the codebase (e.g., lines 364, 374, 389).

Additionally, the ! null assertion on min() could fail if both values are NaN or undefined. Add a safety check:

-export function getSlippageTolerancePercent(estAmountOut: string): string {
-  return min([
-    (100 * (config.SKIP_SLIPPAGE_TOLERANCE_USDC * ETH_USDC_QUANTUM)) / parseInt(estAmountOut, 10),
-    parseFloat(config.SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE),
-  ])!.toString();
-}
+export function getSlippageTolerancePercent(estAmountOut: string): string {
+  const estAmountOutNum = BigInt(estAmountOut);
+  const anchorPercent = Number(
+    (BigInt(100) * BigInt(config.SKIP_SLIPPAGE_TOLERANCE_USDC) * BigInt(ETH_USDC_QUANTUM)) / estAmountOutNum
+  );
+  const result = min([
+    anchorPercent,
+    parseFloat(config.SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE),
+  ]);
+  if (result === undefined) {
+    throw new Error('Failed to calculate slippage tolerance');
+  }
+  return result.toString();
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 596d466 and a9dd0e2.

📒 Files selected for processing (2)
  • indexer/services/comlink/__tests__/helpers/skip-helper.test.ts (3 hunks)
  • indexer/services/comlink/src/helpers/skip-helper.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • indexer/services/comlink/tests/helpers/skip-helper.test.ts
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Kefancao
PR: dydxprotocol/v4-chain#3118
File: indexer/services/comlink/__tests__/helpers/skip-helper.test.ts:9-32
Timestamp: 2025-10-15T15:26:24.856Z
Learning: In the skip-helper slippage tolerance implementation for dYdX ETH swaps, `min()` is intentionally used to select the smaller (tighter) slippage tolerance percentage between `SKIP_SLIPPAGE_TOLERANCE_USDC * ETH_USDC_QUANTUM / estimatedAmountOut` and `SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE`. This ensures maximum protection by choosing the most conservative slippage bound, as smaller slippage tolerance means less potential loss.
📚 Learning: 2025-10-15T15:26:24.856Z
Learnt from: Kefancao
PR: dydxprotocol/v4-chain#3118
File: indexer/services/comlink/__tests__/helpers/skip-helper.test.ts:9-32
Timestamp: 2025-10-15T15:26:24.856Z
Learning: In the skip-helper slippage tolerance implementation for dYdX ETH swaps, `min()` is intentionally used to select the smaller (tighter) slippage tolerance percentage between `SKIP_SLIPPAGE_TOLERANCE_USDC * ETH_USDC_QUANTUM / estimatedAmountOut` and `SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE`. This ensures maximum protection by choosing the most conservative slippage bound, as smaller slippage tolerance means less potential loss.

Applied to files:

  • indexer/services/comlink/src/helpers/skip-helper.ts
🧬 Code graph analysis (1)
indexer/services/comlink/src/helpers/skip-helper.ts (1)
indexer/services/comlink/src/lib/smart-contract-constants.ts (1)
  • ETH_USDC_QUANTUM (31-31)
⏰ 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). (29)
  • GitHub Check: call-build-ecs-service-vulcan / (vulcan) Check docker image build
  • GitHub Check: build-and-push-mainnet
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: call-build-ecs-service-roundtable / (roundtable) Check docker image build
  • GitHub Check: call-build-ecs-service-socks / (socks) Check docker image build
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: call-build-ecs-service-comlink / (comlink) Check docker image build
  • GitHub Check: call-build-ecs-service-ender / (ender) Check docker image build
  • GitHub Check: check-build-bazooka
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-auxo-lambda / (auxo) Build and Push Lambda
  • GitHub Check: (Public Testnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: check-build-auxo
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-vulcan / (vulcan) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-bazooka-lambda / (bazooka) Build and Push Lambda
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-roundtable / (roundtable) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-socks / (socks) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-ender / (ender) Build and Push
  • GitHub Check: (Mainnet) Build and Push ECS Services / call-build-and-push-ecs-service-comlink / (comlink) Build and Push
  • GitHub Check: test / run_command
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: build-and-push-testnet
  • GitHub Check: Analyze (go)
  • GitHub Check: run_command
  • GitHub Check: lint
  • GitHub Check: Summary

Comment on lines +115 to +117
// acceptable slippage is smallest of SKIP_SLIPPAGE_TOLERANCE_USDC (Default $100) divided
// by the estimatedAmountOut or the SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE.
const slippageTolerancePercent = getSlippageTolerancePercent(routeResult.estimatedAmountOut);
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Clarify: Implementation differs from PR specification.

The comment describes the current implementation (min($100/amount, maxPercent)), but the PR description states slippage should be "anchored to max($100, 0.001 * amount)", which translates to:

slippageDelta = max($100_in_micro_USDC, 0.001 * estimatedAmountOut)
slippagePercent = (slippageDelta / estimatedAmountOut) * 100

For a $200,000 amount:

  • Current implementation: min($100/$200k, maxPercent) = ~0.05%
  • PR specification: max($100, $200)/$200k = 0.1%

These produce different results. Based on learnings, min() was chosen intentionally for "maximum protection," but this contradicts the stated PR objective. Please either update the PR description to match the implementation, or update the implementation to match the specification.

Based on learnings.


🏁 Script executed:

#!/bin/bash
set -e
rg -n "function getSlippageTolerancePercent" -C5 indexer/services/comlink/src/helpers/skip-helper.ts

Length of output: 520


Align slippage tolerance formula with PR specification

getSlippageTolerancePercent (lines 395–400) currently computes

min(
  SKIP_SLIPPAGE_TOLERANCE_USDC/estimatedAmountOut,
  SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE
)

but the PR description specifies

max(SKIP_SLIPPAGE_TOLERANCE_USDC, 0.001 * estimatedAmountOut) / estimatedAmountOut

Please update the implementation or the PR description to ensure they match the intended formula.

🤖 Prompt for AI Agents
In indexer/services/comlink/src/helpers/skip-helper.ts around lines 115 to 117,
the slippage tolerance calculation uses getSlippageTolerancePercent which
currently returns min(SKIP_SLIPPAGE_TOLERANCE_USDC/estimatedAmountOut,
SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE) but the PR intends
max(SKIP_SLIPPAGE_TOLERANCE_USDC, 0.001 * estimatedAmountOut) /
estimatedAmountOut; update getSlippageTolerancePercent to compute
max(SKIP_SLIPPAGE_TOLERANCE_USDC, 0.001 * estimatedAmountOut) divided by
estimatedAmountOut (and clamp to 0..SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE if
needed), or if the original min-based behavior is desired, update the PR
description to match the implemented formula.

TURNKEY_API_SENDER_PUBLIC_KEY: 'test-public-key',
TURNKEY_API_SENDER_PRIVATE_KEY: 'test-private-key',
SKIP_SLIPPAGE_TOLERANCE_USDC: 100,
SKIP_SLIPPAGE_TOLERANCE_PERCENTAGE: '0.1', // 10%
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this 10% or 10bps? 10% seems like a lot?

APPROVAL_ENABLED: parseBoolean({ default: true }),
// largest amount we will tolerate to swap in usdc.
MAXIMUM_BRIDGE_AMOUNT_USDC: parseInteger({ default: 99_900 }),
MAXIMUM_BRIDGE_AMOUNT_USDC: parseInteger({ default: 999_000 }),
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we increasing this?

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

Labels

Development

Successfully merging this pull request may close these issues.

3 participants