Skip to content

Enable RBTC swaps using Changelly floating point API#767

Merged
kvhnuke merged 1 commit intoenkryptcom:devop/package-updates-2-14from
ahsan-javaiid:rbtc-swaps
Nov 5, 2025
Merged

Enable RBTC swaps using Changelly floating point API#767
kvhnuke merged 1 commit intoenkryptcom:devop/package-updates-2-14from
ahsan-javaiid:rbtc-swaps

Conversation

@ahsan-javaiid
Copy link
Contributor

@ahsan-javaiid ahsan-javaiid commented Oct 21, 2025

Description

This PR enables RBTC swaps using changelly floating point api.
API Reference: https://docs.changelly.com/category/floating-rate-transactions

Video Demo

rbtc-transfer.mp4

Demo screenshots

Screenshot 2025-10-22 at 12 24 49 AM Screenshot 2025-10-22 at 12 24 34 AM

Testing Status

Tested RBTC swaps between ETH and Rootstock.

Summary by CodeRabbit

  • Improvements
    • RBTC now supports floating-rate swaps, expanding available swap paths and pricing options.
    • Min/max amount calculations and quote behavior now respect provided amounts and floating-rate dynamics for more accurate estimates.
    • Quote and transaction flows route correctly for RBTC floating-rate swaps, with clearer messaging and consistent handling of responses.

@coderabbitai
Copy link

coderabbitai bot commented Oct 21, 2025

Walkthrough

Change updates the Changelly swap provider to accept an optional amount in getMinMaxAmount, route RBTC pairs through floating-rate RPCs (getExchangeAmount / createTransaction) when applicable, and propagate amountFrom (derived from amount) into relevant RPC requests and response handling.

Changes

Cohort / File(s) Summary
Changelly provider core
packages/swap/src/providers/changelly/index.ts
Add optional amount?: string to getMinMaxAmount signature; compute and pass amountFrom when invoking floating-rate RPCs; switch RPC selection between getFixRate / getFixRateForAmount / createFixTransaction and getExchangeAmount / createTransaction based on RBTC presence; adjust request payloads, response id/amount handling, and error messages to reflect chosen RPC method.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ChangellyProvider
    participant ChangellyRPC

    Client->>ChangellyProvider: getQuote(fromToken, toToken, amount)
    alt RBTC involved (floating-rate)
        ChangellyProvider->>ChangellyRPC: getExchangeAmount(params with amountFrom)
        ChangellyRPC-->>ChangellyProvider: exchangeAmountResponse (id, amount)
        ChangellyProvider->>ChangellyRPC: createTransaction(params with amountFrom)
        ChangellyRPC-->>ChangellyProvider: transactionResponse (id)
    else Fixed-rate path
        ChangellyProvider->>ChangellyRPC: getFixRateForAmount(params)
        ChangellyRPC-->>ChangellyProvider: fixRateResponse (id, amount)
        ChangellyProvider->>ChangellyRPC: createFixTransaction(params)
        ChangellyRPC-->>ChangellyProvider: fixTransactionResponse (id)
    end
    ChangellyProvider-->>Client: normalizedQuote / transactionResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Enable RBTC swaps using Changelly floating point API" directly and accurately describes the main change in the changeset. The modifications across the Changelly provider specifically focus on enabling RBTC (Rootstock Bitcoin) tokens to be used with Changelly's floating-rate API, which matches the title's core message. The title is concise, clear, uses specific terms (RBTC, Changelly, floating point API) rather than vague language, and avoids noise. A developer scanning the commit history would immediately understand that this PR adds RBTC swap support via Changelly's floating-rate transactions.
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

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

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/swap/src/providers/changelly/index.ts (1)

510-517: Avoid accessing response.error.* when there is no error (can throw).

On the “no quotes” branch, response.error can be undefined. Guard it to prevent runtime exceptions.

-          `  code=${String(response.error.code)}` +
-          `  message=${String(response.error.message)}`,
+          `  code=${String(response.error?.code ?? "N/A")}` +
+          `  message=${String(response.error?.message ?? "N/A")}`,
🧹 Nitpick comments (2)
packages/swap/src/providers/changelly/index.ts (2)

312-327: Method selection, deprecations, and logging fixes.

  • getFixRate is deprecated in v2; prefer getFixRateForAmount for fixed rates. (docs.changelly.com)
  • For floating, getExchangeAmount is correct; it can include limits (min/max) when amountFrom is within limits. If out of limits, consider a fallback (temporary) to deprecated getMinAmount or retry heuristics. (docs.changelly.com)
  • Normalize ticker case when checking for RBTC.
  • Hoist method so the catch can log the actual method.
@@
-    try {
-      const params: ChangellyApiGetFixRateParams & { amountFrom?: string } = {
+    // Default to fixed-rate method; may be overridden below.
+    let method: "getFixRate" | "getExchangeAmount" = "getFixRate";
+    try {
+      const params: ChangellyApiGetFixRateParams & { amountFrom?: string } = {
         // Optional amountFrom field only used in getExchangeAmount rpc call for floating rates
         from: this.getTicker(fromToken, this.network),
         to: this.getTicker(
           toToken as TokenType,
           toToken.networkInfo.name as SupportedNetworkName,
         ),
       };
 
-      let method: string = "getFixRate";
-      if ([params.from, params.to].includes("rbtc")) {
+      // Use floating rate for RBTC pairs
+      if ([params.from, params.to].map((t) => t?.toLowerCase()).includes("rbtc")) {
         // In case of RBTC use floating rate
         method = "getExchangeAmount";
         params.amountFrom = options.amount; // amountFrom required only in case of getExchangeAmount
       }
-      const response =
-        await this.changellyRequest<ChangellyApiGetFixRateResult>(
-          method,
-          params,
-          { signal },
-        );
+      const response =
+        await this.changellyRequest<
+          ChangellyApiGetFixRateResult | ChangellyApiGetFixRateForAmountResult
+        >(method, params, { signal });
@@
-      console.warn(
-        `Errored calling Changelly JSONRPC HTTP API "getFixRate"` +
+      console.warn(
+        `Errored calling Changelly JSONRPC HTTP API "${method}"` +
           `  fromToken=${fromToken.symbol}` +
           `  toToken=${toToken.symbol}` +
           `  took=${(Date.now() - startedAt).toLocaleString()}ms` +
           `  err=${String(err)}`,
       );

Note: getFixRate is deprecated; switching to getFixRateForAmount aligns with v2 guidance. (docs.changelly.com)

Also applies to: 365-371


321-326: Normalize RBTC ticker check to lowercase to avoid case‑sensitivity bugs.

Use .toLowerCase() when checking for "rbtc" in both places.

-      if ([params.from, params.to].includes("rbtc")) {
+      if ([params.from, params.to].map((t) => t?.toLowerCase()).includes("rbtc")) {
@@
-      if ([params.from, params.to].includes("rbtc")) {
+      if ([params.from, params.to].map((t) => t?.toLowerCase()).includes("rbtc")) {

Also applies to: 480-482

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1bd8c09 and 2037129.

📒 Files selected for processing (1)
  • packages/swap/src/providers/changelly/index.ts (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/swap/src/providers/changelly/index.ts (4)
packages/swap/src/types/index.ts (2)
  • TokenType (49-60)
  • TokenTypeTo (78-80)
packages/swap/src/providers/changelly/types.ts (4)
  • ChangellyApiGetFixRateParams (138-152)
  • ChangellyApiGetFixRateResult (159-258)
  • ChangellyApiGetFixRateForAmountResult (326-437)
  • ChangellyApiCreateFixedRateTransactionResult (558-686)
packages/utils/src/index.ts (1)
  • fromBase (49-49)
packages/utils/src/units.ts (1)
  • fromBase (159-159)
🔇 Additional comments (1)
packages/swap/src/providers/changelly/index.ts (1)

296-299: Rewrite as: No action required — this is a safe, provider-specific change.

The method getMinMaxAmount() is not defined in the abstract ProviderClass interface (packages/swap/src/types/index.ts:296–319), so the signature change in Changelly is provider-internal, not a breaking public API change. All invocations within Changelly correctly pass the optional amount parameter, and no external code outside the provider implementations calls this method.

Likely an incorrect or invalid review comment.

@ahsan-javaiid ahsan-javaiid force-pushed the rbtc-swaps branch 2 times, most recently from 8152a38 to 159f039 Compare October 21, 2025 19:54
Copy link

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/swap/src/providers/changelly/index.ts (2)

296-327: Min/max cannot come from getExchangeAmount; use fixed-rate endpoint (or dedicated min/max).

getMinMaxAmount switches to getExchangeAmount for RBTC, but later reads minFrom/maxFrom/minTo/maxTo which aren’t provided by getExchangeAmount. This will break RBTC quotes (min/max become undefined → toBase/toBN errors).

Apply:

-      let method: string = "getFixRate";
-      if ([params.from, params.to].includes("rbtc")) {
-        // In case of RBTC use floating rate
-        method = "getExchangeAmount";
-        params.amountFrom = options.amount; // amountFrom required only in case of getExchangeAmount
-      }
+      const method = "getFixRate"; // min/max are exposed by this endpoint even if swap flow is floating

Also fix the catch log to reflect the actual method:

-        `Errored calling Changelly JSONRPC HTTP API "getFixRate"` +
+        `Errored calling Changelly JSONRPC HTTP API "${method}"` +

Also applies to: 347-355, 365-371


510-517: Crash risk: logging references response.error when there is no error.

In the “no quotes” branch, response.error is undefined, so response.error.code/message will throw.

-      if (!response.result || !response.result[0]?.id) {
+      if (!response.result || !response.result[0]?.id) {
         console.warn(
-          `Changelly "${method}" response contains no quotes,` +
-            ` returning no quotes` +
-            `  fromToken=${options.fromToken.symbol} (${params.from})` +
-            `  toToken=${options.toToken.symbol} (${params.to})` +
-            `  took=${(Date.now() - startedAt).toLocaleString()}ms` +
-            `  code=${String(response.error.code)}` +
-            `  message=${String(response.error.message)}`,
+          `Changelly "${method}" response contains no quotes,` +
+            ` returning no quotes` +
+            `  fromToken=${options.fromToken.symbol} (${params.from})` +
+            `  toToken=${options.toToken.symbol} (${params.to})` +
+            `  took=${(Date.now() - startedAt).toLocaleString()}ms`,
         );
         return null;
       }
♻️ Duplicate comments (4)
packages/swap/src/providers/changelly/index.ts (4)

611-621: Only fixed‑rate quotes have a rateId; don’t set changellyQuoteId for floating.

-          meta: {
-            ...meta,
-            changellyQuoteId: firstChangellyFixRateQuote.id,
-            changellynetworkFee: toBN(networkFeeBase),
-          },
+          meta: {
+            ...meta,
+            // rateId present only for fixed quotes
+            changellyQuoteId: method === "getFixRateForAmount" ? firstChangellyFixRateQuote.id : undefined,
+            changellynetworkFee: toBN(networkFeeBase),
+          },

680-697: Build swap params by flow; omit rateId for floating and include fromAddress.

-      const params: ChangellyApiCreateFixedRateTransactionParams = {
-        from: this.getTicker(quote.options.fromToken, this.network),
-        to: this.getTicker(
-          quote.options.toToken as TokenType,
-          quote.options.toToken.networkInfo.name as SupportedNetworkName,
-        ),
-        refundAddress: quote.options.fromAddress,
-        address: quote.options.toAddress,
-        amountFrom: fromBase(
-          quote.options.amount.toString(),
-          quote.options.fromToken.decimals,
-        ),
-        rateId: quote.meta.changellyQuoteId,
-      };
-      let method: string = "createFixTransaction";
-      if ([params.from, params.to].includes("rbtc")) {
-        method = "createTransaction"; // floating rate tx
-      }
+      const fromTicker = this.getTicker(quote.options.fromToken, this.network);
+      const toTicker = this.getTicker(
+        quote.options.toToken as TokenType,
+        quote.options.toToken.networkInfo.name as SupportedNetworkName,
+      );
+      const isFloating = [fromTicker, toTicker].some((t) => t?.toLowerCase() === "rbtc");
+      const baseParams = {
+        from: fromTicker,
+        to: toTicker,
+        refundAddress: quote.options.fromAddress,
+        address: quote.options.toAddress,
+        amountFrom: fromBase(
+          quote.options.amount.toString(),
+          quote.options.fromToken.decimals,
+        ),
+      } as any;
+      const params: any = isFloating
+        ? { ...baseParams, fromAddress: quote.options.fromAddress }
+        : { ...baseParams, rateId: quote.meta.changellyQuoteId };
+      const method: "createFixTransaction" | "createTransaction" =
+        isFloating ? "createTransaction" : "createFixTransaction";

145-152: Guard against missing token in RBTC branch (runtime NPE).

cur.protocol === "RBTC" path doesn’t require cur.token, but below you read cur.token.address. Add cur.token to the guard.

Apply:

-      if (
-        (cur.enabledTo && cur.fixRateEnabled && cur.token) ||
-        cur.protocol === "RBTC"
-      ) {
+      if (
+        cur.token &&
+        ((cur.enabledTo && cur.fixRateEnabled) || cur.protocol === "RBTC")
+      ) {

504-507: Don’t treat JSON‑RPC id as a quote/rate id. Remove override.

-      if (response.result[0] && response.id) {
-        // getExchangeAmount returns id in response object
-        response.result[0].id = String(response.id);
-      }
🧹 Nitpick comments (5)
packages/swap/src/providers/changelly/index.ts (5)

479-482: RBTC flow switch OK; make the check case-insensitive.

-      let method: string = "getFixRateForAmount";
-      if ([params.from, params.to].includes("rbtc")) {
+      let method: string = "getFixRateForAmount";
+      if ([params.from, params.to].some((t) => t?.toLowerCase() === "rbtc")) {
         method = "getExchangeAmount";
       }

521-530: Use dynamic method name in log.

-          `Changelly "getFixRateForAmount" returned more than one quote, continuing with first quote` +
+          `Changelly "${method}" returned more than one quote, continuing with first quote` +

633-636: Make success log reflect the actual endpoint.

-        `getQuote: Successfully retrieved quote from Changelly via "getFixRateForAmount"` +
+        `getQuote: Successfully retrieved quote from Changelly via "${method}"` +

989-994: Use dynamic method name in error log for amountExpectedTo parsing.

-          `Changelly "createFixTransaction" "amountExpectedTo" possibly returned more` +
+          `Changelly "${method}" "amountExpectedTo" possibly returned more` +

479-482: Normalize RBTC checks in all places.

Use .toLowerCase() wherever you check for RBTC to avoid case mismatches across lists and tickers.

Also applies to: 695-697

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2037129 and 159f039.

📒 Files selected for processing (1)
  • packages/swap/src/providers/changelly/index.ts (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/swap/src/providers/changelly/index.ts (3)
packages/swap/src/types/index.ts (2)
  • TokenType (49-60)
  • TokenTypeTo (78-80)
packages/swap/src/providers/changelly/types.ts (4)
  • ChangellyApiGetFixRateParams (138-152)
  • ChangellyApiGetFixRateResult (159-258)
  • ChangellyApiGetFixRateForAmountResult (326-437)
  • ChangellyApiCreateFixedRateTransactionResult (558-686)
packages/utils/src/index.ts (1)
  • fromBase (49-49)
🔇 Additional comments (2)
packages/swap/src/providers/changelly/index.ts (2)

436-444: Passing normalized amount for min/max context is fine.

Confirm that getMinMaxAmount still behaves correctly now that it ignores amount for min/max when using getFixRate.


297-297: Review comment can be resolved — no external call sites are affected.

The verification confirms that getMinMaxAmount() is not part of the abstract ProviderClass contract (absent from type definitions), so signature variations across providers are acceptable. The only call site is internal to Changelly (line 440) and already passes the options parameter, making it compatible with the new optional amount field. No external code depends on this method.

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/swap/src/providers/changelly/index.ts (1)

679-699: Build swap params per flow; omit rateId for floating and use transaction’s networkFee for final amount.

  • Floating createTransaction requires amountFrom and may include fromAddress; it must not include rateId. Fixed createFixTransaction requires rateId. (docs.changelly.com)
  • Use the networkFee returned by the create‑transaction response (not the earlier quote meta) when computing toTokenAmount, as fees can change between estimate and creation. (docs.changelly.com)

Apply:

@@
-      const params: ChangellyApiCreateFixedRateTransactionParams = {
-        from: this.getTicker(quote.options.fromToken, this.network),
-        to: this.getTicker(
-          quote.options.toToken as TokenType,
-          quote.options.toToken.networkInfo.name as SupportedNetworkName,
-        ),
-        refundAddress: quote.options.fromAddress,
-        address: quote.options.toAddress,
-        amountFrom: fromBase(
-          quote.options.amount.toString(),
-          quote.options.fromToken.decimals,
-        ),
-        rateId: quote.meta.changellyQuoteId,
-      };
-      
-      let method: string = "createFixTransaction";
-      if ([params.from, params.to].includes("rbtc")) {
-        method = "createTransaction"; // floating rate tx
-      }
+      const fromTicker = this.getTicker(quote.options.fromToken, this.network);
+      const toTicker = this.getTicker(
+        quote.options.toToken as TokenType,
+        quote.options.toToken.networkInfo.name as SupportedNetworkName,
+      );
+      const isFloating = [fromTicker, toTicker]
+        .some((t) => t?.toLowerCase() === "rbtc");
+      const baseParams = {
+        from: fromTicker,
+        to: toTicker,
+        refundAddress: quote.options.fromAddress,
+        address: quote.options.toAddress,
+        amountFrom: fromBase(
+          quote.options.amount.toString(),
+          quote.options.fromToken.decimals,
+        ),
+      } as any;
+      const params: any = isFloating
+        ? { ...baseParams, fromAddress: quote.options.fromAddress }
+        : { ...baseParams, rateId: quote.meta.changellyQuoteId };
+      const method: "createTransaction" | "createFixTransaction" =
+        isFloating ? "createTransaction" : "createFixTransaction";
@@
-      const fee = 1;
-      let baseToAmount: string;
+      const fee = 1;
+      let baseToAmount: string;
@@
-      const retResponse: ProviderSwapResponse = {
+      // Use the network fee returned by the create*Transaction response
+      const txNetworkFeeBase = toBase(
+        changellyFixedRateTx.networkFee,
+        quote.options.toToken.decimals,
+      );
+      const retResponse: ProviderSwapResponse = {
         fromTokenAmount: quote.options.amount,
         provider: this.name,
         type: SwapType.regular,
-        toTokenAmount: toBN(baseToAmount).sub(quote.meta.changellynetworkFee),
+        toTokenAmount: toBN(baseToAmount).sub(toBN(txNetworkFeeBase)),
         additionalNativeFees,
         transactions: [transaction],
         slippage: quote.meta.slippage || DEFAULT_SLIPPAGE,
         fee,

Also applies to: 701-704, 980-1016, 1017-1026

♻️ Duplicate comments (1)
packages/swap/src/providers/changelly/index.ts (1)

145-161: RBTC guard fix in toTokens looks good.

Condition now requires cur.token when allowing RBTC as a destination; avoids NPEs when accessing cur.token.address.

🧹 Nitpick comments (2)
packages/swap/src/providers/changelly/index.ts (2)

296-326: Make RBTC detection case‑insensitive and use the selected method consistently in logs.

  • Use lowercase when checking for "rbtc" to avoid case brittleness.
  • Reuse the selected method in the catch log (currently hardcoded "getFixRate"), by hoisting method outside try.

Apply:

@@
-  async getMinMaxAmount(
-    options: { fromToken: TokenType; toToken: TokenTypeTo; amount?: string },
+  async getMinMaxAmount(
+    options: { fromToken: TokenType; toToken: TokenTypeTo; amount?: string },
     context?: { signal?: AbortSignal },
   ): Promise<MinMaxResponse> {
@@
-    try {
-      const params: ChangellyApiGetFixRateParams & { amountFrom?: string } = {
+    let method: "getFixRate" | "getExchangeAmount" = "getFixRate";
+    try {
+      const params: ChangellyApiGetFixRateParams & { amountFrom?: string } = {
         // Optional amountFrom field only used in getExchangeAmount rpc call for floating rates
         from: this.getTicker(fromToken, this.network),
         to: this.getTicker(
           toToken as TokenType,
           toToken.networkInfo.name as SupportedNetworkName,
         ),
       };
 
-      let method: string = "getFixRate";
-      if ([params.from, params.to].includes("rbtc")) {
+      const isFloating = [params.from, params.to]
+        .some((t) => t?.toLowerCase() === "rbtc");
+      if (isFloating) {
         // In case of RBTC use floating rate
         method = "getExchangeAmount";
-        params.amountFrom = options.amount; // amountFrom required only in case of getExchangeAmount
+        params.amountFrom = options.amount; // required by getExchangeAmount
       }
@@
-      console.warn(
-        `Errored calling Changelly JSONRPC HTTP API "getFixRate"` +
+      console.warn(
+        `Errored calling Changelly JSONRPC HTTP API "${method}"` +
         `  fromToken=${fromToken.symbol}` +
         `  toToken=${toToken.symbol}` +
         `  took=${(Date.now() - startedAt).toLocaleString()}ms` +
         `  err=${String(err)}`,
       );

Also applies to: 333-341, 364-371


333-341: Optional: parse floating error to derive limits or retry with safe amount.

If getExchangeAmount returns “Invalid amount … minimal/maximal amount is …” you currently return zeros, which suppresses quotes even when a valid clamp is possible. Consider:

  • Parsing the error to extract {limit} and populate min/max, or
  • Fallback to a second getExchangeAmount with a safe probe amount inside limits, or
  • As a last resort, hit the deprecated limits endpoints to derive min/max.

Ref: limits guidance and deprecations. (docs.changelly.com)

Would you like a follow‑up patch that parses the error and populates min/max automatically?

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 159f039 and 3d13b84.

📒 Files selected for processing (1)
  • packages/swap/src/providers/changelly/index.ts (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/swap/src/providers/changelly/index.ts (4)
packages/swap/src/types/index.ts (2)
  • TokenType (49-60)
  • TokenTypeTo (78-80)
packages/swap/src/providers/changelly/types.ts (3)
  • ChangellyApiGetFixRateParams (138-152)
  • ChangellyApiGetFixRateForAmountResult (326-437)
  • ChangellyApiCreateFixedRateTransactionResult (558-686)
packages/utils/src/index.ts (1)
  • fromBase (49-49)
packages/utils/src/units.ts (1)
  • fromBase (159-159)

Comment on lines +479 to 486
let method: string = "getFixRateForAmount";
if ([params.from, params.to].includes("rbtc")) {
method = "getExchangeAmount";
}
const response =
await this.changellyRequest<ChangellyApiGetFixRateForAmountResult>(
"getFixRateForAmount",
method,
params,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Don’t inject JSON‑RPC id as a rate id; make quote flow conditional by method.

  • JSON‑RPC id is just a client correlation id; it is not a quote/rate id. Remove the mutation of response.result[0].id = response.id. (docs.changelly.com)
  • Floating estimates (getExchangeAmount) don’t return/use rateId; fixed quotes do. Only require id and propagate changellyQuoteId for the fixed flow (getFixRateForAmount). (docs.changelly.com)
  • Make the RBTC check case‑insensitive and update hardcoded log strings to use the actual method.

Apply:

@@
-      let method: string = "getFixRateForAmount";
-      if ([params.from, params.to].includes("rbtc")) {
-        method = "getExchangeAmount";
-      }
+      const isFloating = [params.from, params.to]
+        .some((t) => t?.toLowerCase() === "rbtc");
+      const method: "getFixRateForAmount" | "getExchangeAmount" =
+        isFloating ? "getExchangeAmount" : "getFixRateForAmount";
@@
-      if (response.error) {
+      if (response.error) {
         console.warn(
-          `Changelly "${method}" returned JSONRPC error response,` +
+          `Changelly "${method}" returned JSONRPC error response,` +
           ` returning no quotes` +
@@
-      if (response.result[0] && response.id) {
-        // getExchangeAmount returns id in response object
-        response.result[0].id = String(response.id);
-      }
-      if (!response.result || !response.result[0]?.id) {
+      if (
+        !response.result ||
+        !response.result[0] ||
+        (method === "getFixRateForAmount" && !response.result[0].id)
+      ) {
         console.warn(
-          `Changelly "${method}" response contains no quotes,` +
+          `Changelly "${method}" response contains no quotes,` +
           ` returning no quotes` +
@@
-        console.warn(
-          `Changelly "getFixRateForAmount" returned more than one quote, continuing with first quote` +
+        console.warn(
+          `Changelly "${method}" returned more than one quote, continuing with first quote` +
           `  fromToken=${options.fromToken.symbol} (${params.from})` +
           `  toToken=${options.toToken.symbol} (${params.to})` +
           `  took=${(Date.now() - startedAt).toLocaleString()}ms` +
           `  count=${response.result.length}ms`,
         );
@@
-        console.warn(
-          `Changelly "getFixRateForAmount" "amountTo" possibly returned more` +
+        console.warn(
+          `Changelly "${method}" "amountTo" possibly returned more` +
           ` decimals than the token has, attempting to trim trailing decimals...` +
@@
-        console.warn(
-          `Changelly "getFixRateForAmount" "networkFee" possibly returned more` +
+        console.warn(
+          `Changelly "${method}" "networkFee" possibly returned more` +
           ` decimals than the token has, attempting to trim trailing decimals...` +
@@
-          meta: {
-            ...meta,
-            changellyQuoteId: firstChangellyFixRateQuote.id,
-            changellynetworkFee: toBN(networkFeeBase),
-          },
+          meta: {
+            ...meta,
+            // Only fixed-rate quotes have a usable rateId
+            changellyQuoteId: method === "getFixRateForAmount"
+              ? firstChangellyFixRateQuote.id
+              : undefined,
+            changellynetworkFee: toBN(networkFeeBase),
+          },

This matches the API: floating uses getExchangeAmount (no rateId), fixed uses getFixRateForAmount (has id for rateId). Also, always show user estimate net of networkFee (you already do). (docs.changelly.com)

Also applies to: 504-507, 508-519, 524-530, 549-556, 585-591, 611-621

🤖 Prompt for AI Agents
In packages/swap/src/providers/changelly/index.ts around lines 479-486 (also
apply same fix patterns at 504-507, 508-519, 524-530, 549-556, 585-591,
611-621): remove the code that mutates response.result[0].id = response.id
(JSON‑RPC id must not be used as a quote id); only set/propagate
changellyQuoteId when the chosen method is "getFixRateForAmount" and use the id
supplied in the API result (e.g., response.result[0].id) if present — do not use
response.id; make the RBTC check case‑insensitive (compare params.from/params.to
after toLowerCase()), and update any hardcoded log messages to include the
actual method variable so logs show which API ("getExchangeAmount" vs
"getFixRateForAmount") was called; ensure the floating flow
("getExchangeAmount") does not expect or require a rateId.

@kvhnuke kvhnuke changed the base branch from develop to devop/package-updates-2-14 November 5, 2025 21:25
@kvhnuke kvhnuke merged commit 5602f3b into enkryptcom:devop/package-updates-2-14 Nov 5, 2025
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments