Skip to content

Conversation

fadeev
Copy link
Member

@fadeev fadeev commented Sep 24, 2025

Removing types from Toolkit, importing types from @zetachain/sdk-cosmos.

Summary by CodeRabbit

  • New Features

    • None
  • Bug Fixes

    • More reliable Sui RPC selection by using numeric chain IDs.
    • Prevented occasional crashes in CCTX tracking when fields are missing.
    • Improved accuracy in fee, quote, and balance handling across chains.
    • Consistent token/chain display names and coin-type detection.
  • Refactor

    • Migrated to Zetachain SDK types for chains, coins, fees, and CCTX.
    • Standardized ID comparisons (string/bigint) and VM/coin-type normalization.
  • Chores

    • Added @zetachain/sdk-cosmos dependency.
  • Tests

    • Updated test suites to align with new SDK-based types.

Copy link
Contributor

coderabbitai bot commented Sep 24, 2025

📝 Walkthrough

Walkthrough

Migrates numerous modules from local/types to Zetachain SDK “SDKType” models, updates comparisons to enum-to-JSON helpers, and normalizes numeric/string/bigint handling. Adds @zetachain/sdk-cosmos dependency. Adjusts utilities, queries, commands, and tests to new types; minor logic tweaks (string coercions, RPC lookups, VM checks).

Changes

Cohort / File(s) Summary
Dependencies
package.json
Added dependency: @zetachain/[email protected].
Client: chains/fees/balances
packages/client/src/getBalances.ts, packages/client/src/getFees.ts, packages/client/src/getForeignCoins.ts, packages/client/src/getQuote.ts, packages/client/src/getSupportedChains.ts
Switched to SDK types (ChainSDKType, ForeignCoinsSDKType, Query*SDKType); replaced string literals with enum JSON helpers; normalized chain_id comparisons and number/string casts; updated response typing.
Commands: CCTX querying
packages/commands/src/query/cctx.ts
Replaced CrossChainTx with CrossChainTxSDKType; status/coin type checks via JSON helpers; added guards for missing fields; adjusted events/response types; safe base64 handling.
Commands: chains views
packages/commands/src/query/chains/list.ts, packages/commands/src/query/chains/show.ts
Migrated to ChainSDKType/ChainParamsSDKType/ForeignCoinsSDKType; updated field derivations (RPC, confirmations, tokens); normalized IDs to string/number as needed.
Commands: tokens views
packages/commands/src/query/tokens/list.ts, packages/commands/src/query/tokens/show.ts
Switched ForeignCoin to ForeignCoinsSDKType across fetch/format/find; coerced numeric fields to strings for display.
Commands: Sui flows
packages/commands/src/sui/deposit.ts, packages/commands/src/sui/depositAndCall.ts
getSuiRpcByChainId now receives Number(chainId).
Tasks: tokens table
packages/tasks/src/tokens.ts
Uses chain.name instead of chain.chain_name for display.
App query: fees
src/query/fees.ts
Response typing to QueryAllForeignCoinsResponseSDKType; foreign_chain_id stringified in results.
Utils: API
utils/api.ts
getCctxByHash/getPendingNoncesForTss return SDKType-based types; fetch generics updated to Query*SDKType.
Utils: balances & token mapping
utils/balances.ts
Adopted SDK types for chains/coins; coin/VM checks via coinTypeToJSON/vmToJSON; normalized chain_id handling; adjusted ZETA/WZETA additions; updated enrich logic.
Utils: Sui RPC
utils/sui.ts
getSuiRpcByChainId now accepts number; internal indexing via String(chainId).
Utils: transactions/CCTX tracking
utils/transactions.ts, utils/trackCCTX.ts
Pending nonces types to PendingNoncesSDKType[]; string coercions for IDs/status; matching by stringified chain IDs.
Types: migration/removals
types/balances.types.ts, types/chains.types.ts, types/foreignCoins.types.ts, types/getFees.types.ts, types/supportedChains.types.ts, types/trackCCTX.types.ts
Public types moved to SDKType equivalents; removed local interfaces (ForeignCoin, ObserverSupportedChain, etc.); widened chain_id in Token/TokenBalance to include bigint.
Tests: updated to SDK types
test/balances.test.ts, test/trackCCTXUtils.test.ts, test/transactions.test.ts, test/uniswap.test.ts
Replaced local types with SDKType variants; adjusted literals to BigInt/enums; updated imports and expectations.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as CLI (cctx query)
  participant API as Observer API
  participant SDK as SDK Type Helpers

  User->>CLI: query cctx --hash <txHash>
  CLI->>API: GET /cctx/<txHash>
  API-->>CLI: QueryGetCctxResponseSDKType
  CLI->>SDK: cctxStatusToJSON(status)
  SDK-->>CLI: "Pending" | "OutboundMined" | "Reverted" | ...
  alt Missing inbound_params or cctx_status
    CLI->>User: No details available
  else Status known
    CLI->>User: Emit formatted CCTX (SDKType)
  end

  loop Poll pending until terminal
    CLI->>API: GET /pending-nonces/<tss>
    API-->>CLI: PendingNoncesSDKType[]
    CLI->>CLI: Match by String(chain_id)/nonce ranges
    CLI->>User: Update events/status
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

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 title clearly indicates the main change—refactoring to use types from the sdk-cosmos npm package—matching the extensive updates in imports and type usage throughout the codebase. It is concise, specific, and directly reflects the primary intent without unnecessary detail.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-sdk-cosmos-types

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.

@fadeev fadeev marked this pull request as ready for review September 25, 2025 09:22
@fadeev fadeev requested review from a team as code owners September 25, 2025 09:22
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: 10

Caution

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

⚠️ Outside diff range comments (10)
src/query/fees.ts (1)

2-16: Use the camelCase response type or normalize snake_case fields
Replace the generic QueryAllForeignCoinsResponseSDKType (defines foreign_coins) with the non-SDK type QueryAllForeignCoinsResponse (which uses foreignCoins), or explicitly map response.data.foreign_coinsforeignCoins before use.

packages/commands/src/query/cctx.ts (5)

131-146: Guard outbound_params and fix snake_case field to prevent crashes

outbound_params is used without existence/length checks, and receiver_chainId should be receiver_chain_id for SDKType.

Apply:

   } = cctx;
-  if (!inbound_params || !cctx_status) return "";
+  if (!inbound_params || !cctx_status || !outbound_params?.length) return "";
 
   const { sender_chain_id, sender, amount, coin_type } = inbound_params;
-  const { receiver_chainId, receiver } = outbound_params[0];
+  const firstOutbound = outbound_params[0];
+  const { receiver_chain_id, receiver } = firstOutbound;
   const { status, status_message, error_message = "" } = cctx_status;

169-177: Render human-readable status string

status appears to be enum numeric; print JSON name for readability.

-  } else {
-    mainStatus = status;
-  }
+  } else {
+    mainStatus = cctxStatusToJSON(status);
+  }

179-183: Fix outbound hash rendering (guarded access + snake_case)

Prevents undefined output and aligns with SDKType field names.

-  const outboundHash =
-    outbound_params[0].hash === ""
-      ? `Waiting for a transaction on chain ${receiver_chainId}...`
-      : `${outbound_params[0].hash} (on chain ${receiver_chainId})`;
+  const outboundHash =
+    firstOutbound.hash === ""
+      ? `Waiting for a transaction on chain ${receiver_chain_id}...`
+      : `${firstOutbound.hash} (on chain ${receiver_chain_id})`;

241-246: Fix receiver chain field names in revert/abort details

Use SDKType snake_case for both hops.

-    if (isReverted || isPendingRevert) {
-      chainDetails = `${receiver_chainId} → ${outbound_params[1].receiver_chainId} ${statusIcon} ${statusMessage}`;
+    if (isReverted || isPendingRevert) {
+      chainDetails = `${receiver_chain_id} → ${outbound_params[1].receiver_chain_id} ${statusIcon} ${statusMessage}`;

257-259: Fix snake_case in Tx Hash chain rendering

Align with SDKType.

-      revertOrAbortTx += `Tx Hash:          ${outbound_params[1].hash} (on chain ${outbound_params[1].receiver_chainId})
+      revertOrAbortTx += `Tx Hash:          ${outbound_params[1].hash} (on chain ${outbound_params[1].receiver_chain_id})
utils/trackCCTX.ts (1)

201-205: Normalize gateway response before storing in state.

getPendingNoncesForTss returns the raw SDK-type payload with bigint fields. Persisting the response verbatim means serializing bigint values into state, which will break JSON serialization in consumers (and Jest snapshots). We should normalize them (e.g., convert to string) right after retrieval, matching the pre-migration behaviour.

utils/transactions.ts (1)

49-57: Don’t stringify possibly undefined fields; this breaks status-change detection.

String(...) turns undefined into "undefined", so statusDefined becomes true and diffing logic misfires.

Apply this diff:

   const tx: CCTX = {
     confirmed_on_destination: false,
     outbound_tx_hash: cctx.outbound_params[0].hash,
     outbound_tx_tss_nonce: Number(cctx.outbound_params[0].tss_nonce),
-    receiver_chainId: String(cctx.outbound_params[0].receiver_chainId),
-    sender_chain_id: String(cctx?.inbound_params?.sender_chain_id),
-    status: String(cctx?.cctx_status?.status),
-    status_message: String(cctx?.cctx_status?.status_message),
+    receiver_chainId: String(cctx.outbound_params[0].receiver_chainId),
+    sender_chain_id: cctx?.inbound_params?.sender_chain_id != null
+      ? String(cctx.inbound_params.sender_chain_id)
+      : undefined,
+    status: cctx?.cctx_status?.status,
+    status_message: cctx?.cctx_status?.status_message,
   };

This preserves undefined and keeps your diffing logic correct.

utils/balances.ts (2)

92-123: Fix VM matching against SDK enums

vmToJSON(Vm.evm) returns labels like "VM_EVM", while supportedChain.vm on ChainSDKType is a numeric enum value (e.g., 1). Comparing "1" against "VM_EVM" always fails, so we never add the native ERC20/SPL/SUI tokens. Stringifying both sides doesn’t help—one side stays numeric. Convert supportedChain.vm with vmToJSON before the comparison (or compare the numeric enums directly) so EVM/SVM/SUI tokens are restored.

Apply this diff to restore correct matching:

-      if (String(supportedChain?.vm) === vmToJSON(Vm.evm)) {
+      if (supportedChain && vmToJSON(supportedChain.vm) === vmToJSON(Vm.evm)) {
         const evmToken: Token = {
           chain_id: String(foreignCoin.foreign_chain_id),
           coin_type: "ERC20",
           contract: foreignCoin.asset,
           decimals: foreignCoin.decimals,
           symbol: foreignCoin.symbol,
           zrc20: foreignCoin.zrc20_contract_address,
         };
         return [evmToken, zrc20Token];
-      } else if (String(supportedChain?.vm) === vmToJSON(Vm.svm)) {
+      } else if (supportedChain && vmToJSON(supportedChain.vm) === vmToJSON(Vm.svm)) {
         const svmToken: Token = {
           chain_id: String(foreignCoin.foreign_chain_id),
           coin_type: "SPL",
           contract: foreignCoin.asset,
           decimals: foreignCoin.decimals,
           symbol: foreignCoin.symbol,
           zrc20: foreignCoin.zrc20_contract_address,
         };
         return [svmToken, zrc20Token];
-      } else if (String(supportedChain?.vm) === vmToJSON(Vm.mvm_sui)) {
+      } else if (supportedChain && vmToJSON(supportedChain.vm) === vmToJSON(Vm.mvm_sui)) {
         const svmToken: Token = {
           chain_id: String(foreignCoin.foreign_chain_id),
           coin_type: "SUI",
           contract: foreignCoin.asset,
           decimals: foreignCoin.decimals,
           symbol: foreignCoin.symbol,
           zrc20: foreignCoin.zrc20_contract_address,
         };
         return [svmToken, zrc20Token];
       }

195-216: Handle BigInt chain IDs when enriching tokens

ChainSDKType.chain_id is a bigint, while token.chain_id is stored as a string. Comparing them directly leaves chain_name undefined, so every token gets filtered out. Convert the chain ID you compare against to a string (or coerce both sides to bigint) before matching.

Use string comparison to retain tokens:

-      const chain_name = supportedChains.find(
-        (c) => c.chain_id === token.chain_id
-      )?.name;
+      const chain_name = supportedChains.find(
+        (c) => String(c.chain_id) === String(token.chain_id)
+      )?.name;
🧹 Nitpick comments (15)
utils/uniswap.ts (1)

4-4: Import as type to avoid extraneous runtime import

Use a type-only import for ForeignCoinsSDKType.

Apply:

-import { ForeignCoinsSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/fungible/foreign_coins";
+import type { ForeignCoinsSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/fungible/foreign_coins";
packages/client/src/getBalances.ts (1)

1-5: Prefer type-only import for ChainSDKType

Avoids bringing in modules at runtime unnecessarily.

Apply:

-import {
-  ChainSDKType,
-  Vm,
-  vmToJSON,
-} from "@zetachain/sdk-cosmos/zetachain/zetacore/pkg/chains/chains";
+import type { ChainSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/pkg/chains/chains";
+import { Vm, vmToJSON } from "@zetachain/sdk-cosmos/zetachain/zetacore/pkg/chains/chains";
packages/tasks/src/tokens.ts (1)

26-26: Robust fallback for chain display name

Prefer nullish-coalescing to support both SDKType name and chain_name during transition.

-    const name = chain ? chain.name : "Unsupported Chain";
+    const name = chain?.name ?? chain?.chain_name ?? "Unsupported Chain";
packages/commands/src/sui/deposit.ts (1)

14-21: Compute numeric chainId once and reuse (optional)

Minor cleanup; also consider passing number to suiDeposit if its options expect numeric chainId.

 const main = async (options: DepositOptions) => {
   const keypair = getKeypair(options);
+  const chainIdNum = Number(options.chainId);
   const isConfirmed = await confirmTransaction({
     amount: options.amount,
     receiver: options.receiver,
-    rpc: getSuiRpcByChainId(Number(options.chainId)),
+    rpc: getSuiRpcByChainId(chainIdNum),
     sender: keypair.toSuiAddress(),
   });
@@
-    {
-      chainId: options.chainId,
+    {
+      chainId: chainIdNum,
       gasLimit: options.gasBudget,

Confirm suiDeposit expects a number for chainId; if it requires string, skip the second change.

Also applies to: 30-31

types/balances.types.ts (1)

5-8: Widening chain_id to bigint: audit JSON/formatting paths

BigInt cannot be JSON.stringified and breaks string ops (e.g., concatenation, localeCompare). Ensure downstream code normalizes chain_id to string at display/boundary layers (parseTokenId, enrichTokens, table sort).

Option: introduce a ChainId = string | bigint alias and add a normalizeChainId(value: ChainId): string helper, using it wherever IDs are printed/compared/sorted.

Also applies to: 18-21

test/balances.test.ts (1)

97-109: Align test types consistently and drop ts-nocheck

You switched one case to ChainSDKType[] but still use ObserverSupportedChain elsewhere under @ts-nocheck. Prefer importing ChainSDKType and updating all occurrences; then remove ts-nocheck.

Example import:

import type { ChainSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/observer/chain";
test/transactions.test.ts (1)

130-131: Prefer runtime-shape mocks over SDK types in tests.

The tests only need the minimal shape consumed by updateEmitter. Importing and instantiating PendingNoncesSDKType brings in the protobuf-specific bigint structure, forcing us to mock BigInts manually. Consider reverting to a local helper/mock shape (or narrowing the type via Pick) to keep the test data lightweight and focused.

packages/commands/src/query/chains/list.ts (1)

42-47: Avoid anonymous response shapes; import the SDK query response type.

Hard-coding { chain_params: { chain_params: ChainParamsSDKType[] } } is brittle. Prefer the official response type from the SDK (e.g., QueryGetChainParamsResponseSDKType) if available.

If that type exists, update:

-import { ChainParamsSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/observer/params";
+import { ChainParamsSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/observer/params";
+import type { QueryGetChainParamsResponseSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/observer/query";

-  const [chainsData, tokensData, chainParamsData] = await Promise.all([
+  const [chainsData, tokensData, chainParamsData] = await Promise.all([
     /* ... */
-    fetchFromApi<{
-      chain_params: {
-        chain_params: ChainParamsSDKType[];
-      };
-    }>(api, "/zeta-chain/observer/get_chain_params"),
+    fetchFromApi<QueryGetChainParamsResponseSDKType>(
+      api,
+      "/zeta-chain/observer/get_chain_params"
+    ),
   ]);

If the exact type name differs, please pick the correct one from the SDK’s observer/query module.

utils/transactions.ts (1)

100-105: Prevent integer precision loss when computing queue size.

Number(...) on 64-bit nonces can overflow. Use BigInt arithmetic and stringify for display.

Apply this diff:

-  if (pendingNonce) {
-    const pending = pendingNonce.nonce_low || "0";
-    const current = tx.outbound_tx_tss_nonce;
-    const diff = current - Number(pending);
-    queue = diff > 0 ? ` (${diff} in queue)` : "";
-  }
+  if (pendingNonce) {
+    const pendingBI = BigInt(pendingNonce.nonce_low ?? "0");
+    const currentBI = BigInt(tx.outbound_tx_tss_nonce ?? 0);
+    const diffBI = currentBI - pendingBI;
+    queue = diffBI > 0n ? ` (${diffBI.toString()} in queue)` : "";
+  }
packages/client/src/getFees.ts (1)

98-102: Fix misleading error context.

This handler is for CCM fees, not CCTX by inbound hash.

Apply this diff:

-    handleError({
-      context: "Something failed fetching CCTX By Inbound hash",
-      error,
-    });
+    handleError({
+      context: "Something went wrong fetching CCM fees",
+      error,
+    });
packages/commands/src/query/tokens/list.ts (1)

65-69: Safe numeric sort.

Using parseInt(String(...)) is fine here. If you ever receive non-numeric IDs, add a fallback comparator to keep a stable order.

utils/api.ts (1)

93-107: Consider server-side filtering for scalability.

If the endpoint supports it, fetch with a ?tss=${tss} filter to avoid pulling all nonces and filtering client-side.

Apply if supported:

-    const data = await fetchFromApi<QueryAllPendingNoncesResponseSDKType>(
-      api,
-      `/zeta-chain/observer/pendingNonces`
-    );
-    return data.pending_nonces.filter((n) => n.tss === tss);
+    const data = await fetchFromApi<QueryAllPendingNoncesResponseSDKType>(
+      api,
+      `/zeta-chain/observer/pendingNonces?tss=${encodeURIComponent(tss)}`
+    );
+    return data.pending_nonces;
packages/commands/src/query/tokens/show.ts (2)

25-36: Render human-readable coin type.

Currently String(token.coin_type) prints the enum’s numeric value. Prefer mapping to the enum key for UX.

Apply this diff:

-    ["Type", String(token.coin_type)],
+    ["Type", CoinType[Number(token.coin_type)] ?? String(token.coin_type)],

Add the enum import at the top:

import { CoinType } from "@zetachain/sdk-cosmos/zetachain/zetacore/pkg/coin/coin";

41-77: Optional: Support friendly field aliases.

To match the chains command UX, consider accepting aliases like chain_id/chain-id by normalizing field before lookup.

packages/commands/src/query/chains/show.ts (1)

47-56: RPC URL helpers: consistent normalization.

Casting once improves clarity and avoids accidental type drift.

Apply:

-  if (isSolanaChainId(String(chain.chain_id))) {
-    return getAPIbyChainId(String(chain.chain_id));
+  const idStr = String(chain.chain_id);
+  if (isSolanaChainId(idStr)) {
+    return getAPIbyChainId(idStr);
   }
-  if (isSuiChainId(String(chain.chain_id))) {
-    return getSuiRpcByChainId(Number(chain.chain_id));
+  if (isSuiChainId(idStr)) {
+    return getSuiRpcByChainId(Number(idStr));
   }
📜 Review details

Configuration used: Path: .coderabbit.yaml

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.

📥 Commits

Reviewing files that changed from the base of the PR and between 26abfda and e83da2b.

⛔ Files ignored due to path filters (24)
  • typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/@openzeppelin/contracts/token/ERC20/utils/SafeERC20__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/@openzeppelin/contracts/utils/Address__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/@zetachain/protocol-contracts/contracts/evm/ERC20Custody__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/@zetachain/protocol-contracts/contracts/evm/GatewayEVM__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/@zetachain/protocol-contracts/contracts/evm/ZetaConnectorNative__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/@zetachain/protocol-contracts/contracts/evm/ZetaConnectorNonNative__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/@zetachain/protocol-contracts/contracts/zevm/GatewayZEVM__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/EthZetaMock.sol/ZetaEthMock__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/SwapHelpers.sol/SwapLibrary__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/TestZRC20__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/shared/MockZRC20__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/EVMSetup.t.sol/EVMSetup__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/FoundrySetup.t.sol/FoundrySetup__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/TokenSetup.t.sol/TokenSetup__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/UniswapV2SetupLib.sol/UniswapV2SetupLib__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/UniswapV3SetupLib.sol/UniswapV3SetupLib__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/ZetaSetup.t.sol/ZetaSetup__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/mock/ERC20Mock__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/mockGateway/NodeLogicMock__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/mockGateway/WrapGatewayEVM__factory.ts is excluded by !typechain-types/**
  • typechain-types/factories/contracts/testing/mockGateway/WrapGatewayZEVM__factory.ts is excluded by !typechain-types/**
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (31)
  • package.json (2 hunks)
  • packages/client/src/getBalances.ts (2 hunks)
  • packages/client/src/getFees.ts (4 hunks)
  • packages/client/src/getForeignCoins.ts (1 hunks)
  • packages/client/src/getQuote.ts (2 hunks)
  • packages/client/src/getSupportedChains.ts (1 hunks)
  • packages/commands/src/query/cctx.ts (10 hunks)
  • packages/commands/src/query/chains/list.ts (4 hunks)
  • packages/commands/src/query/chains/show.ts (9 hunks)
  • packages/commands/src/query/tokens/list.ts (4 hunks)
  • packages/commands/src/query/tokens/show.ts (3 hunks)
  • packages/commands/src/sui/deposit.ts (1 hunks)
  • packages/commands/src/sui/depositAndCall.ts (1 hunks)
  • packages/tasks/src/tokens.ts (1 hunks)
  • src/query/fees.ts (3 hunks)
  • test/balances.test.ts (1 hunks)
  • test/trackCCTXUtils.test.ts (2 hunks)
  • test/transactions.test.ts (4 hunks)
  • test/uniswap.test.ts (3 hunks)
  • types/balances.types.ts (2 hunks)
  • types/chains.types.ts (2 hunks)
  • types/foreignCoins.types.ts (0 hunks)
  • types/getFees.types.ts (0 hunks)
  • types/supportedChains.types.ts (0 hunks)
  • types/trackCCTX.types.ts (0 hunks)
  • utils/api.ts (3 hunks)
  • utils/balances.ts (7 hunks)
  • utils/sui.ts (1 hunks)
  • utils/trackCCTX.ts (3 hunks)
  • utils/transactions.ts (4 hunks)
  • utils/uniswap.ts (2 hunks)
💤 Files with no reviewable changes (4)
  • types/getFees.types.ts
  • types/supportedChains.types.ts
  • types/foreignCoins.types.ts
  • types/trackCCTX.types.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: fadeev
PR: zeta-chain/toolkit#346
File: packages/commands/src/ton/depositAndCall.ts:0-0
Timestamp: 2025-06-13T15:33:54.781Z
Learning: fadeev prefers responses in English.
Learnt from: fadeev
PR: zeta-chain/toolkit#346
File: packages/commands/src/ton/deposit.ts:0-0
Timestamp: 2025-06-13T15:32:09.225Z
Learning: User fadeev prefers that responses be in English; avoid replying in Russian in future interactions.
🧬 Code graph analysis (8)
packages/client/src/getForeignCoins.ts (1)
packages/client/src/client.ts (1)
  • ZetaChainClient (100-293)
packages/commands/src/sui/depositAndCall.ts (1)
utils/sui.ts (1)
  • getSuiRpcByChainId (289-293)
packages/commands/src/sui/deposit.ts (1)
utils/sui.ts (1)
  • getSuiRpcByChainId (289-293)
packages/commands/src/query/chains/list.ts (2)
utils/api.ts (1)
  • fetchFromApi (15-33)
types/chains.types.ts (2)
  • ChainTokenMap (5-7)
  • ChainConfirmationMap (9-11)
utils/balances.ts (1)
types/balances.types.ts (1)
  • Token (18-27)
packages/client/src/getBalances.ts (2)
utils/balances.ts (1)
  • prepareMulticallContexts (223-260)
utils/chains.ts (1)
  • getRpcUrl (3-15)
packages/commands/src/query/chains/show.ts (2)
utils/solana.commands.helpers.ts (1)
  • getAPIbyChainId (140-148)
utils/sui.ts (1)
  • getSuiRpcByChainId (289-293)
packages/commands/src/query/cctx.ts (1)
src/schemas/commands/cctx.ts (1)
  • cctxOptionsSchema (5-10)
🔇 Additional comments (22)
packages/client/src/getQuote.ts (2)

5-5: Verify SDK import path stability for CoinType

Ensure this path exists in the targeted sdk-cosmos version you plan to publish/use. Some versions differ in generated paths.


82-84: Good: robust type-safe gas token detection

Switching to CoinType.Gas and normalizing foreign_chain_id to string improves correctness and clarity.

src/query/fees.ts (1)

87-87: LGTM: chain_id normalized to string

String(contract.foreign_chain_id) avoids type ambiguity and ensures stable sorting.

utils/uniswap.ts (1)

242-242: Type update looks correct

Signature now aligns with SDKType usage across the codebase.

packages/client/src/getBalances.ts (3)

83-83: LGTM: explicit ChainSDKType in predicate

Keeps typings tight while searching by chain name.


87-87: LGTM: Number() on chain_id

Number(chain.chain_id) is appropriate here for viem chain lookup.


75-78: Normalize VM comparison with vmToJSON
At packages/client/src/getBalances.ts:75-78 replace

String(
  supportedChains.find(c => c.name === token.chain_name)?.vm
) === vmToJSON(Vm.evm)

with

(() => {
  const vmVal = supportedChains.find(c => c.name === token.chain_name)?.vm as Vm;
  return vmVal === Vm.evm || vmToJSON(vmVal) === vmToJSON(Vm.evm);
})()

Ensure evmTokens is non-empty at runtime for known EVM chains.

package.json (1)

153-153: No action needed: @zetachain/[email protected] is published npm registry shows 0.0.7 as the latest version.

packages/commands/src/query/cctx.ts (2)

96-113: Endpoint contract check: querying index via inboundHashToCctxData

You add tx.index to the frontier but keep calling /inboundHashToCctxData/. Confirm the endpoint accepts indexes; otherwise this will retry 404s forever.


1-9: No action needed: @zetachain/[email protected] (latest) is pinned in package.json

packages/commands/src/sui/deposit.ts (1)

19-19: LGTM: numeric chainId for RPC lookup

Casting to number matches getSuiRpcByChainId signature.

types/chains.types.ts (1)

1-17: LGTM: central types now consistently use SDKType variants.

The switch to ForeignCoinsSDKType, ChainParamsSDKType, and ChainSDKType aligns with the rest of the refactor.

packages/client/src/getFees.ts (1)

80-96: SDKType JSON field mismatch in CCM fees response

  • QueryConvertGasToZetaResponseSDKType defines snake_case fields; accessing data.outboundGasInZeta/data.protocolFeeInZeta will be undefined.
  • Either use snake_case keys (data.outbound_gas_in_zeta, data.protocol_fee_in_zeta) or switch to the non-SDK response type to keep camelCase.
  • Confirm which JSON shape your REST gateway returns and standardize across all clients.
packages/commands/src/query/chains/list.ts (1)

34-41: Use foreign_coins instead of foreignCoins.
In packages/commands/src/query/chains/list.ts, replace:

-    tokens: tokensData.foreignCoins,
+    tokens: tokensData.foreign_coins,

Also apply the same change to the block at lines 49–52.

test/trackCCTXUtils.test.ts (2)

163-170: LGTM on SDKType migration and BigInt usage.

Using PendingNoncesSDKType[] and BigInt fields matches the SDKType shape; test intent is clear.


1-1: Verified export and version
@zetachain/sdk-cosmos v0.0.7 is published and the package exports PendingNoncesSDKType from zetachain/zetacore/observer/pending_nonces.

test/uniswap.test.ts (3)

1-2: LGTM: enum/type imports align with SDKType migration.


69-76: BigInt and enum values look correct.

Using CoinType.ERC20 and BigInt for foreign_chain_id/gas_limit is consistent.

Also applies to: 84-88


101-119: Good coverage of address case-insensitivity and unknown tokens.

The tests exercise core edge cases for formatPoolsWithTokenDetails.

Also applies to: 128-148, 183-209

utils/api.ts (2)

1-3: Note: Package/version availability check.

If package.json pins @zetachain/[email protected], verify it exists publicly; otherwise CI will break. See earlier comment for a script.


39-47: Type return refactor looks correct.

Returning QueryGetCctxResponseSDKType["CrossChainTx"] | undefined keeps the surface aligned with SDK types.

packages/commands/src/query/chains/show.ts (1)

261-265: Search by chain ID: good normalization.

Using String(c.chain_id) === searchValue is correct post-migration.

Comment on lines 154 to 158
if (
status === "OutboundMined" ||
(status === "PendingOutbound" && outbound_params[0].hash !== "")
String(status) === cctxStatusToJSON(CctxStatus.OutboundMined) ||
(String(status) === cctxStatusToJSON(CctxStatus.PendingOutbound) &&
outbound_params[0].hash !== "")
) {
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

Use firstOutbound.hash after guarding array access

Avoid direct indexing; rely on the guarded alias.

-    (String(status) === cctxStatusToJSON(CctxStatus.PendingOutbound) &&
-      outbound_params[0].hash !== "")
+    (String(status) === cctxStatusToJSON(CctxStatus.PendingOutbound) &&
+      firstOutbound.hash !== "")
📝 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 (
status === "OutboundMined" ||
(status === "PendingOutbound" && outbound_params[0].hash !== "")
String(status) === cctxStatusToJSON(CctxStatus.OutboundMined) ||
(String(status) === cctxStatusToJSON(CctxStatus.PendingOutbound) &&
outbound_params[0].hash !== "")
) {
if (
String(status) === cctxStatusToJSON(CctxStatus.OutboundMined) ||
(String(status) === cctxStatusToJSON(CctxStatus.PendingOutbound) &&
firstOutbound.hash !== "")
) {
🤖 Prompt for AI Agents
In packages/commands/src/query/cctx.ts around lines 154 to 158, the condition
directly indexes outbound_params[0].hash after only partially guarding array
access; change it to first verify outbound_params.length > 0, create a guarded
alias (e.g. const firstOutbound = outbound_params[0]) and then use
firstOutbound.hash in the conditional, or rewrite the condition to check
outbound_params.length > 0 && firstOutbound.hash !== "" so array access is
always safe.

Comment on lines +196 to 198
if (String(coin_type) !== coinTypeToJSON(CoinType.NoAssetCall)) {
mainTx += `Amount: ${amount} ${coin_type} tokens\n`;
}
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

Fix coin type comparison and display

String(coin_type) will not match JSON names; use coinTypeToJSON consistently.

-  if (String(coin_type) !== coinTypeToJSON(CoinType.NoAssetCall)) {
-    mainTx += `Amount:   ${amount} ${coin_type} tokens\n`;
-  }
+  if (coinTypeToJSON(coin_type) !== coinTypeToJSON(CoinType.NoAssetCall)) {
+    mainTx += `Amount:   ${amount} ${coinTypeToJSON(coin_type)} tokens\n`;
+  }
📝 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 (String(coin_type) !== coinTypeToJSON(CoinType.NoAssetCall)) {
mainTx += `Amount: ${amount} ${coin_type} tokens\n`;
}
if (coinTypeToJSON(coin_type) !== coinTypeToJSON(CoinType.NoAssetCall)) {
mainTx += `Amount: ${amount} ${coinTypeToJSON(coin_type)} tokens\n`;
}
🤖 Prompt for AI Agents
In packages/commands/src/query/cctx.ts around lines 196 to 198, the code
compares String(coin_type) to coinTypeToJSON(CoinType.NoAssetCall) and prints
coin_type directly; change the comparison to use coinTypeToJSON(coin_type) !==
coinTypeToJSON(CoinType.NoAssetCall) and when building the display line use
coinTypeToJSON(coin_type) (or a mapped readable token name) instead of
String(coin_type), so both the equality check and the printed token name use the
same JSON name format.

Comment on lines 214 to 221
outbound_params[1] &&
["Reverted", "PendingRevert", "Aborted"].includes(status)
["Reverted", "PendingRevert", "Aborted"].includes(String(status))
) {
const isReverted = status === "Reverted";
const isPendingRevert = status === "PendingRevert";
const isAborted = status === "Aborted";
const isReverted = String(status) === cctxStatusToJSON(CctxStatus.Reverted);
const isPendingRevert =
String(status) === cctxStatusToJSON(CctxStatus.PendingRevert);
const isAborted = String(status) === cctxStatusToJSON(CctxStatus.Aborted);

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

Revert/abort block never executes due to wrong status includes

String(status) won’t equal "Reverted"/"PendingRevert"/"Aborted". Compute flags via cctxStatusToJSON and gate on them.

-  if (
-    outbound_params[1] &&
-    ["Reverted", "PendingRevert", "Aborted"].includes(String(status))
-  ) {
-    const isReverted = String(status) === cctxStatusToJSON(CctxStatus.Reverted);
-    const isPendingRevert =
-      String(status) === cctxStatusToJSON(CctxStatus.PendingRevert);
-    const isAborted = String(status) === cctxStatusToJSON(CctxStatus.Aborted);
+  const isReverted = cctxStatusToJSON(status) === cctxStatusToJSON(CctxStatus.Reverted);
+  const isPendingRevert = cctxStatusToJSON(status) === cctxStatusToJSON(CctxStatus.PendingRevert);
+  const isAborted = cctxStatusToJSON(status) === cctxStatusToJSON(CctxStatus.Aborted);
+  if (outbound_params[1] && (isReverted || isPendingRevert || isAborted)) {
📝 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
outbound_params[1] &&
["Reverted", "PendingRevert", "Aborted"].includes(status)
["Reverted", "PendingRevert", "Aborted"].includes(String(status))
) {
const isReverted = status === "Reverted";
const isPendingRevert = status === "PendingRevert";
const isAborted = status === "Aborted";
const isReverted = String(status) === cctxStatusToJSON(CctxStatus.Reverted);
const isPendingRevert =
String(status) === cctxStatusToJSON(CctxStatus.PendingRevert);
const isAborted = String(status) === cctxStatusToJSON(CctxStatus.Aborted);
const isReverted = cctxStatusToJSON(status) === cctxStatusToJSON(CctxStatus.Reverted);
const isPendingRevert = cctxStatusToJSON(status) === cctxStatusToJSON(CctxStatus.PendingRevert);
const isAborted = cctxStatusToJSON(status) === cctxStatusToJSON(CctxStatus.Aborted);
if (outbound_params[1] && (isReverted || isPendingRevert || isAborted)) {
🤖 Prompt for AI Agents
In packages/commands/src/query/cctx.ts around lines 214 to 221, the condition
uses literal strings ("Reverted","PendingRevert","Aborted") but status is
serialized via cctxStatusToJSON, so the branch never runs; compute the expected
string constants by calling cctxStatusToJSON(CctxStatus.Reverted/ PendingRevert/
Aborted) (or compute isReverted/isPendingRevert/isAborted first) and use those
values in the includes/gate expression (or directly gate on the boolean flags)
so the revert/abort block executes correctly.

Comment on lines +149 to 160
chain: ChainSDKType,
allTokens: ForeignCoinsSDKType[],
chainParams: ChainParamsSDKType[]
): ChainInfo => {
const tokens = allTokens
.filter((t) => t.foreign_chain_id === chain.chain_id)
.filter((t) => t.foreign_chain_id === BigInt(chain.chain_id))
.map((t) => t.symbol);

const confirmations = chainParams.find(
(p) => p.chain_id === chain.chain_id
)?.confirmation_count;
const confirmations = chainParams
.find((p) => p.chain_id === BigInt(chain.chain_id))
?.confirmation_count.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

Bug: Mixed string/bigint comparison breaks token/confirmations lookup.

foreign_chain_id and chain_id may arrive as strings from JSON. Comparing to BigInt(...) will fail, yielding empty tokens/confirmations.

Use a consistent normalization (string or BigInt) on both sides. Minimal fix:

-  const tokens = allTokens
-    .filter((t) => t.foreign_chain_id === BigInt(chain.chain_id))
-    .map((t) => t.symbol);
+  const chainIdStr = String(chain.chain_id);
+  const tokens = allTokens
+    .filter((t) => String(t.foreign_chain_id) === chainIdStr)
+    .map((t) => t.symbol);
-
-  const confirmations = chainParams
-    .find((p) => p.chain_id === BigInt(chain.chain_id))
-    ?.confirmation_count.toString();
+  const confirmations = chainParams
+    .find((p) => String(p.chain_id) === chainIdStr)
+    ?.confirmation_count?.toString();
📝 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
chain: ChainSDKType,
allTokens: ForeignCoinsSDKType[],
chainParams: ChainParamsSDKType[]
): ChainInfo => {
const tokens = allTokens
.filter((t) => t.foreign_chain_id === chain.chain_id)
.filter((t) => t.foreign_chain_id === BigInt(chain.chain_id))
.map((t) => t.symbol);
const confirmations = chainParams.find(
(p) => p.chain_id === chain.chain_id
)?.confirmation_count;
const confirmations = chainParams
.find((p) => p.chain_id === BigInt(chain.chain_id))
?.confirmation_count.toString();
chain: ChainSDKType,
allTokens: ForeignCoinsSDKType[],
chainParams: ChainParamsSDKType[]
): ChainInfo => {
const chainIdStr = String(chain.chain_id);
const tokens = allTokens
.filter((t) => String(t.foreign_chain_id) === chainIdStr)
.map((t) => t.symbol);
const confirmations = chainParams
.find((p) => String(p.chain_id) === chainIdStr)
?.confirmation_count?.toString();
🤖 Prompt for AI Agents
In packages/commands/src/query/chains/show.ts around lines 149 to 160, token and
confirmation lookups compare values with BigInt(...) while incoming JSON fields
may be strings, causing mismatches; normalize both sides to the same type
(prefer strings for safe JSON handling): convert chain.chain_id and p.chain_id/
t.foreign_chain_id to String(...) before comparing (e.g.
String(t.foreign_chain_id) === String(chain.chain_id)), and when reading
confirmation_count convert it robustly to string (e.g. confirmation_count !=
null ? String(p.confirmation_count) : undefined); update the filter/find
comparisons accordingly so mixed string/BigInt inputs match.

Comment on lines +1 to +2
import { ForeignCoinsSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/fungible/foreign_coins";
import { QueryAllForeignCoinsResponseSDKType } from "@zetachain/sdk-cosmos/zetachain/zetacore/fungible/query";
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

🧩 Analysis chain

SDKType JSON field mismatch and inconsistent HTTP client.

  • QueryAllForeignCoinsResponseSDKType uses foreign_coins, not foreignCoins.
  • Prefer the shared fetchFromApi (with timeout) over raw fetch.

Apply this diff:

 import { z } from "zod";
 
 import { DEFAULT_API_URL } from "../../../../../src/constants/api";
 import { tokensListOptionsSchema } from "../../../../../src/schemas/commands/tokens";
+import { fetchFromApi } from "../../../../../utils/api";
@@
 export const fetchForeignCoins = async (
   apiUrl: string
-): Promise<ForeignCoinsSDKType[]> => {
-  const response = await fetch(`${apiUrl}/zeta-chain/fungible/foreign_coins`);
-
-  if (!response.ok) {
-    throw new Error(`HTTP error! status: ${response.status}`);
-  }
-
-  const data = (await response.json()) as QueryAllForeignCoinsResponseSDKType;
-  return data.foreignCoins;
-};
+): Promise<ForeignCoinsSDKType[]> => {
+  const data = await fetchFromApi<QueryAllForeignCoinsResponseSDKType>(
+    apiUrl,
+    "/zeta-chain/fungible/foreign_coins"
+  );
+  return data.foreign_coins;
+};

Please sanity-check one response payload to confirm the field naming and ensure we didn’t miss other camelCase/snake_case spots.

Also applies to: 14-25


🏁 Script executed:

#!/bin/bash
set -e

echo ">>> Display packages/commands/src/query/tokens/list.ts (first 200 lines)"
sed -n '1,200p' packages/commands/src/query/tokens/list.ts

echo
echo ">>> Locate utils/api files"
fd -t f -I utils/api*

echo
echo ">>> Show definition of fetchFromApi in utils/api"
grep -R "fetchFromApi" utils/api*

echo
echo ">>> Search for fetchFromApi usage across repository"
rg -n "fetchFromApi"

Length of output: 4880


🏁 Script executed:

#!/bin/bash
set -e

echo ">>> utils/api.ts (lines 1-200)"
sed -n '1,200p' utils/api.ts

echo
echo ">>> packages/commands/src/query/chains/list.ts (lines 1-200)"
sed -n '1,200p' packages/commands/src/query/chains/list.ts

Length of output: 8301


Use shared fetchFromApi and snake_case JSON field

In packages/commands/src/query/tokens/list.ts (lines 14–25), replace the raw fetch call with fetchFromApi and access data.foreign_coins – QueryAllForeignCoinsResponseSDKType uses snake_case. No other camelCase/snake_case mismatches found in this file.

 import { tokensListOptionsSchema } from "../../../../../src/schemas/commands/tokens";
+import { fetchFromApi } from "../../../../../utils/api";
@@
-export const fetchForeignCoins = async (
-  apiUrl: string
-): Promise<ForeignCoinsSDKType[]> => {
-  const response = await fetch(`${apiUrl}/zeta-chain/fungible/foreign_coins`);
-
-  if (!response.ok) {
-    throw new Error(`HTTP error! status: ${response.status}`);
-  }
-
-  const data = (await response.json()) as QueryAllForeignCoinsResponseSDKType;
-  return data.foreignCoins;
-};
+export const fetchForeignCoins = async (
+  apiUrl: string
+): Promise<ForeignCoinsSDKType[]> => {
+  const data = await fetchFromApi<QueryAllForeignCoinsResponseSDKType>(
+    apiUrl,
+    "/zeta-chain/fungible/foreign_coins"
+  );
+  return data.foreign_coins;
+};
🤖 Prompt for AI Agents
In packages/commands/src/query/tokens/list.ts around lines 14–25, the code uses
a raw fetch and accesses camelCase fields; replace the raw fetch call with the
shared fetchFromApi helper (import it from the shared utilities), call it with
the same endpoint, and read the response using snake_case: access
data.foreign_coins (not data.foreignCoins) which matches
QueryAllForeignCoinsResponseSDKType; keep existing SDK types/imports and adapt
any local variable names to use the snake_case property.

message,
receiver: options.receiver,
rpc: getSuiRpcByChainId(options.chainId),
rpc: getSuiRpcByChainId(Number(options.chainId)),
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

Validate numeric chain ID parsing.

Number(options.chainId) will yield NaN if the CLI ever passes a non-numeric string (e.g., an unexpected enum extension). That would bubble into getSuiRpcByChainId, ultimately calling getFullnodeUrl(undefined). We should either assert the conversion result or keep passing the zod-validated string and let getSuiRpcByChainId handle string literals.

🤖 Prompt for AI Agents
In packages/commands/src/sui/depositAndCall.ts around line 35, the code uses
Number(options.chainId) which can produce NaN for non-numeric inputs; either
validate the conversion or leave the validated string through. Replace
Number(...) with a safe parse and guard: attempt to parse the numeric chainId
(e.g., parseInt) and assert it is a finite number before calling
getSuiRpcByChainId, throwing a clear error if parsing fails, or alternatively
pass options.chainId (the zod-validated string) directly to getSuiRpcByChainId
so that function handles string literals; ensure you do not call
getSuiRpcByChainId with undefined or NaN.

Comment on lines 154 to +173
.map((chain) => {
const chainLabel = Object.keys(chains).find(
(key) => chains[key].chain_id === parseInt(chain.chain_id)
(key) => chains[key].chain_id === Number(chain.chain_id)
);

if (chainLabel) {
const contract = getAddress("zetaToken", chainLabel as ParamChainName);
if (contract) {
return {
chain_id: chain.chain_id,
chain_id: String(chain.chain_id),
coin_type: "ERC20",
contract,
decimals: 18,
symbol: "WZETA",
} as Token;
};
}
}
return null;
})
.filter((token): token is Token => token !== null);
.filter((token) => token !== null) as Token[];
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

Preserve ChainSDKType chains without a contract

getAddress("zetaToken", chainLabel) returns undefined for networks without a deployed contract, so this change now filters those chains out entirely. Previously we returned a null token and skipped it later, leaving Zeta-only chains intact. With the .filter(...) as Token[] the corresponding chains vanish from wzetaTokens, so downstream balance displays lose entries.

Stick with the explicit null handling (filtering via type guard) so we don’t silently drop chains.

Apply this diff to keep unsupported chains without WZETA:

-  const wzetaTokens = supportedChains
-    .map((chain) => {
+  const wzetaTokens = supportedChains
+    .map((chain) => {
       const chainLabel = Object.keys(chains).find(
         (key) => chains[key].chain_id === Number(chain.chain_id)
       );

       if (chainLabel) {
         const contract = getAddress("zetaToken", chainLabel as ParamChainName);
         if (contract) {
           return {
             chain_id: String(chain.chain_id),
             coin_type: "ERC20",
             contract,
             decimals: 18,
             symbol: "WZETA",
           };
         }
       }
       return null;
-    })
-    .filter((token) => token !== null) as Token[];
+    })
+    .filter((token): token is Token => token !== null);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In utils/balances.ts around lines 154–173, the current .filter(... ) as Token[]
removes chains that lack a deployed zetaToken contract; revert to preserving
those entries so downstream Zeta-only chains remain visible by removing the
final filter/cast and returning the mapped array as (Token | null)[] (or
explicitly type the result to include nulls) so nulls are handled later via the
existing type-guard filtering instead of being dropped here.

Comment on lines +289 to 292
export const getSuiRpcByChainId = (chainId: number) => {
const network =
networks[chainIds.indexOf(String(chainId) as (typeof chainIds)[number])];
return getFullnodeUrl(network);
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

Add explicit error when chain ID is not recognized.

If chainIds.indexOf(...) returns -1 (e.g., because the caller passed NaN or an unsupported ID), network becomes undefined, and getFullnodeUrl will throw a cryptic error. Let’s detect index === -1 up front and throw a clear message (Invalid chain ID), preserving prior type-safety after widening the function signature.

Apply this diff:

 export const getSuiRpcByChainId = (chainId: number) => {
-  const network =
-    networks[chainIds.indexOf(String(chainId) as (typeof chainIds)[number])];
+  const index = chainIds.indexOf(
+    String(chainId) as (typeof chainIds)[number]
+  );
+  if (index === -1) {
+    throw new Error(`Invalid Sui chain ID: ${chainId}`);
+  }
+  const network = networks[index];
   return getFullnodeUrl(network);
 };
📝 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 const getSuiRpcByChainId = (chainId: number) => {
const network =
networks[chainIds.indexOf(String(chainId) as (typeof chainIds)[number])];
return getFullnodeUrl(network);
export const getSuiRpcByChainId = (chainId: number) => {
const index = chainIds.indexOf(
String(chainId) as (typeof chainIds)[number]
);
if (index === -1) {
throw new Error(`Invalid Sui chain ID: ${chainId}`);
}
const network = networks[index];
return getFullnodeUrl(network);
};
🤖 Prompt for AI Agents
In utils/sui.ts around lines 289 to 292, the code derives network via
networks[chainIds.indexOf(String(chainId))] which yields undefined when index
=== -1; add an explicit check: compute index = chainIds.indexOf(String(chainId))
and if index === -1 throw a clear Error('Invalid chain ID: ' + chainId);
otherwise use networks[index] and pass it to getFullnodeUrl; keep the function
signature as-is (number) but ensure you convert to string for the lookup so
type-safety is preserved.

return (
foreignCoin.foreign_chain_id === chainID &&
foreignCoin.coin_type === "Gas"
String(foreignCoin.foreign_chain_id) === chainID &&
Copy link
Member

@hernan-clich hernan-clich Sep 25, 2025

Choose a reason for hiding this comment

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

I do understand why we need to do this (the new sdk type says foreign_chain_id is a bigint while our previous, custom type had it as a string), but I don't think we should be doing this, the real, runtime type is a string.

Copy link
Member

Choose a reason for hiding this comment

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

This is a response from the Foreign Coins endpoint:

image

Copy link
Member

Choose a reason for hiding this comment

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

This clearly resolves as a string.

image

Copy link
Member

Choose a reason for hiding this comment

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

The way I see it we could:

  1. Create a runtime accurate interface:
export interface ForeignCoinsRuntimeType extends Omit<ForeignCoinsSDKType, 'foreign_chain_id'> {
  foreign_chain_id: string;
}
  1. Fix the type on the sdk types lib.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants