Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/support swap oraidex osmosis #343

Closed
wants to merge 33 commits into from

Conversation

haunv3
Copy link
Contributor

@haunv3 haunv3 commented Oct 3, 2024

Summary by CodeRabbit

  • New Features

    • Introduced a new dependency for enhanced functionality in the universal swap package.
    • Added functions for path validation and receiver address verification.
    • Added the CosmosMsg, OsmosisMsg, and OraichainMsg classes for managing token swap and bridge messages on their respective platforms.
    • Implemented new interfaces and enums to improve message handling and categorization.
    • Enhanced types.ts with new action types and optional properties for better data management.
    • Introduced functionality for generating messages related to universal swaps across different blockchain environments.
    • Added a ChainMsg class to encapsulate chain message properties and validation.
    • Integrated support for the Celestia network, including configuration and network type updates.
    • Added a new constant for Celestia network configuration.
    • Introduced a comprehensive implementation for executing universal swaps involving multiple blockchain networks.
    • Updated the UniversalSwapHelper and UniversalSwapHandler classes to enhance token swap functionalities and improve error handling.
  • Bug Fixes

    • Improved validation checks for token swaps and bridge operations.
  • Documentation

    • Updated export statements for easier access to module functionalities.
    • Updated .gitignore entries for better project management.
    • Updated version numbers in package files for clarity.
    • Expanded test suite for generateMsgSwap to cover additional scenarios and improve robustness.

Copy link

coderabbitai bot commented Oct 3, 2024

Walkthrough

The pull request introduces several changes to the @oraichain/oraidex-universal-swap package. Modifications include updates to the .gitignore file, which now includes new entries and alters existing ones. The package.json file reflects a new dependency addition and a version update. New validation functions are added in common.ts, while new classes for handling messaging, such as CosmosMsg, OsmosisMsg, and OraichainMsg, are introduced in their respective files. Additionally, several interfaces and enums are defined or updated to enhance functionality across the module.

Changes

File Path Change Summary
.gitignore Added entries: .yarn, demo-local; modified entry for .yarn from ignored to included.
packages/universal-swap/package.json Added dependency: "@oraichain/osor-api-contracts-sdk": "^1.0.2"; version updated to 1.1.13.
packages/universal-swap/src/helper.ts Added method: getAddress; updated method signature for addOraiBridgeRoute; deprecated methods: generateSwapRoute, simulateSwap.
packages/universal-swap/src/handler.ts Updated method signatures for swapCosmosToOtherNetwork and processUniversalSwap; added method: alphaSmartRouterSwapNewMsg.
packages/universal-swap/src/msg/common.ts Added methods: validatePath and validateReceiver for path and receiver validation.
packages/universal-swap/src/msg/cosmos.ts Added class: CosmosMsg for constructing messages related to token swaps on Oraichain.
packages/universal-swap/src/msg/oraichain.ts Added class: OraichainMsg for handling swap and bridge operations in the Oraichain ecosystem.
packages/universal-swap/src/msg/osmosis.ts Added class: OsmosisMsg for constructing messages related to swaps and bridges on Osmosis.
packages/universal-swap/src/msg/index.ts Re-exported all entities from cosmos, osmosis, and oraichain modules.
packages/universal-swap/src/msg/types.ts Added interfaces: SwapMsgInfo, BridgeMsgInfo, and MiddlewareResponse; added enum: PostActionType.
packages/universal-swap/src/msg/msgs.ts Added functions for message generation: getDestPrefixForBridgeToEvmOnOrai, buildMemoSwap, buildExecuteMsg, generateMsgSwap.
packages/oraidex-common/package.json Version updated to 1.1.23.
packages/oraidex-common/src/celestia-network.ts Added constant: celestiaNetwork with configuration details for the Celestia blockchain.
packages/universal-swap/tests/msg/msgs.spec.ts Added tests for generateMsgSwap function to validate message generation and error handling.

Suggested reviewers

  • trung2891
  • quangdz1704

Poem

🐰 In the land of swaps and chains,
New paths and tokens dance like grains.
With bridges built and messages spun,
The Oraichain journey has just begun!
Hops of joy, let the code play,
For every change brings a brighter day! 🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Oct 3, 2024

badge

Code Coverage Summary

Filename                                                                      Stmts    Miss  Cover    Missing
--------------------------------------------------------------------------  -------  ------  -------  --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
docs/assets/main.js                                                              58      58  0.00%    1-58
docs/assets/search.js                                                             1       1  0.00%    1
packages/ibc-routing/hardhat.config.ts                                           59      59  0.00%    1-75
packages/ibc-routing/src/db.ts                                                  158     158  0.00%    1-263
packages/ibc-routing/src/event.ts                                                85      85  0.00%    1-122
packages/ibc-routing/src/index.ts                                                31      31  0.00%    1-56
packages/ibc-routing/src/machine.ts                                              30      30  0.00%    1-34
packages/ibc-routing/test/mock-eth-ws.ts                                         52      52  0.00%    1-55
packages/ibc-routing/test/mock-tendermint-ws.spec.ts                             25      17  32.00%   7-24
packages/oraidex-common/src/alpha-network.ts                                     61       0  100.00%
packages/oraidex-common/src/axios-request.ts                                     11      11  0.00%    1-20
packages/oraidex-common/src/bigdecimal.ts                                       139      50  64.02%   23-24, 30-33, 40-41, 45-54, 63-65, 71-73, 120-124, 127-146, 149-150, 153-154, 180-181, 184-185
packages/oraidex-common/src/celestia-network.ts                                  52       0  100.00%
packages/oraidex-common/src/constant.ts                                         170       0  100.00%
packages/oraidex-common/src/helper.ts                                           466     142  69.52%   141-142, 145-156, 159-169, 267-269, 286-293, 296-309, 319-325, 335-350, 353-360, 363-393, 396-404, 407-409, 412-414, 418-427, 436-450, 456-458, 543-544, 569-572
packages/oraidex-common/src/ibc-info.ts                                         184     184  0.00%    1-218
packages/oraidex-common/src/index.ts                                             14      14  0.00%    1-14
packages/oraidex-common/src/network.ts                                         1024       0  100.00%
packages/oraidex-common/src/pairs.ts                                            154       4  97.40%   151-152, 157-158
packages/oraidex-common/src/token.ts                                             71       0  100.00%
packages/oraidex-common/src/wallet.ts                                           146     146  0.00%    1-236
packages/oraidex-common/src/config/chainInfosWithIcon.ts                        208     208  0.00%    1-222
packages/oraidex-common/src/interface/index.ts                                    1       1  0.00%    1
packages/oraidex-common/src/interface/wallet.ts                                  19      19  0.00%    1-20
packages/oraidex-common/tests/bigdecimal.spec.ts                                220       0  100.00%
packages/oraidex-common/tests/helper.spec.ts                                    560       8  98.57%   97-98, 337-338, 628-629, 659-660
packages/oraidex-common/tests/pairs.spec.ts                                      24       0  100.00%
packages/oraiswap-v3/src/const.ts                                                11       0  100.00%
packages/oraiswap-v3/src/error.ts                                                 5       2  60.00%   3-4
packages/oraiswap-v3/src/handler.ts                                             292      28  90.41%   63-64, 100-101, 216-217, 341-355, 358-366
packages/oraiswap-v3/src/helpers.ts                                             444     234  47.29%   122-123, 133-134, 159-160, 188-189, 199-200, 283-287, 290-297, 300-356, 359-364, 367-376, 380-436, 439-447, 450-481, 484-497, 500-523, 526-532, 535-549
packages/oraiswap-v3/src/index.ts                                                 7       0  100.00%
packages/oraiswap-v3/src/main.ts                                                 37      37  0.00%    1-72
packages/oraiswap-v3/src/types.ts                                                13       0  100.00%
packages/oraiswap-v3/src/zap-consumer.ts                                        496     478  3.62%    54-58, 61-62, 65-98, 101-143, 146-165, 168-186, 189-219, 222-331, 334-567, 570-608
packages/oraiswap-v3/src/wasm/oraiswap_v3_wasm.d.ts                              32      32  0.00%    469-500
packages/oraiswap-v3/src/wasm/oraiswap_v3_wasm.js                              1543     710  53.98%   32-34, 92-94, 100-101, 103-108, 113-120, 136-145, 160-223, 233-255, 264-284, 293-313, 322-342, 351-371, 380-400, 409-429, 463-464, 480-502, 508-511, 518-530, 536-548, 555-558, 565-579, 587-590, 595-598, 611-614, 620-623, 636-639, 660-663, 668-671, 688-729, 736-754, 762-776, 783-786, 793-807, 817-838, 848-869, 884-885, 910-911, 929-953, 958-961, 966-969, 976-979, 984-987, 992-995, 1002-1005, 1010-1013, 1028-1031, 1036-1039, 1044-1047, 1062-1065, 1080-1083, 1088-1091, 1096-1099, 1106-1109, 1114-1117, 1122-1125, 1132-1135, 1149-1150, 1161-1164, 1170-1173, 1179-1182, 1188-1191, 1196-1199, 1204-1207, 1214-1217, 1219-1225, 1300-1301, 1329-1330, 1333-1335, 1356-1361, 1364-1365, 1368-1372, 1386-1387, 1390-1391, 1394-1395, 1398-1399, 1402-1403, 1406-1407, 1410-1413, 1416-1417, 1420-1421, 1424-1425, 1428-1431, 1434-1437, 1444, 1447-1448, 1451-1458, 1465-1466, 1469-1470, 1473-1474, 1477, 1480-1481, 1484-1491, 1500-1504, 1507, 1510-1511
packages/oraiswap-v3/tests/handler.spec.ts                                      276       0  100.00%
packages/oraiswap-v3/tests/helpers.spec.ts                                      577       0  100.00%
packages/oraiswap-v3/tests/test-common.ts                                        32       0  100.00%
packages/oraiswap-v3/tests/zap-consumer.spec.ts                                  50       0  100.00%
packages/universal-swap/src/handler.ts                                         1245     648  47.95%   80-81, 102-103, 118-120, 126-129, 151-156, 177-199, 279-309, 366-371, 422-442, 493-498, 545-556, 580, 582-586, 599-610, 617-632, 635-693, 704-714, 717-727, 734-741, 755-832, 836-867, 871-890, 893-910, 914-971, 976-1060, 1066-1158, 1169-1176, 1194-1197, 1217-1218, 1220-1221, 1227, 1292-1295, 1328-1331, 1338-1348, 1353-1373, 1414-1415, 1428-1471, 1534-1535
packages/universal-swap/src/helper.ts                                          1001     375  62.53%   90-92, 228-229, 275-280, 308-309, 315-321, 352-369, 375-411, 425-479, 520-522, 547-553, 579-581, 585-587, 594-624, 687-688, 721-728, 735-776, 789-849, 888-889, 904, 932, 939-962, 964-965, 1007-1008, 1012-1015, 1025-1067, 1075-1091, 1150-1160, 1171-1176, 1195, 1221-1233, 1351-1362
packages/universal-swap/src/index.ts                                              6       6  0.00%    1-6
packages/universal-swap/src/swap-filter.ts                                       40       0  100.00%
packages/universal-swap/src/types.ts                                             14       0  100.00%
packages/universal-swap/src/wrapper.ts                                           79      79  0.00%    1-117
packages/universal-swap/src/msg/common.ts                                        34      12  64.70%   11-12, 16-17, 20-21, 24-25, 30-31, 33-34
packages/universal-swap/src/msg/index.ts                                          3       0  100.00%
packages/universal-swap/src/msg/msgs.ts                                         126      44  65.07%   22-23, 62-69, 84-89, 91-94, 100-101, 115-116, 131-153
packages/universal-swap/src/msg/types.ts                                          6       0  100.00%
packages/universal-swap/src/msg/chains/chain.ts                                  20       4  80.00%   18-19, 22-23
packages/universal-swap/src/msg/chains/cosmos.ts                                107       9  91.58%   22-23, 27-28, 56, 62-63, 122-123
packages/universal-swap/src/msg/chains/index.ts                                   3       0  100.00%
packages/universal-swap/src/msg/chains/oraichain.ts                             416      79  81.00%   39-40, 45-46, 67-90, 118-124, 141, 194, 277, 429-452, 484-503
packages/universal-swap/src/msg/chains/osmosis.ts                               234     119  49.14%   27-28, 33-34, 73-74, 91, 101-106, 122, 136-154, 158-163, 202-296
packages/universal-swap/src/proto/index.ts                                        1       1  0.00%    1
packages/universal-swap/src/proto/universal-swap-memo-proto-handler.ts           66      60  9.09%    21-46, 50-91
packages/universal-swap/src/proto/universal_swap_memo.ts                        911     727  20.19%   110-112, 135-183, 186-193, 196-213, 216-217, 219-230, 233-235, 246-266, 269-274, 277-282, 285-286, 288-291, 294-296, 300-304, 307-327, 330-333, 336-341, 344-345, 347-350, 353-355, 359-366, 369-396, 399-405, 408-416, 419-420, 422-426, 429-431, 448-482, 485-490, 493-504, 507-508, 510-515, 518-520, 531-532, 537-571, 574-583, 586-597, 600-601, 603-613, 616-618, 629-630, 638-679, 682-690, 693-707, 710-711, 713-727, 730-732, 755-803, 806-813, 816-833, 836-837, 839-846, 849-851, 865-866, 874-922, 925-932, 935-952, 955-956, 958-965, 968-970, 974-981, 984-1011, 1014-1018, 1021-1029, 1032-1033, 1035-1039, 1042-1044, 1055-1075, 1078-1079, 1082-1087, 1090-1091, 1093-1096, 1111-1119, 1122-1128
packages/universal-swap/src/universal-demos/alpha-ibc-new.ts                    170     170  0.00%    1-176
packages/universal-swap/src/universal-demos/alpha-smart-router.ts               117     117  0.00%    1-126
packages/universal-swap/src/universal-demos/decode-memo.ts                       13      13  0.00%    1-14
packages/universal-swap/src/universal-demos/evm-to-evm.ts                        52      52  0.00%    1-68
packages/universal-swap/src/universal-demos/from-cosmos-to-evm.ts                41      41  0.00%    1-53
packages/universal-swap/src/universal-demos/from-oraichain-to-evm.ts             42      42  0.00%    1-62
packages/universal-swap/src/universal-demos/from-oraichain-to-oraichain.ts       49      49  0.00%    1-66
packages/universal-swap/src/universal-demos/handle-simulate-swap.ts              32      32  0.00%    1-36
packages/universal-swap/src/universal-demos/ibc-hooks-demo.ts                    40      40  0.00%    1-55
packages/universal-swap/src/universal-demos/neutaro-ibc-demo.ts                  40      40  0.00%    1-47
packages/universal-swap/src/universal-demos/noble-ibc-demo.ts                    41      41  0.00%    1-45
packages/universal-swap/src/universal-demos/offline-wallet.ts                    19      19  0.00%    1-21
packages/universal-swap/tests/helper.spec.ts                                    704      24  96.59%   378, 954-976
packages/universal-swap/tests/index.spec.ts                                    1750      30  98.28%   240-243, 283-284, 289-290, 292-293, 295-296, 1138-1157
packages/universal-swap/tests/smart-router-common.ts                            864       0  100.00%
packages/universal-swap/tests/test-common.ts                                     56       0  100.00%
packages/universal-swap/tests/msg/comos-msg.spec.ts                             208       0  100.00%
packages/universal-swap/tests/msg/msgs.spec.ts                                  580       0  100.00%
packages/universal-swap/tests/msg/oraichain-msg.spec.ts                         689       2  99.70%   115-116
packages/universal-swap/tests/msg/osmosis-msg.spec.ts                           357       0  100.00%
packages/universal-swap/tests/msg/test-data.ts                                    8       8  0.00%    1-9
TOTAL                                                                         18022    5682  68.47%

Diff against main

Filename                                                             Stmts    Miss  Cover
-----------------------------------------------------------------  -------  ------  --------
packages/oraidex-common/src/celestia-network.ts                        +52       0  +100.00%
packages/oraidex-common/src/constant.ts                                 +1       0  +100.00%
packages/oraidex-common/src/ibc-info.ts                                 +1      +1  +100.00%
packages/oraidex-common/src/index.ts                                    +1      +1  +100.00%
packages/oraidex-common/src/network.ts                                  +2       0  +100.00%
packages/universal-swap/src/handler.ts                                 +17     -14  +1.86%
packages/universal-swap/src/helper.ts                                  +53     +49  -3.08%
packages/universal-swap/src/types.ts                                    +4       0  +100.00%
packages/universal-swap/src/msg/common.ts                              +34     +12  +64.70%
packages/universal-swap/src/msg/index.ts                                +3       0  +100.00%
packages/universal-swap/src/msg/msgs.ts                               +126     +44  +65.07%
packages/universal-swap/src/msg/types.ts                                +6       0  +100.00%
packages/universal-swap/src/msg/chains/chain.ts                        +20      +4  +80.00%
packages/universal-swap/src/msg/chains/cosmos.ts                      +107      +9  +91.58%
packages/universal-swap/src/msg/chains/index.ts                         +3       0  +100.00%
packages/universal-swap/src/msg/chains/oraichain.ts                   +416     +79  +81.00%
packages/universal-swap/src/msg/chains/osmosis.ts                     +234    +119  +49.14%
packages/universal-swap/src/proto/universal_swap_memo.ts                 0     -91  +9.99%
packages/universal-swap/src/universal-demos/alpha-ibc-new.ts          +170    +170  +100.00%
packages/universal-swap/src/universal-demos/alpha-smart-router.ts       +3      +3  +100.00%
packages/universal-swap/src/universal-demos/decode-memo.ts             +13     +13  +100.00%
packages/universal-swap/tests/helper.spec.ts                            +7       0  +0.04%
packages/universal-swap/tests/index.spec.ts                             +7      -2  +0.12%
packages/universal-swap/tests/msg/comos-msg.spec.ts                   +208       0  +100.00%
packages/universal-swap/tests/msg/msgs.spec.ts                        +580       0  +100.00%
packages/universal-swap/tests/msg/oraichain-msg.spec.ts               +689      +2  +99.70%
packages/universal-swap/tests/msg/osmosis-msg.spec.ts                 +357       0  +100.00%
packages/universal-swap/tests/msg/test-data.ts                          +8      +8  +100.00%
TOTAL                                                                +3122    +407  +3.88%

Results for commit: 34ce351

Minimum allowed coverage is 0%

♻️ This comment has been updated with latest results

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: 23

🧹 Outside diff range and nitpick comments (13)
packages/universal-swap/src/msg/types.ts (3)

12-23: LGTM: BridgeMsgInfo interface is comprehensive and well-structured.

The BridgeMsgInfo interface correctly encapsulates the necessary information for a bridge message, using appropriate types for each property. The optional properties are correctly marked.

Consider adding brief comments for other properties to improve code documentation, similar to the comment for the 'prefix' property. This would enhance the overall readability and maintainability of the code.


25-31: LGTM: PostActionType enum covers necessary action types.

The PostActionType enum effectively categorizes various post-action types that might be needed in the context of swaps or bridges.

Consider using explicit numbering for the enum values to prevent unintended changes if the order is modified in the future. For example:

export enum PostActionType {
  None = 0,
  Transfer = 1,
  ContractCall = 2,
  IbcTransfer = 3,
  IbcWasmTransfer = 4
}

This approach makes the values more robust against future modifications.


1-31: Overall, excellent addition of types for swap and bridge operations.

This file introduces well-structured and comprehensive types (SwapMsgInfo, BridgeMsgInfo, and PostActionType) that will enhance type safety and developer experience when working with swap and bridge operations. The code is clean, follows TypeScript best practices, and is ready for use in the project.

As the project evolves, consider creating separate files for swap-related and bridge-related types if these grow significantly. This would improve modularity and make it easier to maintain and extend these types in the future.

packages/universal-swap/src/msg/common.ts (1)

5-8: Update function documentation to include all parameters.

The current documentation doesn't mention the hasSwap parameter. Consider updating it to include all parameters and their purposes.

Here's a suggested update:

/**
 * Validates that the path adheres to specific rules regarding action types.
 * @param path The path to be validated
 * @param hasSwap Whether swap actions are allowed in the path (default: true)
 */
packages/universal-swap/src/msg/cosmos.ts (1)

26-26: Consider initializing 'memo' with an empty string instead of 'undefined'

Assigning undefined to memo might lead to issues when the memo is used in JSON serialization or external calls that expect a string.

Initialize memo with an empty string if no value is provided:

-    protected memo?: string
+    protected memo: string = ''
packages/universal-swap/src/msg/oraichain.ts (8)

165-165: Typographical error in error message

The error message contains a typo:

throw generateError("Missing prefix os Obridge address for bridge to EVM");

It should be "or" instead of "os".

Apply this diff to correct the message:

- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or Obridge address for bridge to EVM");

243-243: Typographical error in error message

Similar typo in the error message:

throw generateError("Missing prefix os Obridge address for bridge to EVM");

Apply this diff:

- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or Obridge address for bridge to EVM");

366-366: Typographical error in error message

Consistent typo in the error message:

throw generateError("Missing prefix os Obridge address for bridge to EVM");

Apply this diff:

- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or Obridge address for bridge to EVM");

126-126: Typographical error in error message

There's a typo in the error message:

throw generateError("Only support swap + convert + bride on Oraichain");

"bride" should be "bridge".

Apply this diff:

- throw generateError("Only support swap + convert + bride on Oraichain");
+ throw generateError("Only support swap + convert + bridge on Oraichain");

378-378: Address the 'FIXME' comment regarding timeout

There's a FIXME comment indicating uncertainty about using nanoseconds with a u64 type:

timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), // FIXME: should we use nano with an u64 type? -> probably quite big for a u64

Would you like assistance in resolving this issue or should a GitHub issue be opened to track this task?


274-274: Clarify error message in 'genMemoAsMiddleware' method

The error message:

throw generateError("Error on generate memo as middleware: Only support ibc bridge");

Could be made more precise to help with debugging.

Consider specifying the unsupported bridge type or providing guidance on supported types.


53-75: Consider implementing the TODO for converter contract query

There's a TODO comment indicating the need to query the converter contract to determine the appropriate conversion method:

// TODO: Query the converter contract to determine the appropriate conversion method

Do you need assistance in implementing this functionality or should we create a GitHub issue to track it?


134-180: Potential redundancy in 'getProtoForPostAction' method

The method contains multiple conditional checks for bridgeInfo.sourcePort. Consider consolidating conditions or using a switch statement for clarity.

Refactoring the method could improve maintainability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between c8e6eb5 and 34e0ebb.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (9)
  • packages/universal-swap/package.json (1 hunks)
  • packages/universal-swap/src/msg/common.ts (1 hunks)
  • packages/universal-swap/src/msg/cosmos.ts (1 hunks)
  • packages/universal-swap/src/msg/index.ts (1 hunks)
  • packages/universal-swap/src/msg/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/osmosis.ts (1 hunks)
  • packages/universal-swap/src/msg/types.ts (1 hunks)
  • packages/universal-swap/src/types.ts (3 hunks)
  • tsconfig.json (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/universal-swap/src/msg/index.ts
  • tsconfig.json
🔇 Additional comments (11)
packages/universal-swap/package.json (1)

20-21: LGTM! Verify usage of the new dependency.

The addition of @oraichain/osor-api-contracts-sdk version ^1.0.2 is consistent with the PR objective to support swaps between Oraidex and Osmosis. This change looks good.

To ensure the new dependency is properly utilized, please run the following script to check its usage across the codebase:

packages/universal-swap/src/msg/types.ts (2)

1-4: LGTM: Import statements are appropriate and well-structured.

The import statements are relevant to the types being defined in this file and follow TypeScript best practices.


6-10: LGTM: SwapMsgInfo interface is well-defined.

The SwapMsgInfo interface correctly encapsulates the necessary information for a swap message, using appropriate types for each property.

packages/universal-swap/src/msg/common.ts (2)

1-3: LGTM: Imports are appropriate and necessary.

The import statements are concise and relevant to the implemented functions. They include necessary types, the bech32 library for address validation, and a utility function for error generation.


1-39: Overall assessment: Good additions with room for minor improvements.

The new functions validatePath and isCw20Token (suggested to be renamed to isValidBech32Address) add valuable validation and checking capabilities to the module. They enhance the robustness of the code by enforcing rules on path structures and validating addresses.

The suggested improvements focus on:

  1. Enhancing documentation for clarity and completeness.
  2. Improving error handling for consistency and internationalization.
  3. Refining function naming for accuracy.

These changes, once implemented, will further improve the maintainability and clarity of the code.

packages/universal-swap/src/types.ts (3)

14-18: LGTM: ActionType enum addition

The ActionType enum is a good addition to the file. It provides clear categorization for different types of actions in the universal swap system. The naming convention follows TypeScript best practices, and using string literals as values can be beneficial for serialization and debugging purposes.


216-216: LGTM: Exporting Path interface

Exporting the Path interface is a good change. It allows the interface to be used in other modules, improving code reusability and modularity. This change aligns with the PR objectives and doesn't introduce any issues.


Line range hint 1-254: Verify the update to UniversalSwapData interface

The AI-generated summary mentions an update to the UniversalSwapData interface, adding an optional affiliates property. However, this change is not visible in the provided code snippet. Could you please verify if this change was actually made? If it was, please provide the updated interface definition for review.

To help verify this change, you can run the following command:

✅ Verification successful

Verify the presence of affiliates in the UniversalSwapData interface

The previous search did not locate the UniversalSwapData interface, possibly due to an unrecognized file type. To confirm the addition of the affiliates property, please run the following command:


Verified the addition of affiliates in the UniversalSwapData interface

The affiliates property has been successfully added to the UniversalSwapData interface as an optional field.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the UniversalSwapData interface definition
rg --type typescript -A 20 'export interface UniversalSwapData' packages/universal-swap/src/types.ts

Length of output: 137


Script:

#!/bin/bash
# Search for the 'affiliates' property within the 'UniversalSwapData' interface
rg 'export interface UniversalSwapData' packages/universal-swap/src/types.ts -A 20 | rg 'affiliates'

Length of output: 137

packages/universal-swap/src/msg/osmosis.ts (2)

25-25: Initialize 'ENTRY_POINT_CONTRACT' with a valid address

The ENTRY_POINT_CONTRACT is currently an empty string. Ensure it is assigned a valid contract address before use to prevent runtime errors.

Please confirm that ENTRY_POINT_CONTRACT is correctly initialized elsewhere in the codebase.


35-36: Use strict equality operator for chain ID comparison

For consistency and to avoid unexpected type coercion, use the strict equality operator !== when comparing strings.

- if (path.chainId !== "osmosis-1") {
+ if (path.chainId !== "osmosis-1") {

Note: The code already uses strict inequality; this is appropriate.

packages/universal-swap/src/msg/oraichain.ts (1)

81-131: Ensure all action types are handled in 'getSwapAndBridgeInfo'

In the getSwapAndBridgeInfo method, if a new ActionType is introduced in the future, it may not be handled, leading to unexpected errors.

Consider adding a default case or handling for all possible ActionType values. Run the following script to check for unhandled ActionType usages:

✅ Verification successful

All ActionType values are properly handled in getSwapAndBridgeInfo

The ActionType enum includes Bridge, Swap, and Convert, all of which are explicitly handled in the getSwapAndBridgeInfo method. Additionally, a default case ensures that any future ActionType additions will be caught, preventing unhandled scenarios.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for all usages of ActionType and ensure they are handled.

# Search for ActionType enum definitions
rg 'enum ActionType'

# Search for all switch statements handling ActionType
rg 'switch\s*\(\s*action\.type\s*\)' -A 10

Length of output: 2753


Script:

#!/bin/bash
# Description: Extract all ActionType enum values for verification.

# Extract the full definition of the ActionType enum
rg 'export enum ActionType' packages/universal-swap/src/types.ts -A 20

Length of output: 444

packages/universal-swap/src/msg/cosmos.ts Outdated Show resolved Hide resolved
packages/universal-swap/src/msg/cosmos.ts Outdated Show resolved Hide resolved
packages/universal-swap/src/msg/cosmos.ts Outdated Show resolved Hide resolved
packages/universal-swap/src/msg/cosmos.ts Outdated Show resolved Hide resolved
Comment on lines 35 to 60
getBridgeInfo(): BridgeMsgInfo {
let bridgeInfo: BridgeMsgInfo;

for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}

return bridgeInfo;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure 'bridgeInfo' is defined before returning or using it

In the getBridgeInfo() method, there is a possibility that bridgeInfo remains undefined if none of the actions in this.path.actions have a type of ActionType.Bridge. This could lead to runtime errors in other methods that rely on bridgeInfo, such as genExecuteMsg() and genMemoAsMiddleware().

Add a check to ensure that bridgeInfo is defined before returning it:

      }
+     if (!bridgeInfo) {
+       throw generateError('No bridge action found in path actions.');
+     }
      return bridgeInfo;
📝 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
getBridgeInfo(): BridgeMsgInfo {
let bridgeInfo: BridgeMsgInfo;
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}
return bridgeInfo;
}
getBridgeInfo(): BridgeMsgInfo {
let bridgeInfo: BridgeMsgInfo;
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}
if (!bridgeInfo) {
throw generateError('No bridge action found in path actions.');
}
return bridgeInfo;
}

ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
remoteDenom: prefix + bridgeInfo.toToken,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Operator precedence issue in 'remoteAddress' assignment

In the expression:

remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,

Due to operator precedence, the expression will not evaluate as intended. The + operator has higher precedence than the ternary ? : operator, so it will concatenate prefix and isBridgeToEvm (a string and a boolean), which is likely unintended.

Add parentheses to ensure correct evaluation:

- remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),

ibc_wasm_transfer: {
ibc_wasm_info: {
local_channel_id: bridgeInfo.sourceChannel,
remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Operator precedence issue in 'remote_address' assignment

Similarly, in the expression:

remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,

The + operator will combine prefix and isBridgeToEvm, leading to incorrect results.

Add parentheses to correct the evaluation:

- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),

Comment on lines 493 to 494
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect assignment of 'amount' and 'denom' in funds

Again, in the funds array:

funds: [
  {
    amount: bridgeInfo.fromToken,
    denom: this.path.tokenInAmount
  }
]

The values of amount and denom need to be swapped.

Apply this diff:

funds: [
  {
-   amount: bridgeInfo.fromToken,
-   denom: this.path.tokenInAmount
+   amount: this.path.tokenInAmount,
+   denom: bridgeInfo.fromToken
  }
]

Comment on lines 341 to 427
// ibc transfer
if (bridgeInfo.sourcePort == "transfer") {
return {
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: bridgeInfo.sourcePort,
sourceChannel: bridgeInfo.sourceChannel,
receiver: this.receiver,
token: {
amount: this.path.tokenInAmount,
denom: this.path.tokenIn
},
sender: this.currentChainAddress,
memo: this.memo,
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
};
}

// ibc wasm transfer
if (bridgeInfo.sourcePort.startsWith("wasm")) {
let prefix = "";
let isBridgeToEvm = isEthAddress(this.receiver);
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
prefix = this.destPrefix;
}

const ibcWasmContractAddress = bridgeInfo.sourceChannel.split(".")[1];
if (!ibcWasmContractAddress)
throw generateError("IBC Wasm source port is invalid. Cannot transfer to the destination chain");

const msg: TransferBackMsg = {
local_channel_id: bridgeInfo.sourceChannel,
remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
remote_denom: prefix + bridgeInfo.toToken,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), // FIXME: should we use nano with an u64 type? -> probably quite big for a u64
memo: this.memo
};

// if asset info is native => send native way, else send cw20 way
if (isCw20Token(bridgeInfo.fromToken)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: bridgeInfo.fromToken,
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: ibcWasmContractAddress,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
}
})
),
funds: []
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: ibcWasmContractAddress,
msg: toUtf8(
JSON.stringify({
transfer_to_remote: {
msg
}
})
),
funds: [
{
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
}
]
})
};
}

throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling and consistency in 'genExecuteMsg' method

In the genExecuteMsg method, the handling of different cases could be made more consistent. Also, consider adding comments to explain the logic flow, especially in complex conditional structures.

Refactoring this method may enhance readability and maintainability.

* @param tokenOut The output token after conversion
* @returns The pool ID generated based on the provided input and output tokens
*/
pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Typo in method name: 'pasreConverterMsgToPoolId' should be 'parseConverterMsgToPoolId'

The method name pasreConverterMsgToPoolId contains a typo. It should be parseConverterMsgToPoolId for correct readability and consistency.

Apply this diff to correct the method name:

- pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
+ parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
📝 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
pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {

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: 12

🧹 Outside diff range and nitpick comments (3)
packages/universal-swap/src/msg/osmosis.ts (1)

1-280: Overall assessment: Good implementation with minor issues to address

The OsmosisMsg class provides a solid foundation for handling swaps and bridges on the Osmosis blockchain. The code is well-structured and implements the necessary functionality. However, there are several minor issues that should be addressed:

  1. Initialize the ENTRY_POINT_CONTRACT property or provide a method to set it.
  2. Fix the typo in the error message for unsupported actions.
  3. Initialize the bridgeInfo variable to avoid potential undefined errors.
  4. Update error messages to consistently reference Osmosis instead of Oraichain.
  5. Fix the incorrect array access using 'slice' in the genMemoAsMiddleware method.
  6. Correct the JSON structure for CW20 token send messages.
  7. Remove unnecessary wrapping of 'msg' in JSON for native tokens.
  8. Swap 'amount' and 'denom' in the funds array for native tokens.

After addressing these issues, I recommend thorough testing of all scenarios, including edge cases, to ensure the class behaves correctly in all situations.

packages/universal-swap/src/msg/oraichain.ts (2)

49-49: Address TODO comment

There's a TODO comment indicating that querying the converter contract is needed to determine the appropriate conversion method. This should be implemented to ensure the correct conversion method is used.

Would you like assistance in implementing the query to the converter contract?


1-497: Enhance class structure and documentation

The OraichainMsg class effectively handles complex logic for swaps and bridges in the Oraichain ecosystem. However, there are some areas where it could be improved:

  1. Consider breaking down larger methods (like genExecuteMsg) into smaller, more focused helper methods to improve readability and maintainability.
  2. Add more inline comments explaining complex logic, especially in methods like getSwapAndBridgeInfo and genExecuteMsg.
  3. Consider creating utility functions for common operations (like handling IBC transfers) to reduce code duplication across methods.
  4. Add JSDoc comments to methods to provide clear documentation of their purpose, parameters, and return values.

These improvements would enhance the overall structure, readability, and maintainability of the class. They would also make it easier for other developers to understand and work with the code in the future.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 34e0ebb and 7172104.

📒 Files selected for processing (3)
  • packages/universal-swap/src/msg/cosmos.ts (1 hunks)
  • packages/universal-swap/src/msg/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/osmosis.ts (1 hunks)
🔇 Additional comments (20)
packages/universal-swap/src/msg/cosmos.ts (7)

1-13: Import statements look good

The import statements are well-organized and include all necessary dependencies for the CosmosMsg class functionality. No unused imports are present.


15-21: Adjust the type of 'memo' parameter

The memo parameter is currently typed as string but assigned undefined by default. This can lead to type errors, especially with strict null checks enabled.

As previously suggested, consider updating the type of memo:

-    protected memo: string = undefined
+    protected memo?: string

This change makes the memo parameter optional and allows it to be either a string or undefined.


29-54: Improve robustness of getBridgeInfo method

There are a couple of potential issues in this method:

  1. The loop continues after finding a bridge action, which could lead to bridgeInfo being overwritten.
  2. There's no check to ensure bridgeInfo is defined before returning it.

To address these issues:

  1. Add a break statement after the switch block to exit the loop after finding the first bridge action:
        }
      }
+     break; // Exit the loop after finding the bridge action
    }
  1. Add a check before returning bridgeInfo:
+   if (!bridgeInfo) {
+     throw generateError('No bridge action found in path actions.');
+   }
    return bridgeInfo;

These changes will prevent potential overwrites and ensure that bridgeInfo is always defined when returned.


56-81: getPostAction method looks good

The method effectively handles both transfer to receiver and IBC transfer cases. It also includes appropriate error handling for unexpected scenarios. The logic is clear and well-structured.


85-98: Handle potential undefined bridgeInfo in genMemoAsMiddleware

The method currently doesn't check if bridgeInfo is defined before using it, which could lead to runtime errors.

To address this issue, add a check for bridgeInfo:

    let bridgeInfo = this.getBridgeInfo();
+   if (!bridgeInfo) {
+     throw generateError('Bridge information is missing.');
+   }

This change ensures that bridgeInfo is defined before it's used, preventing potential runtime errors.


104-126: Handle potential undefined bridgeInfo in genExecuteMsg

The method currently doesn't check if bridgeInfo is defined before using it, which could lead to runtime errors.

To address this issue, add a check for bridgeInfo:

    let bridgeInfo = this.getBridgeInfo();
+   if (!bridgeInfo) {
+     throw generateError('Bridge information is missing.');
+   }

This change ensures that bridgeInfo is defined before it's used, preventing potential runtime errors.

Apart from this, the method looks good. It correctly generates the execute message for the Osmosis platform and includes appropriate error handling for unsupported source ports.


1-127: Overall assessment: Good implementation with minor improvements needed

The CosmosMsg class provides a comprehensive set of methods for handling token swaps on the Oraichain platform. The overall structure and functionality are well-designed and appropriate for the intended purpose.

A few minor improvements have been suggested throughout the review:

  1. Adjusting the type of the memo parameter in the constructor.
  2. Improving the robustness of the getBridgeInfo method.
  3. Handling potential undefined bridgeInfo in genMemoAsMiddleware and genExecuteMsg methods.

Once these small issues are addressed, the implementation will be more robust and less prone to potential runtime errors. Good job on creating a well-structured and functional class for handling complex token swap operations.

packages/universal-swap/src/msg/osmosis.ts (4)

1-15: LGTM: Imports are relevant and well-organized.

The imports are appropriate for the functionality of the OsmosisMsg class, covering necessary types, utilities, and external dependencies.


76-76: ⚠️ Potential issue

Fix typo in error message

There's a typo in the error message: "bride" should be "bridge".

- throw generateError("Only support swap + bride on Osmosis");
+ throw generateError("Only support swap + bridge on Osmosis");

Likely invalid or redundant comment.


271-276: ⚠️ Potential issue

Swap 'amount' and 'denom' in the funds array for native tokens

In the native token case, the amount and denom fields in the funds array are swapped. The amount should be this.path.tokenInAmount, and the denom should be bridgeInfo.fromToken.

 funds: [
   {
-    amount: bridgeInfo.fromToken,
-    denom: this.path.tokenInAmount
+    amount: this.path.tokenInAmount,
+    denom: bridgeInfo.fromToken
   }
 ]

Likely invalid or redundant comment.


138-138: ⚠️ Potential issue

Fix incorrect array access using 'slice'

The code uses slice incorrectly to access the last element of swapOps. The slice method returns a portion of an array, but you need to access an element directly.

- let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out;
+ let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out;

Likely invalid or redundant comment.

packages/universal-swap/src/msg/oraichain.ts (9)

1-40: LGTM: Imports and class initialization

The imports and class initialization look good. The constructor properly validates the chain ID and path, which is a good practice for ensuring data integrity.


47-74: LGTM: Token conversion logic

The method correctly handles the conversion of different token types (CW20 and native) to pool IDs. The conditional logic is well-structured and easy to follow.


79-129: LGTM: Swap and bridge info generation

The getSwapAndBridgeInfo method effectively handles different action types and builds the necessary swap operations and bridge information. The logic is well-structured and easy to follow.


132-178: LGTM: Post-action generation for IBC transfers

The getProtoForPostAction method effectively handles different scenarios for post-actions in IBC transfers. The logic is well-structured and covers various cases comprehensively.


184-208: LGTM: IBC WASM memo generation

The genMemoForIbcWasm method effectively generates a memo for swap through IBC WASM after bridge. The use of protobuf encoding ensures a standardized message format. The logic is well-structured and covers all necessary components of the memo.


262-324: LGTM: Middleware memo generation

The genMemoAsMiddleware method effectively handles different cases for generating memos for actions on Oraichain as middleware. The logic is well-structured and covers both IBC transfer only and swap + action scenarios.


288-288: ⚠️ Potential issue

Fix incorrect usage of 'slice' method

The slice method is being used incorrectly. It should be called as a function, not accessed like an array.

Apply this diff to correct the usage of slice:

- let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out;
+ let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out;

Likely invalid or redundant comment.


47-47: ⚠️ Potential issue

Fix typo in method name

The method name pasreConverterMsgToPoolId contains a typo. It should be parseConverterMsgToPoolId.

Apply this diff to correct the method name:

- pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
+ parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {

Likely invalid or redundant comment.


249-249: ⚠️ Potential issue

Fix operator precedence in ternary operation

There's a potential issue with operator precedence in the ternary operation. The + operator has higher precedence than the ternary operator, which may lead to unexpected behavior.

Apply this diff to fix the operator precedence:

- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),

Likely invalid or redundant comment.

Comment on lines 18 to 19
SWAP_VENUE_NAME = "osmosis-poolmanager";
ENTRY_POINT_CONTRACT = "";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider initializing ENTRY_POINT_CONTRACT

The ENTRY_POINT_CONTRACT property is initialized as an empty string. This might cause issues if it's used without being set elsewhere. Consider initializing it with a default value or adding a method to set it after instantiation.

*/
getSwapAndBridgeInfo(): [SwapOperation[], BridgeMsgInfo] {
let swapOps: SwapOperation[] = [];
let bridgeInfo: BridgeMsgInfo;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Initialize bridgeInfo to avoid potential undefined errors

The bridgeInfo variable is declared but not initialized. This could lead to undefined errors when accessing its properties later if the Bridge action is not present.

Consider initializing bridgeInfo:

- let bridgeInfo: BridgeMsgInfo;
+ let bridgeInfo: BridgeMsgInfo | undefined;

Then, update the return statement to handle the case where bridgeInfo is undefined:

return [swapOps, bridgeInfo!];

This change ensures type safety while allowing bridgeInfo to be undefined if there's no Bridge action.

};
}

throw generateError("Missing postAction for universalSwap on Oraichain");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update error message to reference Osmosis instead of Oraichain

The error message mentions Oraichain, but this code is for Osmosis. Update the message for consistency:

- throw generateError("Missing postAction for universalSwap on Oraichain");
+ throw generateError("Missing postAction for universalSwap on Osmosis");
📝 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
throw generateError("Missing postAction for universalSwap on Oraichain");
throw generateError("Missing postAction for universalSwap on Osmosis");

Comment on lines 265 to 270
contract: this.ENTRY_POINT_CONTRACT,
msg: toUtf8(
JSON.stringify({
msg
})
),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove unnecessary wrapping of 'msg' in JSON for native tokens

In the native token case, the msg field is being wrapped incorrectly. The msg should not be nested under an additional msg key in the JSON.

 msg: toUtf8(
   JSON.stringify({
-    msg
+    ...msg
   })
 ),
📝 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
contract: this.ENTRY_POINT_CONTRACT,
msg: toUtf8(
JSON.stringify({
msg
})
),
contract: this.ENTRY_POINT_CONTRACT,
msg: toUtf8(
JSON.stringify({
...msg
})
),

Comment on lines 239 to 255
if (isCw20Token(bridgeInfo.fromToken)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: bridgeInfo.fromToken,
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
}
})
),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix JSON structure in CW20 token send message

The JSON structure for the send message in the CW20 token case is incorrect due to an extra nested send field.

 JSON.stringify({
   send: {
-    send: {
-      contract: this.ENTRY_POINT_CONTRACT,
-      amount: this.path.tokenInAmount,
-      msg: toBinary(msg)
-    }
+    contract: this.ENTRY_POINT_CONTRACT,
+    amount: this.path.tokenInAmount,
+    msg: toBinary(msg)
   }
 })
📝 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 (isCw20Token(bridgeInfo.fromToken)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: bridgeInfo.fromToken,
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
}
})
),
if (isCw20Token(bridgeInfo.fromToken)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: bridgeInfo.fromToken,
msg: toUtf8(
JSON.stringify({
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
})
),

Comment on lines 417 to 418
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix incorrect assignment of 'amount' and 'denom' in funds

The amount and denom are assigned incorrectly in the funds array. amount should be this.path.tokenInAmount and denom should be bridgeInfo.fromToken.

Apply these diffs to correct the assignments:

funds: [
  {
-   amount: bridgeInfo.fromToken,
-   denom: this.path.tokenInAmount
+   amount: this.path.tokenInAmount,
+   denom: bridgeInfo.fromToken
  }
]
funds: [
  {
-   amount: bridgeInfo.fromToken,
-   denom: this.path.tokenInAmount
+   amount: this.path.tokenInAmount,
+   denom: bridgeInfo.fromToken
  }
]

Also applies to: 491-492

Comment on lines 330 to 497
funds: []
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: ibcWasmContractAddress,
msg: toUtf8(
JSON.stringify({
transfer_to_remote: {
msg
}
})
),
funds: [
{
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
}
]
})
};
}

throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
}

let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out;
let min_asset = isCw20Token(tokenOutOfSwap)
? {
cw20: {
amount: this.minimumReceive,
address: tokenOutOfSwap
}
}
: {
native: {
amount: this.minimumReceive,
denom: tokenOutOfSwap
}
};

// swap and action
let msg: ExecuteMsg = {
swap_and_action: {
affiliates: [],
min_asset,
post_swap_action: this.getPostAction(bridgeInfo),
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
user_swap: {
swap_exact_asset_in: { swap_venue_name: this.SWAP_VENUE_NAME, operations: swapOps }
}
}
};

// if asset info is native => send native way, else send cw20 way
if (isCw20Token(bridgeInfo.fromToken)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: bridgeInfo.fromToken,
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
}
})
),
funds: []
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: this.ENTRY_POINT_CONTRACT,
msg: toUtf8(
JSON.stringify({
msg
})
),
funds: [
{
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
}
]
})
};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refactoring to improve code organization

The genExecuteMsg method is quite long and handles multiple cases. Consider refactoring it into smaller, more focused methods to improve readability and maintainability.

Consider breaking down the genExecuteMsg method into smaller helper methods, each handling a specific case (IBC transfer, IBC WASM transfer, swap and action). This would make the code easier to understand and maintain.

Comment on lines 1 to 497
funds: []
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: ibcWasmContractAddress,
msg: toUtf8(
JSON.stringify({
transfer_to_remote: {
msg
}
})
),
funds: [
{
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
}
]
})
};
}

throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
}

let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out;
let min_asset = isCw20Token(tokenOutOfSwap)
? {
cw20: {
amount: this.minimumReceive,
address: tokenOutOfSwap
}
}
: {
native: {
amount: this.minimumReceive,
denom: tokenOutOfSwap
}
};

// swap and action
let msg: ExecuteMsg = {
swap_and_action: {
affiliates: [],
min_asset,
post_swap_action: this.getPostAction(bridgeInfo),
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
user_swap: {
swap_exact_asset_in: { swap_venue_name: this.SWAP_VENUE_NAME, operations: swapOps }
}
}
};

// if asset info is native => send native way, else send cw20 way
if (isCw20Token(bridgeInfo.fromToken)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: bridgeInfo.fromToken,
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
}
})
),
funds: []
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: this.ENTRY_POINT_CONTRACT,
msg: toUtf8(
JSON.stringify({
msg
})
),
funds: [
{
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
}
]
})
};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling and consistency

While error handling is generally consistent using the generateError function, some improvements could be made:

  1. Some error messages could be more specific or informative. For example, the error message on line 124 could include more context about why only certain operations are supported.
  2. Consider adding more detailed error messages in complex logic sections to aid in debugging.
  3. In some places, like line 370, the error message could include the actual invalid value to help with troubleshooting.

Review all error messages in the class and ensure they provide sufficient context and information for effective debugging and error handling.

break;
}
default:
throw generateError("Only support swap + convert + bride on Oraichain");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in error message

There's a typo in the error message. "bride" should be "bridge".

Apply this diff to correct the error message:

- throw generateError("Only support swap + convert + bride on Oraichain");
+ throw generateError("Only support swap + convert + bridge on Oraichain");
📝 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
throw generateError("Only support swap + convert + bride on Oraichain");
throw generateError("Only support swap + convert + bridge on Oraichain");

return {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix operator precedence in ternary operation

There's a potential issue with operator precedence in the ternary operation. The + operator has higher precedence than the ternary operator, which may lead to unexpected behavior.

Apply this diff to fix the operator precedence:

- remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
📝 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
remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),

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

🧹 Outside diff range and nitpick comments (2)
packages/universal-swap/src/msg/oraichain.ts (2)

25-40: Consider improving error handling in the constructor

The constructor checks if the chainId is "Oraichain" and throws an error if it's not. However, it might be helpful to include the actual chainId in the error message for easier debugging.

Consider modifying the error message to include the actual chainId:

- throw generateError("This path must be on Oraichain");
+ throw generateError(`This path must be on Oraichain, but got ${path.chainId}`);

288-288: Improve array access for better robustness

When accessing the last element of the swapOps array, it's better to use array indexing instead of the slice method for better performance and clarity.

Consider changing the line to:

- let tokenOutOfSwap = swapOps.slice[swapOps.length - 1].denom_out;
+ let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 7172104 and 967e5e0.

📒 Files selected for processing (2)
  • packages/universal-swap/src/msg/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/osmosis.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/universal-swap/src/msg/osmosis.ts
🔇 Additional comments (6)
packages/universal-swap/src/msg/oraichain.ts (6)

184-208: LGTM: genMemoForIbcWasm implementation

The genMemoForIbcWasm method is well-implemented. It correctly generates the memo message for swap through IBC WASM after bridge, using the getSwapAndBridgeInfo method to get swap operations and bridge info.


47-47: ⚠️ Potential issue

Fix typo in method name

There's a typo in the method name. "pasre" should be "parse".

Apply this diff to correct the method name:

- pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
+ parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {

Likely invalid or redundant comment.


124-124: ⚠️ Potential issue

Fix typo in error message

There's a typo in the error message. "bride" should be "bridge".

Apply this diff to correct the error message:

- throw generateError("Only support swap + convert + bride on Oraichain");
+ throw generateError("Only support swap + convert + bridge on Oraichain");

Likely invalid or redundant comment.


491-492: ⚠️ Potential issue

Fix incorrect assignment of 'amount' and 'denom' in funds

The amount and denom are assigned incorrectly in the funds array. amount should be this.path.tokenInAmount and denom should be bridgeInfo.fromToken.

Apply this diff to correct the assignments:

funds: [
  {
-   amount: bridgeInfo.fromToken,
-   denom: this.path.tokenInAmount
+   amount: this.path.tokenInAmount,
+   denom: bridgeInfo.fromToken
  }
]

Likely invalid or redundant comment.


249-249: ⚠️ Potential issue

Fix operator precedence in ternary operation

There's a potential issue with operator precedence in the ternary operation. The + operator has higher precedence than the ternary operator, which may lead to unexpected behavior.

Apply this diff to fix the operator precedence:

- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),

Likely invalid or redundant comment.


170-170: ⚠️ Potential issue

Fix operator precedence in ternary operation

There's a potential issue with operator precedence in the ternary operation. The + operator has higher precedence than the ternary operator, which may lead to unexpected behavior.

Apply this diff to fix the operator precedence:

- remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),

Likely invalid or redundant comment.

Comment on lines 21 to 498
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: ibcWasmContractAddress,
msg: toUtf8(
JSON.stringify({
transfer_to_remote: {
msg
}
})
),
funds: [
{
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
}
]
})
};
}

throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
}

let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out;
let min_asset = isCw20Token(tokenOutOfSwap)
? {
cw20: {
amount: this.minimumReceive,
address: tokenOutOfSwap
}
}
: {
native: {
amount: this.minimumReceive,
denom: tokenOutOfSwap
}
};

// swap and action
let msg: ExecuteMsg = {
swap_and_action: {
affiliates: [],
min_asset,
post_swap_action: this.getPostAction(bridgeInfo),
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
user_swap: {
swap_exact_asset_in: { swap_venue_name: this.SWAP_VENUE_NAME, operations: swapOps }
}
}
};

// if asset info is native => send native way, else send cw20 way
if (isCw20Token(bridgeInfo.fromToken)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: bridgeInfo.fromToken,
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
}
})
),
funds: []
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: this.ENTRY_POINT_CONTRACT,
msg: toUtf8(
JSON.stringify({
msg
})
),
funds: [
{
amount: bridgeInfo.fromToken,
denom: this.path.tokenInAmount
}
]
})
};
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refactoring to improve code organization and error handling

The OraichainMsg class implements complex logic for handling swap and bridge operations. While the implementation is generally correct, consider the following improvements:

  1. Error handling: Implement a consistent error handling strategy across all methods. Consider creating custom error types for different scenarios.
  2. Code duplication: Some methods like getProtoForPostAction and getPostAction have similar structures. Consider extracting common logic into helper methods.
  3. Method length: Some methods, particularly genExecuteMsg, are quite long. Consider breaking them down into smaller, more focused methods for improved readability and maintainability.
  4. Comments: While there are some comments, adding more detailed documentation for complex logic sections would improve code maintainability.

Consider implementing these suggestions to improve the overall structure and maintainability of the OraichainMsg class.

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: 16

🧹 Outside diff range and nitpick comments (4)
packages/universal-swap/src/msg/chains/cosmos.ts (1)

43-44: Avoid unnecessary type assertions

In the fromChain and toChain properties, you are asserting this.path.chainId and this.path.tokenOutChainId as NetworkChainId. If these properties are already of type NetworkChainId, the assertions may be unnecessary.

Consider removing the unnecessary type assertions for cleaner code:

- fromChain: this.path.chainId as NetworkChainId,
- toChain: this.path.tokenOutChainId as NetworkChainId
+ fromChain: this.path.chainId,
+ toChain: this.path.tokenOutChainId

This enhances code readability and maintains type safety if the types are already compatible.

packages/universal-swap/src/msg/chains/osmosis.ts (1)

165-165: Unnecessary unary plus operator

The unary plus operator is used before calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT). If calculateTimeoutTimestamp already returns a number, the unary plus is unnecessary.

Consider removing the unary plus for clarity:

- timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
+ timeout_timestamp: calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
packages/universal-swap/src/msg/chains/oraichain.ts (2)

124-124: Correct typo in error message: "bride" should be "bridge"

In the error message, "Only support swap + convert + bride on Oraichain", the word "bride" is a typo and should be corrected to "bridge".

Apply this diff to fix the typo:

- throw generateError("Only support swap + convert + bride on Oraichain");
+ throw generateError("Only support swap + convert + bridge on Oraichain");

1-501: Add unit tests for the OraichainMsg class

Given the complexity and importance of the OraichainMsg class, it's crucial to have comprehensive unit tests to validate its functionality and prevent regressions.

Would you like assistance in setting up unit tests for this class? I can help draft some initial test cases.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 91da451 and 42bdc06.

📒 Files selected for processing (6)
  • packages/universal-swap/src/msg/chains/cosmos.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/index.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/osmosis.ts (1 hunks)
  • packages/universal-swap/src/msg/index.ts (1 hunks)
  • packages/universal-swap/src/msg/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/universal-swap/src/msg/chains/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/universal-swap/src/msg/index.ts
🧰 Additional context used
🪛 Biome
packages/universal-swap/src/msg/types.ts

[error] 34-34: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)


[error] 35-35: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

🔇 Additional comments (8)
packages/universal-swap/src/msg/types.ts (4)

1-4: LGTM: Import statements are appropriate.

The import statements are relevant to the types being defined in this file and there are no apparent unused imports.


6-10: LGTM: SwapMsgInfo interface is well-defined.

The SwapMsgInfo interface is properly structured to represent swap message information. The types used for each property are appropriate and align with the expected data for a swap operation.


12-23: LGTM: BridgeMsgInfo interface is comprehensive and flexible.

The BridgeMsgInfo interface is well-structured to represent bridge message information. It covers various aspects of a bridge operation, including source and destination details. The use of optional properties (memo and prefix) provides flexibility for different use cases.


25-31: LGTM: PostActionType enum is well-defined.

The PostActionType enum appropriately categorizes different types of post-actions, providing a clear set of options for use in the system.

packages/universal-swap/src/msg/chains/cosmos.ts (2)

107-129: ⚠️ Potential issue

Correct the error message to reflect the proper network

In the genExecuteMsg() method, the error message references "Oraichain" when it should reference "Osmosis", based on the method's purpose and comment.

Update the error message to accurately reflect the context:

if (bridgeInfo.sourcePort != "transfer") {
-  throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer");
+  throw generateError("Error generating executeMsg on Osmosis: Only support IBC transfer");
}

This improves the clarity and accuracy of the error message.

Likely invalid or redundant comment.


121-122: Ensure tokenInAmount and tokenIn are defined in Path

In the genExecuteMsg() method, you access this.path.tokenInAmount and this.path.tokenIn. Ensure that these properties are defined in the Path type and are properly initialized before use.

Run the following script to confirm that tokenInAmount and tokenIn are properties of Path:

This script checks the Path interface to ensure these properties exist and helps prevent potential runtime errors due to undefined properties.

✅ Verification successful

Verification Successful: Both tokenInAmount and tokenIn are defined in the Path interface and properly initialized.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that `tokenInAmount` and `tokenIn` are properties of `Path`

# Search for the definition of the Path interface
rg --type typescript -A 10 'interface Path' | rg 'tokenInAmount|tokenIn'

Length of output: 109


Script:

#!/bin/bash
# Description: Verify that `tokenInAmount` and `tokenIn` are properties of `Path`

# Search for the definition of the Path interface in .ts files
fd --extension ts --type file --exec rg -A 10 'interface Path' {} | rg 'tokenInAmount|tokenIn'

Length of output: 142

packages/universal-swap/src/msg/chains/osmosis.ts (2)

18-18: Confirm swap venue name

Ensure that SWAP_VENUE_NAME is correctly set to "osmosis-poolmanager" and matches the expected venue name in the Osmosis protocol.

Would you like me to confirm the swap venue name or provide updates if necessary?


201-209: Ensure correct handling of timeout timestamp

Verify that the timeoutTimestamp is correctly calculated and compatible with the IBC protocol requirements.

Run the following script to check the timeoutTimestamp value:

✅ Verification successful

Verified: timeoutTimestamp is correctly calculated and compatible with the IBC protocol requirements.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the timeout timestamp calculation.

# Test: Ensure calculateTimeoutTimestamp returns a valid number.
echo "Timeout Timestamp: $(node -e 'console.log(require("@oraichain/oraidex-common").calculateTimeoutTimestamp(require("@oraichain/oraidex-common").IBC_TRANSFER_TIMEOUT))')"

Length of output: 268


Script:

#!/bin/bash
# Description: Locate the implementation of calculateTimeoutTimestamp function.

ast-grep --lang typescript --pattern 'function calculateTimeoutTimestamp($_) { $$$ }' .

# Description: Find the declaration and value of IBC_TRANSFER_TIMEOUT.

rg 'export\s+const\s+IBC_TRANSFER_TIMEOUT\s*='

Length of output: 220


Script:

#!/bin/bash
# Description: Search for any occurrences of calculateTimeoutTimestamp in the codebase.

rg 'calculateTimeoutTimestamp' .

# Description: Locate the implementation of calculateTimeoutTimestamp function with a more flexible ast-grep pattern.

ast-grep --lang typescript --pattern '($_) => { $$$ }' .

Length of output: 63286


Script:

#!/bin/bash
# Description: Extract the implementation of calculateTimeoutTimestamp function.

rg 'export\s+const\s+calculateTimeoutTimestamp\s*=' packages/oraidex-common/src/helper.ts -A20

Length of output: 857

Comment on lines 33 to 36
export interface MiddleWareResponse {
memo: String;
receiver: String;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use lowercase 'string' type in MiddleWareResponse interface.

The MiddleWareResponse interface is using 'String' as a type, which is not consistent with TypeScript best practices. It's recommended to use lowercase 'string' instead.

Please apply the following changes:

 export interface MiddleWareResponse {
-  memo: String;
-  receiver: String;
+  memo: string;
+  receiver: string;
 }

This change aligns with TypeScript conventions and improves consistency in the codebase.

📝 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 interface MiddleWareResponse {
memo: String;
receiver: String;
}
export interface MiddleWareResponse {
memo: string;
receiver: string;
}
🧰 Tools
🪛 Biome

[error] 34-34: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)


[error] 35-35: Don't use 'String' as a type.

Use lowercase primitives for consistency.
Safe fix: Use 'string' instead

(lint/complexity/noBannedTypes)

Comment on lines 85 to 101
genMemoAsMiddleware(): MiddleWareResponse {
let bridgeInfo = this.getBridgeInfo();
// ibc bridge
return {
receiver: this.currentChainAddress,
memo: JSON.stringify({
forward: {
receiver: this.receiver,
port: bridgeInfo.sourcePort,
channel: bridgeInfo.sourceChannel,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
retries: 2,
next: this.memo
}
})
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Handle potential undefined bridgeInfo in genMemoAsMiddleware()

In the genMemoAsMiddleware() method, bridgeInfo is obtained by calling getBridgeInfo(). If bridgeInfo is undefined, accessing its properties will result in a runtime error.

Ensure that bridgeInfo is defined before using it:

let bridgeInfo = this.getBridgeInfo();

// Add a check to ensure bridgeInfo is defined
if (!bridgeInfo) {
  throw generateError("Failed to generate memo: bridgeInfo is undefined");
}

// Proceed to use bridgeInfo...

Alternatively, since getBridgeInfo() should now throw an error if bridgeInfo is undefined (as per previous suggestion), this method will not proceed if bridgeInfo is undefined.

Comment on lines 32 to 51
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Break the loop after finding the bridge action

In the getBridgeInfo() method, once the bridge action is found and bridgeInfo is initialized, the loop continues to iterate over the remaining actions. If there are multiple actions, this might not be intended.

Consider breaking out of the loop after initializing bridgeInfo:

for (let action of this.path.actions) {
  switch (action.type) {
    case ActionType.Bridge: {
      bridgeInfo = {
        // ... initialization ...
      };
+     // Break out of the loop after finding the bridge action
+     return bridgeInfo;
    }
    default:
      throw generateError(`Only support bridge on ${this.path.chainId}`);
  }
}

- return bridgeInfo;

This ensures that once the necessary bridgeInfo is obtained, the method returns immediately, avoiding unnecessary iterations.

📝 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
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
// Break out of the loop after finding the bridge action
return bridgeInfo;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}

Comment on lines 30 to 54
let bridgeInfo: BridgeMsgInfo;

for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}

return bridgeInfo;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure bridgeInfo is properly initialized before returning

In the getBridgeInfo() method, bridgeInfo may remain undefined if no action of type ActionType.Bridge is found in this.path.actions. Returning an undefined bridgeInfo could lead to runtime errors in methods that use this value without checking for undefined.

To fix this, consider adding a check after the loop to ensure bridgeInfo has been initialized:

  for (let action of this.path.actions) {
    switch (action.type) {
      case ActionType.Bridge: {
        bridgeInfo = {
          // ... initialization ...
        };
        break;
      }
      default:
        throw generateError(`Only support bridge on ${this.path.chainId}`);
    }
  }

+ if (!bridgeInfo) {
+   throw generateError("No bridge action found in path");
+ }

  return bridgeInfo;

This ensures that if no bridge action is found, an error is thrown, preventing undefined values from propagating.

📝 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
let bridgeInfo: BridgeMsgInfo;
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}
return bridgeInfo;
}
let bridgeInfo: BridgeMsgInfo;
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}
if (!bridgeInfo) {
throw generateError("No bridge action found in path");
}
return bridgeInfo;
}

Comment on lines +76 to +77
throw generateError("Only support swap + bride on Osmosis");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Typo in error message

There is a typo in the error message: "bride" should be "bridge".

Apply this diff to correct the typo:

- throw generateError("Only support swap + bride on Osmosis");
+ throw generateError("Only support swap + bridge on Osmosis");
📝 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
throw generateError("Only support swap + bride on Osmosis");
}
throw generateError("Only support swap + bridge on Osmosis");
}

protected memo: string = ""
) {
// check chainId = "osmosis-1"
if (path.chainId !== "osmosis-1") {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Hardcoded chain ID

The chain ID "osmosis-1" is hardcoded. Consider using a constant or an enumeration to improve maintainability and reduce the risk of typos.

Apply this change:

- if (path.chainId !== "osmosis-1") {
+ const OSMOSIS_CHAIN_ID = "osmosis-1";
+ if (path.chainId !== OSMOSIS_CHAIN_ID) {

Committable suggestion was skipped due to low confidence.

return {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix operator precedence in expressions involving ternary operators

The expressions using prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver may not evaluate as intended due to operator precedence. The + operator has higher precedence than the ternary ?: operator, leading to potential bugs.

Wrap the ternary expressions in parentheses to ensure correct evaluation:

- remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),

Apply similar changes to lines 170, 250, and 380 where this pattern occurs.

Also applies to: 250-250, 380-380

Comment on lines 161 to 164
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
prefix = this.destPrefix;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure destPrefix and obridgeAddress are validated before use

When isBridgeToEvm is true, the code throws an error if destPrefix or obridgeAddress are missing. Consider adding validation or default values during object construction to handle this case proactively.

You might initialize destPrefix and obridgeAddress in the constructor with default values or add checks before their usage.

Also applies to: 239-243, 368-372

* @param tokenOut The output token after conversion
* @returns The pool ID generated based on the provided input and output tokens
*/
pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in method name: pasreConverterMsgToPoolIdparseConverterMsgToPoolId

The method name pasreConverterMsgToPoolId appears to be misspelled. Renaming it to parseConverterMsgToPoolId enhances code readability and maintains consistency.

Apply this diff to correct the method name:

- pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
+ parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
📝 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
pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {

userSwap,
minimumReceive: this.minimumReceive,
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
postSwapAction: this.getProtoForPostAction(bridgeInfo),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Verify that bridgeInfo is defined before using in getProtoForPostAction

In the method genMemoForIbcWasm, bridgeInfo is passed to getProtoForPostAction without ensuring it's defined. If bridgeInfo is undefined, this could lead to runtime errors.

Add a check to ensure bridgeInfo is not undefined before using it:

let memo: Memo = {
  userSwap,
  minimumReceive: this.minimumReceive,
  timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
- postSwapAction: this.getProtoForPostAction(bridgeInfo),
+ postSwapAction: this.getProtoForPostAction(bridgeInfo || {}),
  recoveryAddr: this.currentChainAddress
};

Alternatively, handle the undefined case within getProtoForPostAction.

Committable suggestion was skipped due to low confidence.

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: 10

🧹 Outside diff range and nitpick comments (4)
packages/universal-swap/src/msg/chains/cosmos.ts (1)

20-20: Confirm Necessity of Default Value for memo Parameter

The constructor sets a default value of memo to an empty string. If an empty memo is acceptable in all contexts, this is fine. Otherwise, consider making memo a required parameter or handling cases where it might be unintentionally omitted.

packages/universal-swap/src/msg/msgs.ts (3)

27-27: Reminder: Address the FIXME comment

At line 27, there's a FIXME comment indicating that the calculation for the minimum receive amount is pending. Implementing this calculation is crucial to protect users from unfavorable slippage during swaps.

Do you need help implementing this calculation, or should we open a GitHub issue to track this task?


67-67: Reminder: Address the FIXME comment

At line 67, there's a FIXME comment indicating that the calculation for the minimum receive amount is pending. This is important to ensure users receive at least the minimum expected amount from their transactions.

Would you like assistance in implementing this calculation, or shall we create a GitHub issue to keep track of it?


6-6: Organize imports for better readability

Consider grouping and ordering imports logically—for example, third-party libraries first, followed by local modules. This improves readability and maintainability.

Example:

import { EncodeObject } from "@cosmjs/proto-signing";
import {
  generateError,
  ORAI_BRIDGE_EVM_DENOM_PREFIX,
  ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX,
} from "@oraichain/oraidex-common";

import { CosmosMsg, OraichainMsg, OsmosisMsg } from "./chains";
import { MiddleWareResponse } from "./types";
import { Path, Route } from "../types";

Also applies to: 7-7, 8-8

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 42bdc06 and 40e9135.

📒 Files selected for processing (7)
  • .gitignore (1 hunks)
  • packages/universal-swap/src/msg/chains/cosmos.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/osmosis.ts (1 hunks)
  • packages/universal-swap/src/msg/common.ts (1 hunks)
  • packages/universal-swap/src/msg/msgs.ts (1 hunks)
  • packages/universal-swap/src/msg/types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .gitignore
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/universal-swap/src/msg/chains/oraichain.ts
  • packages/universal-swap/src/msg/types.ts
🔇 Additional comments (7)
packages/universal-swap/src/msg/chains/osmosis.ts (7)

1-15: LGTM: Imports are appropriate and well-organized.

The imports cover all necessary types, functions, and constants required for the OsmosisMsg class implementation. There are no unused imports or missing dependencies.


40-82: Correct the typo in the error message.

The getSwapAndBridgeInfo method correctly processes both Swap and Bridge actions. However, there's a typo in the error message for unsupported actions.

The typo "bride" instead of "bridge" in the error message has already been identified in a previous review. Please ensure this is corrected as suggested earlier.


84-109: Correct the chain name in the error message.

The getPostAction method correctly handles both transfer and IBC transfer cases. However, there's an incorrect chain name in the error message.

The incorrect chain name "Oraichain" instead of "Osmosis" in the error message has already been identified in a previous review. Please ensure this is corrected as suggested earlier.


113-181: LGTM: genMemoAsMiddleware implementation is correct and comprehensive.

The genMemoAsMiddleware method correctly handles both IBC transfer only and swap with action cases. It properly utilizes the getSwapAndBridgeInfo method to obtain necessary information and constructs the appropriate memo format for each scenario.


187-282: Correct the chain name in the error message.

The genExecuteMsg method correctly handles both IBC transfer and swap with action cases, including proper handling of different token types. However, there's an incorrect chain name in an error message.

The incorrect chain name "Oraichain" instead of "Osmosis" in the error message has already been identified in a previous review. Please ensure this is corrected as suggested earlier.

Additionally, there's an issue with the CW20 send message structure that was identified in a previous review. Please ensure this is corrected as suggested earlier.


1-283: Overall implementation is solid, with a few minor issues to address.

The OsmosisMsg class provides a comprehensive implementation for handling swap and bridge operations on the Osmosis blockchain. The methods are well-structured and cover the necessary scenarios. However, there are a few minor issues that need to be addressed:

  1. Consider using a constant for the Osmosis chain ID for better maintainability.
  2. Correct the typos and incorrect chain names in error messages as pointed out in previous comments.
  3. Fix the CW20 send message structure as mentioned in a previous review.

Once these issues are resolved, the implementation will be robust and ready for use.


17-35: 🛠️ Refactor suggestion

Consider using a constant for the Osmosis chain ID.

The constructor correctly initializes the class and performs necessary validations. However, the Osmosis chain ID "osmosis-1" is hardcoded. Consider defining it as a constant at the top of the file or importing it from a constants file for better maintainability.

Apply this change:

+ const OSMOSIS_CHAIN_ID = "osmosis-1";

export class OsmosisMsg {
  // ... other code ...

  constructor(
    // ... other parameters ...
  ) {
-   if (path.chainId !== "osmosis-1") {
+   if (path.chainId !== OSMOSIS_CHAIN_ID) {
      throw generateError("This path must be on Osmosis");
    }
    // ... rest of the constructor ...
  }
}

Likely invalid or redundant comment.

Comment on lines +9 to +26
export const validatePath = (path: Path, hasSwap: boolean = true) => {
if (path.actions.length == 0) {
throw generateError("Require at least one action");
}
const numBridgeActions = path.actions.filter((action) => action.type === ActionType.Bridge).length;

if (numBridgeActions > 1) {
throw generateError("Only one Bridge action is allowed in the path");
}

if (numBridgeActions == 1 && path.actions[path.actions.length - 1].type !== ActionType.Bridge) {
throw generateError("Bridge action must be the last in the path");
}

if (!hasSwap && path.actions.some((action) => action.type == ActionType.Convert || action.type == ActionType.Swap)) {
throw generateError("Don't support swap action");
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider implementing internationalization and using TypeScript's Error class.

While the function logic is correct, there are still opportunities for improvement:

  1. The error messages are hardcoded strings, which makes future internationalization difficult.
  2. The function uses a custom generateError function instead of TypeScript's built-in Error class.

To address these issues and improve consistency with typical TypeScript error handling, consider the following refactor:

export const validatePath = (path: Path, hasSwap: boolean = true) => {
  if (path.actions.length == 0) {
    throw new Error('ERROR_EMPTY_PATH');
  }
  const numBridgeActions = path.actions.filter((action) => action.type === ActionType.Bridge).length;

  if (numBridgeActions > 1) {
    throw new Error('ERROR_MULTIPLE_BRIDGE_ACTIONS');
  }

  if (numBridgeActions == 1 && path.actions[path.actions.length - 1].type !== ActionType.Bridge) {
    throw new Error('ERROR_BRIDGE_NOT_LAST');
  }

  if (!hasSwap && path.actions.some((action) => action.type == ActionType.Convert || action.type == ActionType.Swap)) {
    throw new Error('ERROR_UNSUPPORTED_SWAP');
  }
};

This refactor uses error keys instead of hardcoded messages, preparing for future internationalization. You would need to implement a translation system to convert these keys into user-friendly messages, potentially in multiple languages.

Comment on lines +28 to +35
export const validateReceiver = (receiver: string, currentChainAddress: string, chainId: string) => {
if (!receiver || receiver == "") {
throw generateError(`Missing receiver when build msg in ${chainId}`);
}
if (!currentChainAddress || currentChainAddress == "") {
throw generateError(`Missing address of ${chainId}`);
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Implement internationalization and use TypeScript's Error class for consistency.

The validateReceiver function could benefit from the same improvements suggested for validatePath:

  1. Replace hardcoded error messages with error keys to facilitate future internationalization.
  2. Use TypeScript's built-in Error class for consistency with typical error handling.

Consider refactoring the function as follows:

export const validateReceiver = (receiver: string, currentChainAddress: string, chainId: string) => {
  if (!receiver || receiver === "") {
    throw new Error('ERROR_MISSING_RECEIVER');
  }
  if (!currentChainAddress || currentChainAddress === "") {
    throw new Error('ERROR_MISSING_CHAIN_ADDRESS');
  }
};

This refactor uses error keys instead of hardcoded messages, preparing for future internationalization. Implement a translation system to convert these keys into user-friendly messages, potentially in multiple languages.

Comment on lines +37 to +48
// This function checks whether the given address is a CW20 token address
export const isCw20Token = (address: string): boolean => {
try {
// Attempt to decode the address using the bech32 library
bech32.decode(address);
// If decoding is successful, return true as it is a valid CW20 token address
return true;
} catch (err) {
// If an error occurs during decoding, return false indicating that it is not a valid CW20 token address
return false;
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve function naming, documentation, and error handling.

The current implementation of isCw20Token has several areas for improvement:

  1. The function name is misleading. It's actually checking if the address is a valid bech32 address, not specifically a CW20 token.
  2. The comment above the function is slightly inaccurate.
  3. The function doesn't distinguish between different types of decoding errors.

Consider the following improvements:

  1. Rename the function to isValidBech32Address for clarity.
  2. Update the function's documentation to accurately describe its behavior.
  3. Implement more specific error handling if needed.

Here's a suggested refactor:

/**
 * Checks whether the given address is a valid bech32 address.
 * @param address The address to check
 * @returns true if the address is a valid bech32 address, false otherwise
 */
export const isValidBech32Address = (address: string): boolean => {
  try {
    bech32.decode(address);
    return true;
  } catch (err) {
    // For more specific error handling, you could check the error type
    // and return different results or log the error
    console.error('Error decoding address:', err);
    return false;
  }
};

This refactor improves the function name, updates the documentation, and adds a comment suggesting more specific error handling if needed in the future.

Comment on lines +68 to +81
if (bridgeInfo.sourcePort == "transfer") {
return {
ibc_transfer: {
ibc_info: {
source_channel: bridgeInfo.sourceChannel,
receiver: bridgeInfo.receiver,
memo: bridgeInfo.memo,
recover_address: this.currentChainAddress
}
}
};
}

throw generateError(`Missing postAction for universalSwap on ${this.path.chainId}`);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider Handling Additional Source Ports in getPostAction()

Currently, the getPostAction() method only supports bridgeInfo.sourcePort equal to "transfer". If there are scenarios where other source ports are valid, consider extending the logic to handle them.

If only "transfer" is supported intentionally, consider adding documentation or comments to clarify this constraint for future maintainers.

Comment on lines +110 to +112
if (bridgeInfo.sourcePort != "transfer") {
throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update Error Message to Reflect Correct Chain

In the genExecuteMsg() method, the error message references "Oraichain," but according to the comment, this method generates an execute message on Osmosis. To avoid confusion, update the error message to reflect the correct chain.

if (bridgeInfo.sourcePort != "transfer") {
-  throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer");
+  throw generateError("Error generating executeMsg on Osmosis: Only support IBC transfer");
}
📝 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 (bridgeInfo.sourcePort != "transfer") {
throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer");
}
if (bridgeInfo.sourcePort != "transfer") {
throw generateError("Error generating executeMsg on Osmosis: Only support IBC transfer");
}

Comment on lines 36 to 64
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["OraiBridge"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
let previousChain = path.chainId;
// we have 2 cases:
// - Previous chain use IBC bridge to Oraichain (noble)
// - Previous chain use IBC Wasm bridge to Oraichain
let msgInfo = previousChain == "noble-1" ? oraichainMsg.genMemoForIbcWasm() : oraichainMsg.genMemoAsMiddleware();
return msgInfo;
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor chain identifiers to use constants or enums

Currently, chain IDs like "Oraichain" and "osmosis-1" are hard-coded as string literals in the switch statement (lines 36-64). Using constants or enums for these chain identifiers can improve code maintainability and reduce the risk of typos.

Example:

// Define chain IDs as constants
const CHAIN_ID_ORAICHAIN = "Oraichain";
const CHAIN_ID_OSMOSIS = "osmosis-1";
// Other chain IDs...

// Then use them in the switch statement
switch (currentChain) {
  case CHAIN_ID_ORAICHAIN:
    // ...
  case CHAIN_ID_OSMOSIS:
    // ...
  // ...
}

Comment on lines 34 to 65
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["OraiBridge"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
let previousChain = path.chainId;
// we have 2 cases:
// - Previous chain use IBC bridge to Oraichain (noble)
// - Previous chain use IBC Wasm bridge to Oraichain
let msgInfo = previousChain == "noble-1" ? oraichainMsg.genMemoForIbcWasm() : oraichainMsg.genMemoAsMiddleware();
return msgInfo;
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Reduce code duplication between buildMemoSwap and buildExecuteMsg

The functions buildMemoSwap (lines 34-65) and buildExecuteMsg (lines 73-97) have similar switch statements and logic for handling different chains. Consider refactoring to extract common code into shared helper functions to improve maintainability and reduce duplication.

Example:

// Create a helper function for common logic
function generateChainSpecificData(
  path: Path,
  receiver: string,
  memo: string,
  addresses: { [chainId: string]: string },
  isExecuteMsg: boolean
): MiddleWareResponse | EncodeObject {
  // Common logic here...
}

// Then use the helper function in both `buildMemoSwap` and `buildExecuteMsg`

Also applies to: 73-97

addresses: { [chainId: string]: string }
): MiddleWareResponse => {
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for missing addresses

Variables currentAddress (lines 35 and 75) and oBridgeAddress (line 39) are accessed from the addresses object using dynamic keys. To prevent potential undefined values and runtime errors, ensure that these keys exist in the addresses object.

Consider adding checks or default values:

let currentAddress = addresses[currentChain];
if (!currentAddress) {
  throw generateError(`Address not found for chain ${currentChain}`);
}

let oBridgeAddress = addresses["OraiBridge"];
if (!oBridgeAddress) {
  throw generateError("OraiBridge address is missing in the addresses object");
}

Also applies to: 39-39, 75-75

if (prefix) {
return prefix;
} else if (chainId.startsWith("0x")) {
throw generateError(`Don't support bridge from Oraichain to ${chainId}`);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Improve error messages for unsupported chains

The error messages thrown at lines 22, 58, and 92 use contractions and might be less formal. Consider rephrasing them for clarity and professionalism.

Apply this diff to improve the error messages:

- throw generateError(`Don't support bridge from Oraichain to ${chainId}`);
+ throw generateError(`Bridging from Oraichain to ${chainId} is not supported`);

- throw generateError("Don't support universal swap in EVM");
+ throw generateError("Universal swap is not supported on EVM chains");

Also applies to: 58-58, 92-92

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: 12

🧹 Outside diff range and nitpick comments (5)
packages/universal-swap/src/msg/chains/chain.ts (2)

5-15: LGTM: Constructor is well-implemented with proper validations.

The constructor correctly initializes all properties and performs necessary validations. The use of validation functions enhances the robustness of the class.

Consider adding validation for minimumReceive to ensure it's a valid string representation of a number:

if (isNaN(Number(minimumReceive))) {
  throw new Error("minimumReceive must be a valid number string");
}

1-24: Overall, well-structured and robust implementation with room for minor improvements.

The ChainMsg class is well-designed, making good use of TypeScript features and following OOP principles. The code is clean, readable, and includes necessary validations.

To further improve the class, consider:

  1. Adding validation for minimumReceive in the constructor and setter method.
  2. Implementing getter methods for other properties if they need to be accessed outside the class.
  3. Adding JSDoc comments to describe the purpose of the class and its methods.

Example of adding JSDoc:

/**
 * Represents a chain message with path, minimum receive amount, receiver, and other details.
 */
export class ChainMsg {
  /**
   * Creates a new ChainMsg instance.
   * @param path - The path of the chain message.
   * @param minimumReceive - The minimum amount to receive.
   * @param receiver - The receiver's address.
   * @param currentChainAddress - The current chain address.
   * @param memo - Optional memo for the message.
   */
  constructor(
    // ... existing constructor code ...
  ) {
    // ... existing constructor body ...
  }

  // ... other methods ...
}

These improvements would enhance the maintainability and usability of the class.

packages/universal-swap/src/msg/chains/osmosis.ts (1)

31-43: Approved with a suggestion for precision handling

The setMinimumReceiveForSwap method correctly calculates the minimum receive amount based on slippage. However, truncating decimal values might lead to precision loss in some cases.

Consider using a more precise method for handling decimals, such as rounding down to the nearest integer instead of truncating. This could be achieved using Math.floor():

- if (minimumReceive.includes(".")) {
-   minimumReceive = minimumReceive.split(".")[0];
- }
+ minimumReceive = Math.floor(Number(minimumReceive)).toString();

This change ensures that the minimum receive amount is always rounded down to the nearest integer, which is more precise and consistent with typical swap implementations.

packages/universal-swap/src/msg/chains/oraichain.ts (2)

65-65: Consider implementing the TODO for querying the converter contract

The TODO comment suggests querying the converter contract to determine the appropriate conversion method. Implementing this could make the function more robust and adaptable to changes in the converter contract.


1-521: Overall assessment: Solid implementation with room for improvement

The OraichainMsg class provides a comprehensive framework for managing token swaps and bridging within the Oraichain network. The implementation is generally sound, but there are several areas that could be improved:

  1. Fix typos in method names and error messages.
  2. Address potential issues with operator precedence in string concatenations.
  3. Improve error handling and input validation in some methods.
  4. Consider refactoring longer methods (e.g., genExecuteMsg) for better maintainability.
  5. Update the ENTRY_POINT_CONTRACT with the correct mainnet address.

Addressing these points will enhance the reliability and maintainability of the code.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 40e9135 and fa54662.

📒 Files selected for processing (6)
  • packages/universal-swap/src/msg/chains/chain.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/cosmos.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/osmosis.ts (1 hunks)
  • packages/universal-swap/src/msg/msgs.ts (1 hunks)
  • packages/universal-swap/src/msg/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/universal-swap/src/msg/chains/cosmos.ts
  • packages/universal-swap/src/msg/types.ts
🔇 Additional comments (15)
packages/universal-swap/src/msg/chains/chain.ts (2)

1-2: LGTM: Imports are well-structured and relevant.

The imports are appropriately organized, using named imports for better clarity. The Path type and validation functions are correctly imported from their respective locations.


4-11: LGTM: Well-structured class definition.

The ChainMsg class is properly exported and uses protected properties, which is a good practice for encapsulation. The constructor parameters effectively initialize these properties.

packages/universal-swap/src/msg/msgs.ts (2)

1-9: Imports and type definitions look good.

The imports are well-organized and include necessary utilities, constants, and types from both external and local modules. This setup provides a solid foundation for the functionality implemented in this file.


1-125: Summary of review for packages/universal-swap/src/msg/msgs.ts

The file implements message generation for universal swap operations across different blockchain environments. While the overall structure and logic are sound, there are several areas for improvement:

  1. Error handling: Add checks for undefined values, especially when accessing properties from the addresses object.
  2. Code duplication: Refactor common logic in buildMemoSwap and buildExecuteMsg into shared helper functions.
  3. Compatibility: Replace Array.prototype.at(-1) with a more widely supported method to access the last element of an array.
  4. Constants: Use constants for chain IDs to improve maintainability and reduce the risk of typos.
  5. TODO comments: Address or provide more specific information for the TODO comments about calculating minimum receive amounts.
  6. Error messages: Rephrase error messages to avoid contractions and improve clarity.

Addressing these points will significantly improve the robustness, maintainability, and compatibility of the code.

packages/universal-swap/src/msg/chains/osmosis.ts (6)

122-190: Well-implemented memo generation logic

The genMemoAsMiddleware method effectively generates memos for both IBC transfer and swap + action scenarios. The implementation is comprehensive and correctly handles different cases.

The method demonstrates good error handling and proper construction of memo formats for various scenarios. It's particularly noteworthy how it handles the complexity of swap operations and IBC transfers within the same method.


1-292: Well-implemented OsmosisMsg class with minor improvements suggested

The OsmosisMsg class provides a comprehensive and well-structured implementation for handling swap and bridge operations on the Osmosis blockchain. The code demonstrates good error handling, type checking, and logical organization.

Key strengths of this implementation:

  1. Proper handling of different scenarios (swap, bridge, IBC transfer).
  2. Comprehensive memo generation for middleware operations.
  3. Correct construction of execute messages for both CW20 and native tokens.

The minor issues identified (typos in error messages, CW20 send structure) have been addressed in previous comments. Once these are resolved, the implementation will be highly robust and maintainable.

Great job on this implementation!


48-91: ⚠️ Potential issue

Correct the typo in the error message

The getSwapAndBridgeInfo method correctly handles Swap and Bridge actions. However, there's a typo in the error message for unsupported action types.

Apply this diff to correct the typo:

- throw generateError("Only support swap + bride on Osmosis");
+ throw generateError("Only support swap + bridge on Osmosis");

93-118: ⚠️ Potential issue

Correct the chain name in the error message

The getPostAction method correctly handles transfer and IBC transfer cases. However, there's an incorrect chain name in the error message.

Apply this diff to correct the error message:

- throw generateError("Missing postAction for universalSwap on Oraichain");
+ throw generateError("Missing postAction for universalSwap on Osmosis");

196-224: ⚠️ Potential issue

Correct the chain name in the error message

The first part of the genExecuteMsg method correctly handles IBC transfer and prepares for swap + action cases. However, there's an incorrect chain name in the error message.

Apply this diff to correct the error message:

- throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer");
+ throw generateError("Error on generate executeMsg on Osmosis: Only support ibc transfer");

225-291: ⚠️ Potential issue

Correct the CW20 send message structure

The second part of the genExecuteMsg method correctly handles both CW20 and native token cases. However, there's an extra nested send key in the CW20 send message structure.

Apply this diff to correct the message structure:

 msg: toUtf8(
   JSON.stringify({
     send: {
-      send: {
-        contract: this.ENTRY_POINT_CONTRACT,
-        amount: this.path.tokenInAmount,
-        msg: toBinary(msg)
-      }
+      contract: this.ENTRY_POINT_CONTRACT,
+      amount: this.path.tokenInAmount,
+      msg: toBinary(msg)
     }
   })
 ),
packages/universal-swap/src/msg/chains/oraichain.ts (5)

27-41: LGTM: Constructor implementation

The constructor correctly extends the base class and validates the path parameter to ensure it corresponds to "Oraichain".


282-350: LGTM: genMemoAsMiddleware implementation

The genMemoAsMiddleware method correctly handles both IBC transfer and swap + action cases. The logic for constructing the memo is sound and there are no apparent issues with the implementation.


25-25: ⚠️ Potential issue

Replace placeholder ENTRY_POINT_CONTRACT with the mainnet address

The ENTRY_POINT_CONTRACT is currently set to a placeholder address with a // FIXME comment. This should be updated to the correct mainnet contract address before deploying to production.


63-63: ⚠️ Potential issue

Fix typo in method name: pasreConverterMsgToPoolIdparseConverterMsgToPoolId

The method name pasreConverterMsgToPoolId appears to be misspelled. Renaming it to parseConverterMsgToPoolId enhances code readability and maintains consistency.


201-228: ⚠️ Potential issue

Verify that bridgeInfo is defined before using in getProtoForPostAction

In the method genMemoForIbcWasm, bridgeInfo is passed to getProtoForPostAction without ensuring it's defined. If bridgeInfo is undefined, this could lead to runtime errors.

Add a check to ensure bridgeInfo is not undefined before using it:

- postSwapAction: this.getProtoForPostAction(bridgeInfo),
+ postSwapAction: this.getProtoForPostAction(bridgeInfo || {}),

Alternatively, handle the undefined case within getProtoForPostAction.

Comment on lines +17 to +23
setMinimumReceive(minimumReceive: string) {
this.minimumReceive = minimumReceive;
}

getMinimumReceive() {
return this.minimumReceive;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Consider adding validation in the setter method.

While the getter method is correctly implemented, the setter method setMinimumReceive lacks validation. This could potentially allow invalid values to be set.

Add validation to the setMinimumReceive method:

setMinimumReceive(minimumReceive: string) {
  if (isNaN(Number(minimumReceive))) {
    throw new Error("minimumReceive must be a valid number string");
  }
  this.minimumReceive = minimumReceive;
}

This ensures that only valid number strings can be set as minimumReceive.

Comment on lines 27 to 69
//FIXME: calc minimum receive
const buildMemoSwap = (
path: Path,
receiver: string,
memo: string,
addresses: { [chainId: string]: string },
slippage: number = 0.01
): MiddleWareResponse => {
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["OraiBridge"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
oraichainMsg.setMinimumReceiveForSwap(slippage);
let previousChain = path.chainId;
// we have 2 cases:
// - Previous chain use IBC bridge to Oraichain (noble)
// - Previous chain use IBC Wasm bridge to Oraichain
let msgInfo = previousChain == "noble-1" ? oraichainMsg.genMemoForIbcWasm() : oraichainMsg.genMemoAsMiddleware();
return msgInfo;
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Improve buildMemoSwap function implementation

  1. The TODO comment about calculating the minimum receive amount should be addressed.
  2. Consider using constants for chain IDs to improve maintainability, as suggested in a previous review.
  3. The error message for unsupported EVM chains uses a contraction. Consider rephrasing it for clarity and professionalism.
  4. Add error handling for potentially undefined addresses[currentChain].

Apply these changes to address the issues:

  1. Implement the minimum receive calculation or add a more specific TODO with acceptance criteria.
  2. Define constants for chain IDs:
    const CHAIN_ID_ORAICHAIN = "Oraichain";
    const CHAIN_ID_OSMOSIS = "osmosis-1";
  3. Rephrase the error message:
    - throw generateError("Don't support universal swap in EVM");
    + throw generateError("Universal swap is not supported on EVM chains");
  4. Add error handling for undefined addresses:
    if (!currentAddress) {
      throw generateError(`Address not found for chain ${currentChain}`);
    }

Additionally, consider extracting the chain-specific logic into separate functions to improve readability and maintainability.

Comment on lines 108 to 125
export const generateMsgSwap = (route: Route, slippage: number = 0.01, addresses: { [chainId: string]: string }) => {
if (route.paths.length == 0) {
throw generateError("Require at least 1 action");
}
let memo: string = "";
let receiver = addresses[route.paths.at(-1)?.tokenOutChainId];

// generate memo for univeral swap
for (let i = route.paths.length - 1; i > 0; i--) {
let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses, slippage);
memo = swapInfo.memo;
receiver = swapInfo.receiver;
}

return buildExecuteMsg(route.paths[0], receiver, memo, addresses, slippage);

// generate execute msg
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve generateMsgSwap function for better compatibility and error handling

  1. The use of Array.prototype.at(-1) might not be supported in all environments. Consider using a more compatible approach to access the last element of the array.
  2. Add error handling for potentially undefined addresses[route.paths.at(-1)?.tokenOutChainId].

Apply these changes to address the issues:

  1. Replace Array.prototype.at(-1) with a more compatible approach:

    - let receiver = addresses[route.paths.at(-1)?.tokenOutChainId];
    + let lastPath = route.paths[route.paths.length - 1];
    + let receiver = addresses[lastPath?.tokenOutChainId];
  2. Add error handling for undefined addresses:

    if (!receiver) {
      throw generateError(`Address not found for the last path's tokenOutChainId`);
    }

These changes will improve the function's compatibility across different JavaScript environments and make it more robust against potential errors.

Comment on lines +19 to +29
export class OsmosisMsg extends ChainMsg {
SWAP_VENUE_NAME = "osmosis-poolmanager";
ENTRY_POINT_CONTRACT = "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7";

constructor(path: Path, minimumReceive: string, receiver: string, currentChainAddress: string, memo: string = "") {
super(path, minimumReceive, receiver, currentChainAddress, memo);
// check chainId = "osmosis-1"
if (path.chainId !== "osmosis-1") {
throw generateError("This path must be on Osmosis");
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a constant for the Osmosis chain ID

The chain ID "osmosis-1" is hardcoded in the constructor. To improve maintainability and reduce the risk of typos, consider using a constant or an enumeration for the chain ID.

Apply this change:

+ private static readonly OSMOSIS_CHAIN_ID = "osmosis-1";

  constructor(path: Path, minimumReceive: string, receiver: string, currentChainAddress: string, memo: string = "") {
    super(path, minimumReceive, receiver, currentChainAddress, memo);
    // check chainId  = "osmosis-1"
-   if (path.chainId !== "osmosis-1") {
+   if (path.chainId !== OsmosisMsg.OSMOSIS_CHAIN_ID) {
      throw generateError("This path must be on Osmosis");
    }
  }
📝 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 class OsmosisMsg extends ChainMsg {
SWAP_VENUE_NAME = "osmosis-poolmanager";
ENTRY_POINT_CONTRACT = "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7";
constructor(path: Path, minimumReceive: string, receiver: string, currentChainAddress: string, memo: string = "") {
super(path, minimumReceive, receiver, currentChainAddress, memo);
// check chainId = "osmosis-1"
if (path.chainId !== "osmosis-1") {
throw generateError("This path must be on Osmosis");
}
}
export class OsmosisMsg extends ChainMsg {
SWAP_VENUE_NAME = "osmosis-poolmanager";
ENTRY_POINT_CONTRACT = "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7";
private static readonly OSMOSIS_CHAIN_ID = "osmosis-1";
constructor(path: Path, minimumReceive: string, receiver: string, currentChainAddress: string, memo: string = "") {
super(path, minimumReceive, receiver, currentChainAddress, memo);
// check chainId = "osmosis-1"
if (path.chainId !== OsmosisMsg.OSMOSIS_CHAIN_ID) {
throw generateError("This path must be on Osmosis");
}
}

Comment on lines 389 to 390
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in error message (similar to previous methods)

The error message for missing prefix or OBridge address contains a typo: "os" should be "or".

Apply this diff to fix the issue:

- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");
📝 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 (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix or OBridge address for bridge to EVM");

Comment on lines +95 to +146
getSwapAndBridgeInfo(): [SwapOperation[], BridgeMsgInfo] {
let swapOps: SwapOperation[] = [];
let bridgeInfo: BridgeMsgInfo;

// build swap operations.
// we have 2 main cases:
// - swap + convert
// - bridge (IBC bridge or Ibc wasm bridge)
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Swap: {
let denomIn = action.tokenIn;
action.swapInfo.forEach((swapInfo) => {
swapOps.push({
denom_in: denomIn,
denom_out: swapInfo.tokenOut,
pool: swapInfo.poolId
});
denomIn = swapInfo.tokenOut;
});
break;
}
case ActionType.Convert: {
swapOps.push({
denom_in: action.tokenIn,
denom_out: action.tokenOut,
pool: this.pasreConverterMsgToPoolId(action.tokenIn, action.tokenOut)
});
break;
}
case ActionType.Bridge: {
bridgeInfo = {
amount: action.tokenInAmount,
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError("Only support swap + convert + bride on Oraichain");
}
}

return [swapOps, bridgeInfo];
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in error message and consider adding input validation

There's a typo in the error message on line 141: "bride" should be "bridge".

Additionally, consider adding input validation for the action object properties to ensure they contain the expected data before accessing them. This can help prevent potential runtime errors.

Apply this diff to fix the typo:

- throw generateError("Only support swap + convert + bride on Oraichain");
+ throw generateError("Only support swap + convert + bridge on Oraichain");
📝 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
getSwapAndBridgeInfo(): [SwapOperation[], BridgeMsgInfo] {
let swapOps: SwapOperation[] = [];
let bridgeInfo: BridgeMsgInfo;
// build swap operations.
// we have 2 main cases:
// - swap + convert
// - bridge (IBC bridge or Ibc wasm bridge)
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Swap: {
let denomIn = action.tokenIn;
action.swapInfo.forEach((swapInfo) => {
swapOps.push({
denom_in: denomIn,
denom_out: swapInfo.tokenOut,
pool: swapInfo.poolId
});
denomIn = swapInfo.tokenOut;
});
break;
}
case ActionType.Convert: {
swapOps.push({
denom_in: action.tokenIn,
denom_out: action.tokenOut,
pool: this.pasreConverterMsgToPoolId(action.tokenIn, action.tokenOut)
});
break;
}
case ActionType.Bridge: {
bridgeInfo = {
amount: action.tokenInAmount,
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError("Only support swap + convert + bride on Oraichain");
}
}
return [swapOps, bridgeInfo];
}
getSwapAndBridgeInfo(): [SwapOperation[], BridgeMsgInfo] {
let swapOps: SwapOperation[] = [];
let bridgeInfo: BridgeMsgInfo;
// build swap operations.
// we have 2 main cases:
// - swap + convert
// - bridge (IBC bridge or Ibc wasm bridge)
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Swap: {
let denomIn = action.tokenIn;
action.swapInfo.forEach((swapInfo) => {
swapOps.push({
denom_in: denomIn,
denom_out: swapInfo.tokenOut,
pool: swapInfo.poolId
});
denomIn = swapInfo.tokenOut;
});
break;
}
case ActionType.Convert: {
swapOps.push({
denom_in: action.tokenIn,
denom_out: action.tokenOut,
pool: this.pasreConverterMsgToPoolId(action.tokenIn, action.tokenOut)
});
break;
}
case ActionType.Bridge: {
bridgeInfo = {
amount: action.tokenInAmount,
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError("Only support swap + convert + bridge on Oraichain");
}
}
return [swapOps, bridgeInfo];
}

Comment on lines 230 to 278
getPostAction(bridgeInfo?: BridgeMsgInfo): Action {
// case 1: transfer to receiver
if (!bridgeInfo) {
return {
transfer: {
to_address: this.receiver
}
};
}

// case 2: ibc transfer
if (bridgeInfo.sourcePort == "transfer") {
return {
ibc_transfer: {
ibc_info: {
source_channel: bridgeInfo.sourceChannel,
receiver: bridgeInfo.receiver,
memo: bridgeInfo.memo,
recover_address: this.currentChainAddress
}
}
};
}

// case 3: ibc wasm transfer
if (bridgeInfo.sourcePort.startsWith("wasm")) {
// handle noble & evm case
let prefix = "";
let isBridgeToEvm = isEthAddress(this.receiver);
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
prefix = this.destPrefix;
}

return {
ibc_wasm_transfer: {
ibc_wasm_info: {
local_channel_id: bridgeInfo.sourceChannel,
remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
remote_denom: prefix + bridgeInfo.toToken,
memo: isBridgeToEvm ? prefix + this.receiver : this.memo
}
}
};
}

throw generateError("Missing postAction for postAction in Oraichain");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix operator precedence and typo in error message (similar to getProtoForPostAction)

The getPostAction method has similar issues to those found in getProtoForPostAction:

  1. There's an operator precedence issue in the IBC wasm transfer case.
  2. The error message for missing prefix or OBridge address contains a typo.

Apply these diffs to fix the issues:

  1. Fix operator precedence:
- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
  1. Fix typo in error message:
- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");
📝 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
getPostAction(bridgeInfo?: BridgeMsgInfo): Action {
// case 1: transfer to receiver
if (!bridgeInfo) {
return {
transfer: {
to_address: this.receiver
}
};
}
// case 2: ibc transfer
if (bridgeInfo.sourcePort == "transfer") {
return {
ibc_transfer: {
ibc_info: {
source_channel: bridgeInfo.sourceChannel,
receiver: bridgeInfo.receiver,
memo: bridgeInfo.memo,
recover_address: this.currentChainAddress
}
}
};
}
// case 3: ibc wasm transfer
if (bridgeInfo.sourcePort.startsWith("wasm")) {
// handle noble & evm case
let prefix = "";
let isBridgeToEvm = isEthAddress(this.receiver);
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
prefix = this.destPrefix;
}
return {
ibc_wasm_transfer: {
ibc_wasm_info: {
local_channel_id: bridgeInfo.sourceChannel,
remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
remote_denom: prefix + bridgeInfo.toToken,
memo: isBridgeToEvm ? prefix + this.receiver : this.memo
}
}
};
}
throw generateError("Missing postAction for postAction in Oraichain");
}
getPostAction(bridgeInfo?: BridgeMsgInfo): Action {
// case 1: transfer to receiver
if (!bridgeInfo) {
return {
transfer: {
to_address: this.receiver
}
};
}
// case 2: ibc transfer
if (bridgeInfo.sourcePort == "transfer") {
return {
ibc_transfer: {
ibc_info: {
source_channel: bridgeInfo.sourceChannel,
receiver: bridgeInfo.receiver,
memo: bridgeInfo.memo,
recover_address: this.currentChainAddress
}
}
};
}
// case 3: ibc wasm transfer
if (bridgeInfo.sourcePort.startsWith("wasm")) {
// handle noble & evm case
let prefix = "";
let isBridgeToEvm = isEthAddress(this.receiver);
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix or OBridge address for bridge to EVM");
prefix = this.destPrefix;
}
return {
ibc_wasm_transfer: {
ibc_wasm_info: {
local_channel_id: bridgeInfo.sourceChannel,
remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
remote_denom: prefix + bridgeInfo.toToken,
memo: isBridgeToEvm ? prefix + this.receiver : this.memo
}
}
};
}
throw generateError("Missing postAction for postAction in Oraichain");
}

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: 9

🧹 Outside diff range and nitpick comments (1)
packages/universal-swap/src/msg/msgs.ts (1)

1-155: Overall improvements for enhanced robustness and maintainability

This file implements crucial functionality for message generation in universal swap operations. While the overall structure is sound, consider the following improvements:

  1. Address TODO comments, particularly regarding the calculation of minimum receive amounts.
  2. Implement consistent error handling across all functions, especially for potentially undefined accesses.
  3. Reduce code duplication between buildMemoSwap and buildExecuteMsg by extracting common logic.
  4. Use constants for chain IDs to improve maintainability.
  5. Consider breaking down larger functions into smaller, more focused functions.
  6. Add input validation for function parameters, especially in exported functions.
  7. Ensure consistency in error handling and structure between similar functions like generateMsgSwap and generateMemoSwap.

Implementing these suggestions will significantly enhance the robustness, maintainability, and consistency of the code.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between fa54662 and 8e2e16f.

📒 Files selected for processing (5)
  • packages/universal-swap/src/msg/chains/cosmos.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/osmosis.ts (1 hunks)
  • packages/universal-swap/src/msg/msgs.ts (1 hunks)
  • packages/universal-swap/src/msg/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/universal-swap/src/msg/chains/cosmos.ts
  • packages/universal-swap/src/msg/chains/osmosis.ts
  • packages/universal-swap/src/msg/types.ts
🔇 Additional comments (7)
packages/universal-swap/src/msg/msgs.ts (1)

1-9: LGTM: Imports are appropriate and well-organized.

The imports cover both internal and external dependencies, including necessary utilities and types from "@oraichain/oraidex-common" and other modules. This setup provides a good foundation for the functionality implemented in this file.

packages/universal-swap/src/msg/chains/oraichain.ts (6)

282-350: LGTM: genMemoAsMiddleware implementation

The genMemoAsMiddleware method correctly handles both IBC transfer and swap + action scenarios. The implementation is well-structured and follows the expected logic for generating memos as middleware.


63-63: ⚠️ Potential issue

Fix typo in method name: pasreConverterMsgToPoolIdparseConverterMsgToPoolId

The method name pasreConverterMsgToPoolId appears to be misspelled. Renaming it to parseConverterMsgToPoolId enhances code readability and maintains consistency.

Apply this diff to correct the method name:

- pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
+ parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {

Likely invalid or redundant comment.


25-25: ⚠️ Potential issue

Update ENTRY_POINT_CONTRACT for mainnet

The ENTRY_POINT_CONTRACT is currently set to a placeholder address with a // FIXME comment. This should be updated to the correct mainnet contract address before deploying to production.

Please replace the placeholder address with the actual mainnet contract address:

- ENTRY_POINT_CONTRACT = "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn"; // FIXME: use mainnet
+ ENTRY_POINT_CONTRACT = "<mainnet_contract_address>";

Replace <mainnet_contract_address> with the actual contract address for the mainnet deployment.

Likely invalid or redundant comment.


220-220: ⚠️ Potential issue

Verify that bridgeInfo is defined before using in getProtoForPostAction

In the method genMemoForIbcWasm, bridgeInfo is passed to getProtoForPostAction without ensuring it's defined. If bridgeInfo is undefined, this could lead to runtime errors.

Add a check to ensure bridgeInfo is not undefined before using it:

let memo: Memo = {
  userSwap,
  minimumReceive: this.minimumReceive,
  timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
- postSwapAction: this.getProtoForPostAction(bridgeInfo),
+ postSwapAction: this.getProtoForPostAction(bridgeInfo || {}),
  recoveryAddr: this.currentChainAddress
};

Alternatively, handle the undefined case within getProtoForPostAction.

Likely invalid or redundant comment.


389-390: ⚠️ Potential issue

Fix typo in error message (similar to previous methods)

The error message for missing prefix or OBridge address contains a typo: "os" should be "or".

Apply this diff to fix the issue:

- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");

Likely invalid or redundant comment.


230-278: ⚠️ Potential issue

Fix operator precedence and typo in error message (similar to getProtoForPostAction)

The getPostAction method has similar issues to those found in getProtoForPostAction:

  1. There's an operator precedence issue in the IBC wasm transfer case.
  2. The error message for missing prefix or OBridge address contains a typo.

Apply these diffs to fix the issues:

  1. Fix operator precedence:
- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
  1. Fix typo in error message:
- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");

Likely invalid or redundant comment.

Comment on lines 28 to 72
const buildMemoSwap = (
path: Path,
receiver: string,
memo: string,
addresses: { [chainId: string]: string },
slippage: number = 0.01,
previousChain?: string
): MiddlewareResponse => {
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["OraiBridge"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
oraichainMsg.setMinimumReceiveForSwap(slippage);
// we have 2 cases:
// - Previous chain use IBC bridge to Oraichain
// - Previous chain use IBC Wasm bridge to Oraichain (EVM, noble)
let msgInfo =
previousChain && (previousChain == "noble-1" || previousChain.startsWith("0x"))
? oraichainMsg.genMemoForIbcWasm()
: oraichainMsg.genMemoAsMiddleware();
return msgInfo;
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor buildMemoSwap for improved maintainability and error handling

  1. Address the TODO comment about calculating the minimum receive amount.
  2. Add error handling for potentially undefined addresses[currentChain].
  3. Use constants for chain IDs to improve maintainability.
  4. Consider splitting the function into smaller, more focused functions.

Example improvements:

const CHAIN_ID_ORAICHAIN = "Oraichain";
const CHAIN_ID_OSMOSIS = "osmosis-1";

const buildMemoSwap = (
  path: Path,
  receiver: string,
  memo: string,
  addresses: { [chainId: string]: string },
  slippage: number = 0.01,
  previousChain?: string
): MiddlewareResponse => {
  let currentChain = path.chainId;
  let currentAddress = addresses[currentChain];
  if (!currentAddress) {
    throw generateError(`Address not found for chain ${currentChain}`);
  }

  // TODO: Implement minimum receive amount calculation
  // const minimumReceive = calculateMinimumReceive(path, slippage);

  switch (currentChain) {
    case CHAIN_ID_ORAICHAIN:
      return buildOraichainMemo(path, receiver, memo, currentAddress, addresses, slippage, previousChain);
    case CHAIN_ID_OSMOSIS:
      return buildOsmosisMemo(path, receiver, memo, currentAddress, slippage);
    default:
      return buildDefaultMemo(path, receiver, memo, currentAddress, slippage, currentChain);
  }
};

// Implement buildOraichainMemo, buildOsmosisMemo, and buildDefaultMemo as separate functions

This refactoring improves readability, maintainability, and error handling while addressing the main issues in the original function.

Comment on lines 75 to 109
const buildExecuteMsg = (
path: Path,
receiver: string,
memo: string,
addresses: { [chainId: string]: string },
slippage: number = 0.01
): EncodeObject => {
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["OraiBridge"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
oraichainMsg.setMinimumReceiveForSwap(slippage);
return oraichainMsg.genExecuteMsg();
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
return cosmosMsg.genExecuteMsg();
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
return cosmosMsg.genExecuteMsg();
}
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor buildExecuteMsg to reduce duplication and improve maintainability

  1. Address the TODO comment about calculating the minimum receive amount.
  2. Add error handling for potentially undefined addresses[currentChain].
  3. Reduce duplication with buildMemoSwap by extracting common logic.
  4. Consider splitting the function into smaller, more focused functions.

Example improvements:

const buildExecuteMsg = (
  path: Path,
  receiver: string,
  memo: string,
  addresses: { [chainId: string]: string },
  slippage: number = 0.01
): EncodeObject => {
  let currentChain = path.chainId;
  let currentAddress = addresses[currentChain];
  if (!currentAddress) {
    throw generateError(`Address not found for chain ${currentChain}`);
  }

  // TODO: Implement minimum receive amount calculation
  // const minimumReceive = calculateMinimumReceive(path, slippage);

  return buildChainSpecificExecuteMsg(currentChain, path, receiver, memo, currentAddress, addresses, slippage);
};

const buildChainSpecificExecuteMsg = (
  currentChain: string,
  path: Path,
  receiver: string,
  memo: string,
  currentAddress: string,
  addresses: { [chainId: string]: string },
  slippage: number
): EncodeObject => {
  switch (currentChain) {
    case CHAIN_ID_ORAICHAIN:
      return buildOraichainExecuteMsg(path, receiver, memo, currentAddress, addresses, slippage);
    case CHAIN_ID_OSMOSIS:
      return buildOsmosisExecuteMsg(path, receiver, memo, currentAddress, slippage);
    default:
      return buildDefaultExecuteMsg(path, receiver, memo, currentAddress, slippage, currentChain);
  }
};

// Implement buildOraichainExecuteMsg, buildOsmosisExecuteMsg, and buildDefaultExecuteMsg as separate functions

This refactoring reduces duplication, improves readability, and addresses the main issues in the original function. Consider extracting common logic between buildMemoSwap and buildExecuteMsg into shared utility functions to further reduce duplication.

Comment on lines 356 to 520
}
})
),
funds: []
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: ibcWasmContractAddress,
msg: toUtf8(
JSON.stringify({
transfer_to_remote: {
msg
}
})
),
funds: [
{
denom: this.path.tokenIn,
amount: this.path.tokenInAmount
}
]
})
};
}

throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
}

let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out;
let min_asset = isCw20Token(tokenOutOfSwap)
? {
cw20: {
amount: this.minimumReceive,
address: tokenOutOfSwap
}
}
: {
native: {
amount: this.minimumReceive,
denom: tokenOutOfSwap
}
};

// swap and action
let msg: ExecuteMsg = {
swap_and_action: {
affiliates: [],
min_asset,
post_swap_action: this.getPostAction(bridgeInfo),
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
user_swap: {
swap_exact_asset_in: { swap_venue_name: this.SWAP_VENUE_NAME, operations: swapOps }
}
}
};

// if asset info is native => send native way, else send cw20 way
if (isCw20Token(this.path.tokenIn)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: this.path.tokenIn,
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
}
})
),
funds: []
})
};
}
// native token

return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: this.ENTRY_POINT_CONTRACT,
msg: toUtf8(JSON.stringify(msg)),
funds: [
{
denom: this.path.tokenIn,
amount: this.path.tokenInAmount
}
]
})
};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refactoring genExecuteMsg for improved maintainability

The genExecuteMsg method is quite long and handles multiple complex cases. While the implementation seems correct, its length and complexity might make it harder to maintain and test.

Consider refactoring this method into smaller, more focused helper methods for each case (IBC transfer, IBC wasm transfer, and swap and action). This would improve readability and make the code easier to maintain and test.

Example refactoring:

private genIbcTransferMsg(bridgeInfo: BridgeMsgInfo): EncodeObject {
  // Implementation for IBC transfer
}

private genIbcWasmTransferMsg(bridgeInfo: BridgeMsgInfo): EncodeObject {
  // Implementation for IBC wasm transfer
}

private genSwapAndActionMsg(swapOps: SwapOperation[], bridgeInfo?: BridgeMsgInfo): EncodeObject {
  // Implementation for swap and action
}

genExecuteMsg(): EncodeObject {
  let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo();

  if (swapOps.length === 0) {
    if (bridgeInfo.sourcePort === "transfer") {
      return this.genIbcTransferMsg(bridgeInfo);
    }
    if (bridgeInfo.sourcePort.startsWith("wasm")) {
      return this.genIbcWasmTransferMsg(bridgeInfo);
    }
    throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
  }

  return this.genSwapAndActionMsg(swapOps, bridgeInfo);
}

This refactoring would make the genExecuteMsg method more concise and easier to understand at a glance.

break;
}
default:
throw generateError("Only support swap + convert + bride on Oraichain");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in error message

There's a typo in the error message: "bride" should be "bridge".

Apply this diff to fix the typo:

- throw generateError("Only support swap + convert + bride on Oraichain");
+ throw generateError("Only support swap + convert + bridge on Oraichain");
📝 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
throw generateError("Only support swap + convert + bride on Oraichain");
throw generateError("Only support swap + convert + bridge on Oraichain");

Comment on lines 149 to 195
getProtoForPostAction(bridgeInfo?: BridgeMsgInfo): Memo_PostAction {
// case 1: transfer to receiver
if (!bridgeInfo) {
return {
transferMsg: {
toAddress: this.receiver
}
};
}

// case 2: ibc transfer
if (bridgeInfo.sourcePort == "transfer") {
return {
ibcTransferMsg: {
sourceChannel: bridgeInfo.sourceChannel,
sourcePort: bridgeInfo.sourcePort,
receiver: bridgeInfo.receiver,
memo: bridgeInfo.memo,
recoverAddress: this.currentChainAddress
}
};
}

// case 3: ibc wasm transfer

if (bridgeInfo.sourcePort.startsWith("wasm")) {
// handle noble & evm case
let prefix = "";
let isBridgeToEvm = isEthAddress(this.receiver);
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
prefix = this.destPrefix;
}

return {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
remoteDenom: prefix + bridgeInfo.toToken,
memo: isBridgeToEvm ? prefix + this.receiver : this.memo
}
};
}

throw generateError("Missing postAction for ibc wasm memo");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix operator precedence and typo in error message

  1. There's an operator precedence issue in the IBC wasm transfer case. The + operator has higher precedence than the ternary operator, which may lead to unexpected behavior.

  2. The error message for missing prefix or OBridge address contains a typo: "os" should be "or".

Apply these diffs to fix the issues:

  1. Fix operator precedence:
- remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
  1. Fix typo in error message:
- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");
📝 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
getProtoForPostAction(bridgeInfo?: BridgeMsgInfo): Memo_PostAction {
// case 1: transfer to receiver
if (!bridgeInfo) {
return {
transferMsg: {
toAddress: this.receiver
}
};
}
// case 2: ibc transfer
if (bridgeInfo.sourcePort == "transfer") {
return {
ibcTransferMsg: {
sourceChannel: bridgeInfo.sourceChannel,
sourcePort: bridgeInfo.sourcePort,
receiver: bridgeInfo.receiver,
memo: bridgeInfo.memo,
recoverAddress: this.currentChainAddress
}
};
}
// case 3: ibc wasm transfer
if (bridgeInfo.sourcePort.startsWith("wasm")) {
// handle noble & evm case
let prefix = "";
let isBridgeToEvm = isEthAddress(this.receiver);
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
prefix = this.destPrefix;
}
return {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
remoteDenom: prefix + bridgeInfo.toToken,
memo: isBridgeToEvm ? prefix + this.receiver : this.memo
}
};
}
throw generateError("Missing postAction for ibc wasm memo");
}
getProtoForPostAction(bridgeInfo?: BridgeMsgInfo): Memo_PostAction {
// case 1: transfer to receiver
if (!bridgeInfo) {
return {
transferMsg: {
toAddress: this.receiver
}
};
}
// case 2: ibc transfer
if (bridgeInfo.sourcePort == "transfer") {
return {
ibcTransferMsg: {
sourceChannel: bridgeInfo.sourceChannel,
sourcePort: bridgeInfo.sourcePort,
receiver: bridgeInfo.receiver,
memo: bridgeInfo.memo,
recoverAddress: this.currentChainAddress
}
};
}
// case 3: ibc wasm transfer
if (bridgeInfo.sourcePort.startsWith("wasm")) {
// handle noble & evm case
let prefix = "";
let isBridgeToEvm = isEthAddress(this.receiver);
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix or OBridge address for bridge to EVM");
prefix = this.destPrefix;
}
return {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
remoteDenom: prefix + bridgeInfo.toToken,
memo: isBridgeToEvm ? prefix + this.receiver : this.memo
}
};
}
throw generateError("Missing postAction for ibc wasm memo");
}

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: 10

🧹 Outside diff range and nitpick comments (10)
packages/oraidex-common/src/celestia-network.ts (3)

1-3: Remove unused import defaultBech32Config

The defaultBech32Config is imported but not used in this file. Consider removing it to keep the imports clean.

Apply this diff to remove the unused import:

-import { CustomChainInfo, defaultBech32Config } from "./network";
+import { CustomChainInfo } from "./network";

15-19: LGTM: Correct network configuration with a minor suggestion

The network type, chain ID, chain name, and chain symbol image URL are correctly set for the Celestia network.

Consider defining the base URL for images as a constant, as it's used multiple times in this file. This would make future updates easier if the image source changes.

const KEPLR_CHAIN_REGISTRY_BASE_URL = "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia";

Then use it like this:

chainSymbolImageUrl: `${KEPLR_CHAIN_REGISTRY_BASE_URL}/chain.png`,

44-53: LGTM: Correct endpoint and staking configurations with a minor suggestion

The RPC and REST endpoints, stake currency configuration, and wallet URL for staking are all correctly set and appropriate for the Celestia network.

For consistency, consider using the same image URL for the stake currency as used in the main currency configuration. Currently, the stake currency uses a different path (/utia.png instead of /chain.png). Unless this is intentional, you might want to use the same image for both:

-    coinImageUrl: "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia/utia.png"
+    coinImageUrl: "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia/chain.png"
packages/universal-swap/src/universal-demos/alpha-smart-router.ts (3)

84-84: Consider externalizing the hardcoded fromAmount.

The fromAmount is currently set to 10. Externalizing this value to a configuration file or making it an input parameter can enhance flexibility and make testing with different amounts easier.


Line range hint 91-91: Externalize the hardcoded EVM sender address for flexibility.

The EVM sender address "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797" is hardcoded. Consider externalizing it to an environment variable or configuration file to improve flexibility and maintainability.

🧰 Tools
🪛 Gitleaks

16-16: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


18-18: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


25-25: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


27-27: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


39-39: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


48-48: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


50-50: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


56-56: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


63-63: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


Line range hint 97-99: Clean up commented-out recipientAddress lines.

The code contains commented-out recipientAddress entries. Removing or uncommenting these lines will improve code readability.

🧰 Tools
🪛 Gitleaks

16-16: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


18-18: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


25-25: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


27-27: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


39-39: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


48-48: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


50-50: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


56-56: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


63-63: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

packages/universal-swap/src/helper.ts (3)

314-321: Rename variable 'approve' to 'addresses' in 'getAddress' method for clarity.

In the getAddress method, the variable approve holds a mapping of coin types to addresses. Renaming it to addresses would enhance readability and make the code more self-explanatory.

Apply this diff to rename the variable:

 static getAddress = (prefix: string, { address60, address118 }, coinType: number = 118) => {
-    const approve = {
+    const addresses = {
     };
-    const { data } = fromBech32(approve[coinType]);
+    const { data } = fromBech32(addresses[coinType]);
     return toBech32(prefix, data);
 };

375-375: Clarify error messages in 'addOraiBridgeRoute' method.

The error messages in lines 375 and 379 may be unclear or confusing to users. Consider rephrasing them to provide clearer guidance.

Apply this diff to improve the error messages:

-      if (!alphaSmartRoute) throw generateError(`Missing router with alpha ibc wasm!`);
+      if (!alphaSmartRoute) throw generateError(`AlphaSmartRoute is missing for alpha IBC WASM operation.`);

 ...

-      if (alphaSmartRoute.routes.length > 1) throw generateError(`Missing router with alpha ibc wasm max length!`);
+      if (alphaSmartRoute.routes.length > 1) throw generateError(`AlphaSmartRoute must contain exactly one route for alpha IBC WASM operation.`);

Also applies to: 379-379


338-338: Improve clarity of error message when 'sourceReceiver' is empty.

The error message in line 338 could be rephrased for better clarity. Instead of "Cannot get source if the sourceReceiver is empty!", consider using "The 'sourceReceiver' address is required and cannot be empty."

Apply this diff to enhance the error message:

-      if (!addresses.sourceReceiver) throw generateError(`Cannot get source if the sourceReceiver is empty!`);
+      if (!addresses.sourceReceiver) throw generateError(`The 'sourceReceiver' address is required and cannot be empty.`);
packages/universal-swap/src/handler.ts (1)

Line range hint 715-753: Rename method 'caculateMinimumReceive' to 'calculateMinimumReceive'.

The method caculateMinimumReceive is misspelled in its definition. It should be calculateMinimumReceive().

Apply this diff to correct the method name:

       // ...
-      async caculateMinimumReceive() {
+      async calculateMinimumReceive() {
         const { relayerFee, simulateAmount, originalToToken, bridgeFee = 1, userSlippage = 0 } = this.swapData;
         // method implementation
       }

Also, ensure all references to this method are updated accordingly.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8e2e16f and 14883e4.

📒 Files selected for processing (12)
  • packages/oraidex-common/package.json (1 hunks)
  • packages/oraidex-common/src/celestia-network.ts (1 hunks)
  • packages/oraidex-common/src/constant.ts (1 hunks)
  • packages/oraidex-common/src/ibc-info.ts (2 hunks)
  • packages/oraidex-common/src/index.ts (1 hunks)
  • packages/oraidex-common/src/network.ts (4 hunks)
  • packages/universal-swap/package.json (2 hunks)
  • packages/universal-swap/src/handler.ts (4 hunks)
  • packages/universal-swap/src/helper.ts (10 hunks)
  • packages/universal-swap/src/types.ts (5 hunks)
  • packages/universal-swap/src/universal-demos/alpha-smart-router.ts (3 hunks)
  • packages/universal-swap/tests/helper.spec.ts (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/oraidex-common/package.json
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/universal-swap/package.json
🧰 Additional context used
🪛 Gitleaks
packages/universal-swap/src/universal-demos/alpha-smart-router.ts

16-16: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


18-18: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


25-25: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


27-27: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


39-39: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


48-48: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


50-50: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


56-56: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


63-63: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (27)
packages/oraidex-common/src/index.ts (1)

14-14: New export added for celestia-network module

The addition of export * from "./celestia-network"; is consistent with the existing export statements in this file. This change expands the public API of the oraidex-common package by including all exports from the celestia-network module.

However, there are a few points to consider:

  1. Ensure that exporting all contents from the celestia-network module is intentional, as it may expose more functionality than necessary.
  2. Verify that the celestia-network module exists and is correctly implemented.
  3. Update any relevant documentation to reflect the newly available exports.

To verify the existence and contents of the celestia-network module, please run the following script:

✅ Verification successful

celestia-network Module Export Verified Successfully

The celestia-network.ts file exists and is correctly exported in packages/oraidex-common/src/index.ts. The exports from celestia-network are utilized within the codebase as shown in network.ts.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the existence and contents of the celestia-network module

# Check if the file exists
if [ -f "packages/oraidex-common/src/celestia-network.ts" ]; then
    echo "File celestia-network.ts exists."
    echo "Contents of celestia-network.ts:"
    cat "packages/oraidex-common/src/celestia-network.ts"
else
    echo "Error: celestia-network.ts file not found in the expected location."
fi

# Search for any usage of exports from celestia-network
echo "Searching for usage of celestia-network exports:"
rg --type typescript "from ['\"]\.\.?/celestia-network['\"]" packages/

Length of output: 2142


Script:

#!/bin/bash
# Description: Verify usage of celestia-network exports without specifying file type

echo "Searching for usage of celestia-network exports without specifying file type:"
rg "from ['\"]\.\.?/celestia-network['\"]" packages/

Length of output: 389

packages/oraidex-common/src/celestia-network.ts (2)

4-14: LGTM: Correct Bech32 configuration and BIP44 coin type

The Bech32 prefixes are correctly set for the Celestia network, and the BIP44 coin type (118) is appropriate for Cosmos-based chains.


30-43: LGTM: Fee currency configuration with a suggestion to verify gas prices

The fee currency configuration for "TIA" is correct and matches the main currency configuration.

Please verify if the gasPriceStep values (low: 0.01, average: 0.02, high: 0.1) are the correct and most up-to-date values for the Celestia network. These values can impact transaction costs, so it's crucial to ensure they are accurate.

To verify these values, you could check the official Celestia documentation or run a script to query the current gas prices from a Celestia node:

Note: This script assumes the RPC endpoint is accessible and returns gas price information. Adjust the endpoint if necessary.

packages/universal-swap/src/types.ts (6)

14-18: LGTM: ActionType enum addition

The new ActionType enum clearly defines the main actions in the universal swap system. The naming is concise and follows common conventions.


Line range hint 52-82: LGTM: Addition of affiliates to UniversalSwapData

The addition of the optional affiliates property to the UniversalSwapData interface enhances the system's capability to handle affiliate-related data. This change maintains backward compatibility and uses the imported Affiliate type for type safety.


99-99: LGTM: Addition of isAlphaIbcWasm to SwapOptions

The new isAlphaIbcWasm property in the SwapOptions interface provides a configuration option for alpha/experimental IBC WASM functionality. This boolean flag allows for easy feature toggling and follows the existing naming conventions in the interface.


Line range hint 217-225: LGTM: Exporting Path interface

The Path interface is now exported, making it accessible to other modules. This change promotes reusability and allows external components to utilize this type definition. The interface structure remains unchanged, ensuring compatibility with existing code.


269-269: LGTM: Addition of maxSplits to RouterConfigSmartRoute

The new maxSplits property in the RouterConfigSmartRoute interface provides a way to configure the maximum number of splits in a smart route. This optional property enhances the smart routing capabilities while maintaining backward compatibility.


Line range hint 1-271: Overall assessment: Enhancements to universal swap types

The changes in this file improve the universal swap system by:

  1. Introducing a new ActionType enum for categorizing main actions.
  2. Adding support for affiliates in the UniversalSwapData interface.
  3. Introducing a new configuration option for alpha IBC WASM functionality.
  4. Exporting the Path interface for better reusability.
  5. Adding a maxSplits property to the RouterConfigSmartRoute for enhanced smart routing control.

These modifications enhance the system's flexibility and functionality while maintaining backward compatibility. The changes are well-structured and consistent with the existing codebase.

packages/oraidex-common/src/constant.ts (3)

182-182: LGTM: Improved enum maintainability

The addition of a trailing comma after the NOBLE_CHAIN_ID entry is a good practice. It enhances code maintainability by allowing easier addition of new entries in the future without modifying this line. This change aligns with common JavaScript/TypeScript style guides.


182-183: Summary: Minor enhancements and Celestia support added

The changes in this file are minimal but impactful:

  1. Added a trailing comma to the NOBLE_CHAIN_ID entry, improving maintainability.
  2. Introduced support for the Celestia network by adding a new CELESTIA_CHAIN_ID entry to the COSMOS_CHAIN_ID_COMMON enum.

These changes enhance the SDK's flexibility and expand its supported networks. The modifications are well-structured and consistent with the existing code. However, it's important to ensure that any other parts of the codebase that rely on the COSMOS_CHAIN_ID_COMMON enum are updated accordingly to handle the new Celestia chain ID.


183-183: LGTM: Added support for Celestia chain ID

The addition of CELESTIA_CHAIN_ID = "celestia" to the COSMOS_CHAIN_ID_COMMON enum is appropriate and consistent with the existing entries. This change introduces support for the Celestia network in the SDK.

To ensure this change is properly integrated, please run the following script to check for any existing usage of Celestia-related constants or functions that might need updating:

Please review the results to ensure that any existing Celestia-related code is updated to use this new constant, and that any switch statements or conditionals using COSMOS_CHAIN_ID_COMMON are updated if necessary.

✅ Verification successful

Verified: Celestia chain ID integration is consistent and complete

The CELESTIA_CHAIN_ID = "celestia" addition has been thoroughly integrated across the codebase. All relevant usages of COSMOS_CHAIN_ID_COMMON now appropriately include the new Celestia constant, ensuring consistent support for the Celestia network without introducing any conflicts.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for existing Celestia-related code that might need updating

# Search for "celestia" or "CELESTIA" in all TypeScript and JavaScript files
echo "Searching for existing Celestia-related code:"
rg -i "celestia" --type ts --type js

# Search for places where COSMOS_CHAIN_ID_COMMON is used
echo "Searching for usage of COSMOS_CHAIN_ID_COMMON:"
rg "COSMOS_CHAIN_ID_COMMON" --type ts --type js

Length of output: 4257

packages/oraidex-common/src/ibc-info.ts (1)

183-186: Confirm consistency in omitting "celestia" from ibcInfosOld

The ibcInfosOld constant now includes "celestia" in the omitted keys. Ensure that this change is consistently reflected throughout the codebase and that there are no lingering references to celestia in older configurations.

Use the following script to check for references to celestia in legacy code:

#!/bin/bash
# Description: Find references to "celestia" in the codebase, specifically in legacy configurations.

rg "celestia"
packages/oraidex-common/src/network.ts (3)

66-66: Import celestiaNetwork is correctly added

The import statement for celestiaNetwork ensures that the Celestia network configuration is included.


80-81: Update NetworkName type to include "Celestia"

Adding "Celestia" to the NetworkName type allows the application to recognize the new network.


547-547: Add celestiaNetwork to chainInfos array

Including celestiaNetwork in the chainInfos array integrates the Celestia network into the application's network configurations.

packages/universal-swap/src/helper.ts (5)

384-398: Verify correct mapping of addresses in 'receiverAddresses' object.

In the receiverAddresses object, ensure that the addresses are correctly mapped for each chain ID. For example, using injAddress for COSMOSHUB_CHAIN_ID may not be appropriate unless intended.

Please confirm that addresses.injAddress and addresses.obridgeAddress are correctly assigned for each chain. If they represent different address formats, consider providing clarifying comments or adjusting the code accordingly.


824-824: Review logic condition in 'handleSimulateSwap' method.

The condition if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) might not cover all intended cases. Consider reviewing the logic to ensure it correctly handles the scenarios for useIbcWasm and useAlphaIbcWasm.

Ensure that the condition correctly reflects the intended behavior when useIbcWasm is false or when useAlphaIbcWasm is true. You might want to revisit the logic to see if it should be:

-     if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) {
+     if (query?.routerOption?.useIbcWasm && !query?.routerOption?.useAlphaIbcWasm) {

401-408: Ensure correct parameters are passed to 'generateMemoSwap' function.

Verify that the parameters passed to generateMemoSwap are correct, especially the alphaRoutes object and the paths array. Incorrect parameters might lead to unexpected behavior.

Double-check the structure of alphaRoutes and ensure that paths contains the necessary data starting from the second element (as you are filtering with index > 0).

If adjustments are needed, consider updating the code accordingly.


340-342: Confirm proper usage of 'getSourceReceiver' method parameters.

In the call to getSourceReceiver, ensure that the third parameter swapOption?.isSourceReceiverTest aligns with the method's expected parameters.

Review the getSourceReceiver method signature to confirm that isSourceReceiverTest is correctly passed and utilized within the method.


86-87: Ensure all required imports are included.

The new imports added in lines 86-87 appear correct. Verify that generateMsgSwap and fromBech32, toBech32 are used appropriately in the code.

Confirm that these imported functions are utilized and necessary in the context of the updated code.

packages/universal-swap/src/handler.ts (6)

63-63: Import statement looks good.

The import of COSMOS_CHAIN_IDS is appropriate and necessary for the subsequent updates.


1154-1155: Destructured assignment is correct.

The inclusion of alphaSmartRoutes in the destructured assignment ensures it's available for use later in the method.


1176-1179: Initialization of 'injAddress' and retrieval of addresses.

Setting injAddress to undefined and using COSMOS_CHAIN_IDS for ORAICHAIN and ORAIBRIDGE aligns with the new constants.


1192-1202: Ensure proper parameters are passed to 'addOraiBridgeRoute'.

With the addition of injAddress and alphaSmartRoutes, verify that addOraiBridgeRoute handles these parameters correctly.

Consider reviewing the method signature of addOraiBridgeRoute to ensure it accepts the added parameters.


1205-1206: Validate the condition and method call in the 'if' statement.

The condition checks for alphaSmartRoutes?.routes?.length and swapOptions.isAlphaIbcWasm, and then calls this.transferAndSwap(swapRoute). Ensure swapRoute is defined and the method behaves as expected.

Confirm that swapRoute is available in this scope and that transferAndSwap can handle the provided arguments.


1185-1185: ⚠️ Potential issue

Fix the typo in method name 'caculateMinimumReceive'.

There's a typo in the method name caculateMinimumReceive(). It should be calculateMinimumReceive().

Apply this diff to correct the method call:

         if (swapOptions?.isIbcWasm) minimumReceive = await this.caculateMinimumReceive();
+        if (swapOptions?.isIbcWasm) minimumReceive = await this.calculateMinimumReceive();

Likely invalid or redundant comment.

Comment on lines +20 to +29
currencies: [
{
coinDecimals: 6,
coinDenom: "TIA",
coinGeckoId: "celestia",
coinMinimalDenom: "utia",
coinImageUrl: "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia/chain.png"
}
],
features: [],
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Potential Issue: Empty features Array in Celestia Network Configuration

The features array in packages/oraidex-common/src/celestia-network.ts is empty, whereas other network configurations include specific features.

  • Consistency Concern: Other networks have defined features such as "stargate" and "ibc-transfer". The absence of these or similar features for Celestia might lead to inconsistencies or missing functionalities.

Consider reviewing and updating the features array to include necessary features for the Celestia network.

🔗 Analysis chain

LGTM: Currency configuration looks good, query about features

The currency configuration for "TIA" is correct and comprehensive.

The features array is empty. Is this intentional, or are there any specific features that should be added for the Celestia network?

To verify if any features are typically used in other network configurations, we can run the following script:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for non-empty features arrays in other network configuration files
rg --type typescript 'features:\s*\[(?!\s*\])' packages/oraidex-common/src

Length of output: 317


Script:

#!/bin/bash
# Search for non-empty features arrays in other network configuration files without using look-around
rg --type typescript 'features:\s*\[[^]]' packages/oraidex-common/src

Length of output: 106


Script:

#!/bin/bash
# Search for non-empty features arrays in network configuration files
rg 'features:\s*\[[^]]' packages/oraidex-common/src

Length of output: 1228

@@ -41,7 +41,7 @@ const [kwt2oraichain, oraichain2kwt] = KWT_ORAICHAIN_CHANNELS.split(/\s+/);

// exclude evm chain

export const ibcInfos: IBCInfoMap = {
export const ibcInfos: Omit<IBCInfoMap, "celestia"> = {
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Issues Found: Remaining references to "celestia" exist in multiple files. Please ensure all these references are appropriately updated or removed to maintain code integrity.

🔗 Analysis chain

Ensure all references to "celestia" are properly handled

The celestia key has been omitted from IBCInfoMap in ibcInfos. Please verify that all references to celestia elsewhere in the codebase are appropriately updated or removed to prevent potential runtime errors.

Run the following script to locate any remaining references to celestia:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Search for any references to "celestia" in the codebase.

rg "celestia"

Length of output: 2893

@@ -586,7 +586,7 @@ describe("test helper functions", () => {
it("test-addOraiBridgeRoute-empty-swapRoute", async () => {
vi.spyOn(UniversalSwapHelper, "getRouteV2").mockResolvedValue("");
const result = await UniversalSwapHelper.addOraiBridgeRoute(
"receiver",
{ sourceReceiver: "receiver" },
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using proper type-safe arguments instead of casting to any.

In the test, the contract parameter is passed as { contractAddress: "any" } as any. Casting to any can undermine TypeScript's type checking. It's advisable to use appropriately typed mocks or stubs to maintain type safety in tests.

Comment on lines 602 to 611
UniversalSwapHelper.addOraiBridgeRoute(
{ sourceReceiver: "" },
undefined as any,
undefined as any,
"0",
undefined,
{
isSourceReceiverTest: false
}
)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid casting parameters to any; use typed mocks or default values.

The test passes undefined as any for several parameters. To enhance type safety and code quality, consider providing properly typed default values or mocks instead of casting to any.

Comment on lines +91 to +92
| "Neutaro-1" // neutaro network;
| "celestia"; // Celestia
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

Celestia chain ID does not follow standard conventions

The chain ID "celestia" should include a version suffix for consistency, such as "celestia-1".

  • File: packages/oraidex-common/src/network.ts
    • Lines: 91-92
🔗 Analysis chain

Verify the chain ID for Celestia follows standard conventions

Chain IDs in Cosmos networks typically include a version suffix (e.g., celestia-1). Please ensure that "celestia" is the correct chain ID or consider updating it to "celestia-1" for consistency.

To confirm existing chain IDs and their naming conventions, run the following script:

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: List all defined chain IDs to verify naming conventions.

# Extract chain IDs from the codebase
rg --type typescript 'chainId:\s*"([\w-]+)"' -o -r '$1' | sort | uniq

Length of output: 106


Script:

#!/bin/bash
# Description: List all defined chain IDs to verify naming conventions.

# Extract chain IDs from .ts and .tsx files
rg 'chainId:\s*"([\w-]+)"' --glob '*.ts' --glob '*.tsx' -o -r '$1' | sort | uniq

Length of output: 2492

@@ -548,7 +607,8 @@ export class UniversalSwapHelper {
offerAmount: offerAmount,
swapOptions: {
protocols: routerConfig.protocols,
dontAlowSwapAfter: routerConfig.dontAllowSwapAfter
dontAlowSwapAfter: routerConfig.dontAllowSwapAfter,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct typo in variable name 'dontAlowSwapAfter' to 'dontAllowSwapAfter'.

There's a typo in the variable name dontAlowSwapAfter. It should be dontAllowSwapAfter to maintain consistency and prevent potential errors.

Apply this diff to fix the typo:

           dontAlowSwapAfter: routerConfig.dontAllowSwapAfter,
+          dontAllowSwapAfter: routerConfig.dontAllowSwapAfter,

Committable suggestion was skipped due to low confidence.

Comment on lines +820 to +824
/**
* useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj not using wasm)
* useIbcWasm case: (evm -> cosmos)
*/
if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Re-evaluate condition in 'handleSimulateSwap' for IBC WASM usage.

The condition in lines 820-824 might not correctly determine when to reset fromInfo and toInfo. Consider checking if the logic aligns with the intended behavior for useIbcWasm and useAlphaIbcWasm options.

Review and, if necessary, adjust the condition to accurately reflect the scenarios where fromInfo and toInfo should be reassigned.

-        if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) {
+        if (query?.routerOption?.useIbcWasm && !query?.routerOption?.useAlphaIbcWasm) {
📝 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
/**
* useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj not using wasm)
* useIbcWasm case: (evm -> cosmos)
*/
if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) {
/**
* useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj not using wasm)
* useIbcWasm case: (evm -> cosmos)
*/
if (query?.routerOption?.useIbcWasm && !query?.routerOption?.useAlphaIbcWasm) {

Comment on lines 1187 to 1188
const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTVE);
if (hasInjectiveAddress) injAddress = this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Correct the constant name 'COSMOS_CHAIN_IDS.INJECTVE' to 'COSMOS_CHAIN_IDS.INJECTIVE'.

The constant COSMOS_CHAIN_IDS.INJECTVE appears to be misspelled. It should be COSMOS_CHAIN_IDS.INJECTIVE.

Apply this diff to fix the constant name:

           const routesFlatten = UniversalSwapHelper.flattenSmartRouters(alphaSmartRoutes.routes);
-          const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTVE);
-          if (hasInjectiveAddress) injAddress = this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE);
+          const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTIVE);
+          if (hasInjectiveAddress) injAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTIVE);
📝 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
const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTVE);
if (hasInjectiveAddress) injAddress = this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE);
const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTIVE);
if (hasInjectiveAddress) injAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTIVE);

Comment on lines 1097 to 1103
{ obridgeAddress, sourceReceiver: oraiAddress },
originalFromToken,
originalToToken,
minimumReceive,
destinationReceiver,
this.config.swapOptions,
alphaSmartRoutes,
obridgeAddress
alphaSmartRoutes
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Verify that 'destinationReceiver' is defined before use.

The variable destinationReceiver is passed to addOraiBridgeRoute, but it is not defined in this scope. This may lead to a ReferenceError.

Please ensure that destinationReceiver is properly defined or replace it with the correct variable. If you intended to use toAddress, consider applying the following change:

           { obridgeAddress, sourceReceiver: oraiAddress },
           originalFromToken,
           originalToToken,
           minimumReceive,
-          destinationReceiver,
+          toAddress,
           this.config.swapOptions,
           alphaSmartRoutes

Committable suggestion was skipped due to low confidence.

Comment on lines 1185 to 1190
if (swapOptions?.isAlphaIbcWasm) {
const routesFlatten = UniversalSwapHelper.flattenSmartRouters(alphaSmartRoutes.routes);
const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTVE);
if (hasInjectiveAddress) injAddress = this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE);
}

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Check for duplicate condition blocks and ensure consistent logic.

There are two consecutive if statements checking swapOptions?.isAlphaIbcWasm. This might be unintended duplication.

Consider consolidating the conditions or verifying if both are necessary:

-        if (swapOptions?.isIbcWasm) minimumReceive = await this.calculateMinimumReceive();
-
         if (swapOptions?.isAlphaIbcWasm) {
           const routesFlatten = UniversalSwapHelper.flattenSmartRouters(alphaSmartRoutes.routes);
-          const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTVE);
-          if (hasInjectiveAddress) injAddress = this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE);
+          const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTIVE);
+          if (hasInjectiveAddress) injAddress = await this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTIVE);
         }

Ensure that the calls to calculateMinimumReceive() and the logic within isAlphaIbcWasm are correctly structured.

Committable suggestion was skipped due to low confidence.

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: 4

🧹 Outside diff range and nitpick comments (1)
packages/universal-swap/src/msg/msgs.ts (1)

1-155: Summary of review for packages/universal-swap/src/msg/msgs.ts

The file implements the necessary functionality for message generation in universal swap operations across different blockchain environments. However, there are several areas for improvement:

  1. Error handling: Enhance error handling throughout the file, especially for potentially undefined values.
  2. Code duplication: Reduce duplication between buildMemoSwap and buildExecuteMsg by extracting common logic.
  3. Compatibility: Replace Array.prototype.at(-1) with a more widely supported method to access the last element of an array.
  4. Maintainability: Use constants for chain IDs instead of hard-coded strings.
  5. TODOs: Address the TODO comments, particularly regarding the calculation of minimum receive amounts.
  6. Input validation: Add more robust input validation, especially in the main exported functions.

Addressing these points will significantly improve the code's quality, maintainability, and robustness. Consider implementing the suggested changes in a follow-up commit.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 14883e4 and 3e7112c.

📒 Files selected for processing (1)
  • packages/universal-swap/src/msg/msgs.ts (1 hunks)
🔇 Additional comments (2)
packages/universal-swap/src/msg/msgs.ts (2)

1-10: Imports and type definitions look good.

The import statements are well-organized and include necessary utilities, types, and message classes for different chains. This sets up the file for its intended functionality.


11-25: ⚠️ Potential issue

Improve error handling and return value in getDestPrefixForBridgeToEvmOnOrai

  1. Rephrase the error message to avoid using contractions and improve clarity.
  2. Consider throwing an error for non-EVM chains instead of returning an empty string to prevent silent failures.
  3. Add explicit return type annotation for better type safety.

Apply this diff to address these issues:

-const getDestPrefixForBridgeToEvmOnOrai = (chainId: string): string => {
+const getDestPrefixForBridgeToEvmOnOrai = (chainId: string): string => {
   const prefixMap: { [key: string]: string } = {
     "0x01": ORAI_BRIDGE_EVM_DENOM_PREFIX,
     "0x38": ORAI_BRIDGE_EVM_DENOM_PREFIX,
     "0x2b6653dc": ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX
   };

   const prefix = prefixMap[chainId];
   if (prefix) {
     return prefix;
   } else if (chainId.startsWith("0x")) {
-    throw generateError(`Don't support bridge from Oraichain to ${chainId}`);
+    throw generateError(`Bridging from Oraichain to ${chainId} is not supported`);
   }
-  return "";
+  throw generateError(`Unexpected chain ID: ${chainId}`);
 };

Likely invalid or redundant comment.

Comment on lines 27 to 72
//FIXME: calc minimum receive
const buildMemoSwap = (
path: Path,
receiver: string,
memo: string,
addresses: { [chainId: string]: string },
slippage: number = 0.01,
previousChain?: string
): MiddlewareResponse => {
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["oraibridge-subnet-2"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
oraichainMsg.setMinimumReceiveForSwap(slippage);
// we have 2 cases:
// - Previous chain use IBC bridge to Oraichain
// - Previous chain use IBC Wasm bridge to Oraichain (EVM, noble)
let msgInfo =
previousChain && (previousChain == "noble-1" || previousChain.startsWith("0x"))
? oraichainMsg.genMemoForIbcWasm()
: oraichainMsg.genMemoAsMiddleware();
return msgInfo;
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Address TODO and improve buildMemoSwap function

  1. The TODO comment about calculating the minimum receive amount should be addressed. Implement this calculation to ensure proper slippage handling.
  2. Consider using constants for chain IDs to improve maintainability.
  3. Add error handling for potentially undefined addresses[currentChain].

Here's an example of how you might start addressing these issues:

const CHAIN_ID_ORAICHAIN = "Oraichain";
const CHAIN_ID_OSMOSIS = "osmosis-1";

const buildMemoSwap = (
  path: Path,
  receiver: string,
  memo: string,
  addresses: { [chainId: string]: string },
  slippage: number = 0.01,
  previousChain?: string
): MiddlewareResponse => {
  let currentChain = path.chainId;
  let currentAddress = addresses[currentChain];
  if (!currentAddress) {
    throw generateError(`Address not found for chain ${currentChain}`);
  }

  // TODO: Implement minimum receive amount calculation
  // const minimumReceive = calculateMinimumReceive(path, slippage);

  switch (currentChain) {
    case CHAIN_ID_ORAICHAIN:
      // ... (rest of the Oraichain case)
    case CHAIN_ID_OSMOSIS:
      // ... (rest of the Osmosis case)
    default:
      // ... (rest of the default case)
  }
};

Consider implementing the calculateMinimumReceive function to address the TODO comment.

Comment on lines 74 to 109
//FIXME: calc minimum receive
const buildExecuteMsg = (
path: Path,
receiver: string,
memo: string,
addresses: { [chainId: string]: string },
slippage: number = 0.01
): EncodeObject => {
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["OraiBridge"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
oraichainMsg.setMinimumReceiveForSwap(slippage);
return oraichainMsg.genExecuteMsg();
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
return cosmosMsg.genExecuteMsg();
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
return cosmosMsg.genExecuteMsg();
}
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor buildExecuteMsg to reduce duplication and improve error handling

  1. Address the TODO comment about calculating the minimum receive amount, similar to buildMemoSwap.
  2. There's significant code duplication between buildMemoSwap and buildExecuteMsg. Consider extracting common logic into a shared helper function.
  3. Add error handling for potentially undefined addresses[currentChain].

Apply these changes to address the issues:

  1. Implement the minimum receive calculation or add a more specific TODO with acceptance criteria.
  2. Extract common logic into a shared helper function:
    function handleChainSpecificLogic(
      chainId: string,
      path: Path,
      receiver: string,
      currentAddress: string,
      memo: string,
      slippage: number,
      addresses: { [chainId: string]: string },
      isExecuteMsg: boolean
    ) {
      // Common logic here
    }
  3. Add error handling for undefined addresses:
    if (!currentAddress) {
      throw generateError(`Address not found for chain ${currentChain}`);
    }

Also, consider using the chain ID constants suggested in the previous comment to improve consistency and maintainability.

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: 22

🧹 Outside diff range and nitpick comments (12)
packages/universal-swap/src/msg/chains/cosmos.ts (2)

21-31: LGTM: setMinimumReceiveForSwap method is well-implemented.

The method correctly handles slippage calculation and sets the minimum receive amount. The slippage check is appropriate, and the calculation looks correct.

Consider adding a comment explaining why the decimal portion is removed, as this might lead to slight precision loss. If this is intentional (e.g., to ensure whole number amounts), a brief explanation would be helpful for future maintainers.


69-94: LGTM: getPostAction method handles cases correctly.

The method appropriately handles both transfer to receiver and IBC transfer cases.

Regarding the past comment about handling additional source ports: If "transfer" is the only supported source port, consider adding a comment to clarify this constraint for future maintainers. For example:

// Note: Currently, only "transfer" is supported as a source port.
// If additional ports are to be supported in the future, extend this logic accordingly.
if (bridgeInfo.sourcePort == "transfer") {
  // ... existing code ...
}

This clarifies the current limitation and provides guidance for potential future extensions.

packages/universal-swap/tests/msg/comos-msg.spec.ts (5)

7-33: LGTM: Test data setup is comprehensive.

The validPath object provides a good set of test data for bridge actions. The use of constants for receiver and currentAddress promotes consistency across tests.

Consider using a more descriptive name for the validPath constant, such as validBridgeActionPath, to better reflect its content and purpose.


34-102: LGTM: Comprehensive test cases for getPostAction.

The use of it.each is an efficient way to test multiple scenarios for the getPostAction method. The test cases cover important scenarios, including error handling for undefined bridgeInfo and valid bridge actions.

Consider adding a test case for an invalid bridgeInfo object to ensure robust error handling. This could help catch potential issues with malformed input data.


104-153: LGTM: Comprehensive test for valid path scenario.

This test case thoroughly checks the behavior of CosmosMsg methods (getBridgeInfo, genMemoAsMiddleware, and genExecuteMsg) with a valid path. The expected outputs are well-defined and correctly match the input data.

Consider breaking this large test into smaller, more focused tests for each method. This would improve test readability and make it easier to identify which specific functionality fails if a test doesn't pass. For example:

it("should correctly get bridge info for a valid path", () => {
  // Test getBridgeInfo
});

it("should generate correct memo as middleware for a valid path", () => {
  // Test genMemoAsMiddleware
});

it("should generate correct execute message for a valid path", () => {
  // Test genExecuteMsg
});

155-220: LGTM: Good coverage of invalid path scenarios.

This test case effectively checks the error handling of CosmosMsg methods with invalid paths, covering both non-bridge action and non-IBC transfer scenarios. The expected error messages are well-defined and appropriate.

Consider using it.each for these invalid path scenarios as well, similar to the first test case. This would make the tests more concise and easier to extend with additional invalid scenarios. For example:

it.each([
  ['non-bridge action', invalidPathNotBridgeAction, 'Only support bridge on cosmoshub-4'],
  ['non-IBC transfer', invalidPathNotIbcTransfer, 'Only support IBC bridge on cosmoshub-4']
])('should throw error for %s', (scenario, invalidPath, expectedErrorMessage) => {
  const cosmos = new CosmosMsg(invalidPath, "1", receiver, currentAddress, nextMemo);
  expect(() => cosmos.genMemoAsMiddleware()).toThrow(expectedErrorMessage);
  expect(() => cosmos.genExecuteMsg()).toThrow(expectedErrorMessage);
});

This approach would make it easier to add more invalid scenarios in the future without duplicating code.


1-220: Overall, excellent test coverage with minor improvement opportunities.

This test file provides comprehensive coverage for the CosmosMsg class, effectively testing both valid and invalid scenarios. The use of Vitest and well-structured test cases contributes to the overall quality of the tests.

To further enhance the test file:

  1. Consider using more descriptive names for test data constants.
  2. Break down larger tests into smaller, more focused test cases.
  3. Utilize it.each more consistently across different test scenarios to improve code reusability and make it easier to add new test cases.

These minor improvements will make the tests even more maintainable and easier to extend in the future.

packages/universal-swap/tests/msg/oraichain-msg.spec.ts (1)

48-116: Good test coverage, but consider improving error case.

The it.each test cases provide good coverage for different scenarios of the getPostAction method. However, the error case could be more explicit.

Consider adding a specific error case to test the error handling more thoroughly. For example:

[
  {
    // Invalid bridgeInfo
    amount: "invalid",
    sourceChannel: "channel-301",
    sourcePort: "transfer",
    // ... other fields
  },
  null,
  "Invalid amount in bridgeInfo"
]

This will ensure that the error handling is tested explicitly.

packages/universal-swap/src/msg/chains/oraichain.ts (2)

65-65: Address TODO comment: Query the converter contract

There's a TODO comment suggesting that the converter contract should be queried to determine the appropriate conversion method. This implementation detail should be addressed to ensure the correct conversion logic is used.

Would you like assistance in implementing the converter contract query or creating a GitHub issue to track this task?


1-521: Overall review: Comprehensive implementation with room for improvements

The OraichainMsg class provides a comprehensive implementation for handling token swaps and bridging operations within the Oraichain ecosystem. The code demonstrates a good understanding of the complex scenarios involved.

However, there are several areas where the code quality and maintainability could be improved:

  1. Fix recurring typos in method names and error messages.
  2. Address operator precedence issues in ternary operations.
  3. Consider refactoring larger methods (e.g., genExecuteMsg) into smaller, more focused helper methods.
  4. Implement more precise decimal handling in the setMinimumReceiveForSwap method.
  5. Address TODO comments and FIXME notes throughout the code.

By addressing these points, the code will become more robust, maintainable, and less prone to subtle bugs. Consider creating separate tasks or issues to track and implement these improvements systematically.

packages/universal-swap/tests/msg/osmosis-msg.spec.ts (2)

65-65: Review the Use of Unary + Operator with calculateTimeoutTimestamp

The unary + operator is used to coerce the result of calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT) to a number in multiple places. If calculateTimeoutTimestamp already returns a number, the unary + is unnecessary. If it returns a string, consider updating the function to return a number to ensure type consistency throughout your code.

Also applies to: 129-129, 222-222, 238-238, 325-325


265-265: Avoid Mutating Shared Variables in Tests

The receiver variable is reassigned at line 265:

receiver = currentAddress;

Modifying a shared variable can lead to test interdependencies and unexpected behaviors. It's better to declare receiver as a local variable within the test to maintain isolation and prevent side effects.

Apply this diff to declare receiver locally:

- receiver = currentAddress;
+ const receiver = currentAddress;
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 3e7112c and bfba1b8.

📒 Files selected for processing (8)
  • packages/universal-swap/src/msg/chains/cosmos.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/osmosis.ts (1 hunks)
  • packages/universal-swap/src/msg/msgs.ts (1 hunks)
  • packages/universal-swap/tests/msg/comos-msg.spec.ts (1 hunks)
  • packages/universal-swap/tests/msg/oraichain-msg.spec.ts (1 hunks)
  • packages/universal-swap/tests/msg/osmosis-msg.spec.ts (1 hunks)
  • packages/universal-swap/tests/msg/test-data.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/universal-swap/src/msg/chains/osmosis.ts
🧰 Additional context used
🔇 Additional comments (12)
packages/universal-swap/src/msg/chains/cosmos.ts (3)

1-19: LGTM: Imports and class declaration look good.

The imports cover all necessary dependencies, and the CosmosMsg class is correctly declared as an extension of ChainMsg. The constructor properly initializes the class with the required parameters.


98-114: LGTM: genMemoAsMiddleware method is well-implemented.

The method correctly generates a middleware response with a properly structured memo for forwarding the message.

Regarding the past comment about handling potential undefined bridgeInfo: This is no longer a concern if getBridgeInfo() now throws an error for undefined cases, as suggested in a previous review. The current implementation assumes getBridgeInfo() always returns a valid BridgeMsgInfo object or throws an error, which is the correct approach.


1-143: Overall, the CosmosMsg class is well-implemented and robust.

The class provides comprehensive functionality for handling bridge messaging and actions within the Cosmos ecosystem. The methods are well-structured and cover the necessary operations for IBC transfers and related actions.

A few minor improvements have been suggested in the individual method reviews, including:

  1. Optimizing the loop in getBridgeInfo.
  2. Adding clarifying comments in getPostAction.
  3. Updating an error message in genExecuteMsg.

These suggestions are minor and don't impact the core functionality. Implementing them would further enhance the code's clarity and maintainability.

Great job on the implementation! The code is of high quality and demonstrates a good understanding of the Cosmos ecosystem and IBC transfers.

packages/universal-swap/tests/msg/comos-msg.spec.ts (1)

1-6: LGTM: Imports and test setup are appropriate.

The imports and test setup are well-structured. The necessary testing utilities from Vitest are imported, along with the relevant classes and utility functions needed for the tests.

packages/universal-swap/tests/msg/oraichain-msg.spec.ts (2)

1-46: LGTM: Imports and test setup are well-structured.

The imports cover all necessary testing utilities and relevant classes. The validPath object provides a comprehensive test scenario with realistic data.


1-375: Good test structure, but consider expanding coverage.

The test file is well-structured and provides good coverage for the getPostAction method of the OraichainMsg class. The use of it.each allows for concise testing of multiple scenarios.

Are there tests for other methods of the OraichainMsg class? If not, consider adding them to ensure comprehensive coverage.

To verify the existence of other test files for OraichainMsg, you can run:

packages/universal-swap/src/msg/chains/oraichain.ts (5)

220-220: Verify that bridgeInfo is defined before using in getProtoForPostAction

In the method genMemoForIbcWasm, bridgeInfo is passed to getProtoForPostAction without ensuring it's defined. If bridgeInfo is undefined, this could lead to runtime errors.

Add a check to ensure bridgeInfo is not undefined before using it:

let memo: Memo = {
  userSwap,
  minimumReceive: this.minimumReceive,
  timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
- postSwapAction: this.getProtoForPostAction(bridgeInfo),
+ postSwapAction: this.getProtoForPostAction(bridgeInfo || {}),
  recoveryAddr: this.currentChainAddress
};

Alternatively, handle the undefined case within getProtoForPostAction.


282-350: LGTM: Well-structured middleware memo generation

The genMemoAsMiddleware method is well-implemented, handling both IBC transfer and swap + action cases correctly. The logic is clear and follows the expected flow for generating memos as middleware on Oraichain.


141-141: ⚠️ Potential issue

Fix typo in error message

There's a typo in the error message: "bride" should be "bridge".

Apply this diff to fix the typo:

- throw generateError("Only support swap + convert + bride on Oraichain");
+ throw generateError("Only support swap + convert + bridge on Oraichain");

Likely invalid or redundant comment.


63-63: ⚠️ Potential issue

Fix typo in method name: pasreConverterMsgToPoolIdparseConverterMsgToPoolId

The method name pasreConverterMsgToPoolId appears to be misspelled. Renaming it to parseConverterMsgToPoolId enhances code readability and maintains consistency.

Apply this diff to correct the method name:

- pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
+ parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {

Likely invalid or redundant comment.


389-390: ⚠️ Potential issue

Fix typo in error message (similar to previous methods)

The error message for missing prefix or OBridge address contains a typo: "os" should be "or".

Apply this diff to fix the issue:

- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");

Likely invalid or redundant comment.

packages/universal-swap/tests/msg/osmosis-msg.spec.ts (1)

1-375: LGTM!

Overall, the test file is well-structured and covers various scenarios for the OsmosisMsg class. The usage of parameterized tests enhances coverage, and the assertions validate the expected behaviors effectively.

Comment on lines +1 to +9
export const cosmosTestData = [
{
type: "Valid Path",
path: "",
nextMemo: "{}",
expectedMemo: ""
// expected
}
];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance test data for comprehensive coverage

The current test data seems incomplete and potentially inconsistent. Consider the following improvements:

  1. Add more test cases to cover different scenarios (e.g., invalid paths, various memo contents).
  2. Ensure consistency between the type and path fields. For a "Valid Path", the path should not be empty.
  3. Align the expectedMemo with the nextMemo or clarify why they differ.
  4. Clarify the purpose of the "// expected" comment or remove it if unnecessary.

Here's a suggested expansion of the test data:

export const cosmosTestData = [
  {
    type: "Valid Path",
    path: "cosmos1abc...def/osmosis1ghi...jkl",
    nextMemo: '{"key": "value"}',
    expectedMemo: '{"key": "value"}'
  },
  {
    type: "Empty Path",
    path: "",
    nextMemo: "{}",
    expectedMemo: ""
  },
  {
    type: "Invalid Path",
    path: "invalid_path",
    nextMemo: "{}",
    expectedMemo: null
  }
  // Add more test cases as needed
];

This expansion provides better coverage and clarifies the relationship between different fields.

Would you like me to generate more comprehensive test cases or open a GitHub issue to track this task?

Comment on lines +119 to +142

genExecuteMsg(): EncodeObject {
let bridgeInfo = this.getBridgeInfo();
if (bridgeInfo.sourcePort != "transfer") {
throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer");
}
// ibc transfer

return {
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: bridgeInfo.sourcePort,
sourceChannel: bridgeInfo.sourceChannel,
receiver: this.receiver,
token: {
amount: this.path.tokenInAmount,
denom: this.path.tokenIn
},
sender: this.currentChainAddress,
memo: this.memo,
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update error message in genExecuteMsg method.

The method correctly generates an execution message for the Osmosis network, but there's a minor issue with the error message.

Update the error message to reflect the correct chain:

if (bridgeInfo.sourcePort != "transfer") {
-  throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer");
+  throw generateError("Error generating executeMsg on Osmosis: Only support IBC transfer");
}

This change aligns the error message with the actual chain being used (Osmosis), improving clarity and accuracy of error reporting.

The rest of the method implementation looks good, correctly constructing the EncodeObject for IBC transfer with all necessary fields.

📝 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
genExecuteMsg(): EncodeObject {
let bridgeInfo = this.getBridgeInfo();
if (bridgeInfo.sourcePort != "transfer") {
throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer");
}
// ibc transfer
return {
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: bridgeInfo.sourcePort,
sourceChannel: bridgeInfo.sourceChannel,
receiver: this.receiver,
token: {
amount: this.path.tokenInAmount,
denom: this.path.tokenIn
},
sender: this.currentChainAddress,
memo: this.memo,
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
};
}
genExecuteMsg(): EncodeObject {
let bridgeInfo = this.getBridgeInfo();
if (bridgeInfo.sourcePort != "transfer") {
throw generateError("Error generating executeMsg on Osmosis: Only support IBC transfer");
}
// ibc transfer
return {
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: bridgeInfo.sourcePort,
sourceChannel: bridgeInfo.sourceChannel,
receiver: this.receiver,
token: {
amount: this.path.tokenInAmount,
denom: this.path.tokenIn
},
sender: this.currentChainAddress,
memo: this.memo,
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
};
}

Comment on lines +36 to +67
getBridgeInfo(): BridgeMsgInfo {
let bridgeInfo: BridgeMsgInfo;

for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
amount: action.tokenInAmount,
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}

// check bridge type must be ibc bridge
if (bridgeInfo.sourcePort != "transfer") {
throw generateError(`Only support IBC bridge on ${this.path.chainId}`);
}

return bridgeInfo;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize the loop in getBridgeInfo method.

The method correctly constructs the BridgeMsgInfo object, but there's an opportunity for optimization.

Consider breaking the loop after finding the bridge action to avoid unnecessary iterations:

for (let action of this.path.actions) {
  switch (action.type) {
    case ActionType.Bridge: {
      bridgeInfo = {
        // ... initialization ...
      };
+     return bridgeInfo; // Exit the loop after finding the bridge action
    }
    default:
      throw generateError(`Only support bridge on ${this.path.chainId}`);
  }
}

- return bridgeInfo;

This change ensures that the method returns immediately after finding the necessary bridgeInfo, avoiding unnecessary iterations over remaining actions.

📝 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
getBridgeInfo(): BridgeMsgInfo {
let bridgeInfo: BridgeMsgInfo;
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
amount: action.tokenInAmount,
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
break;
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}
// check bridge type must be ibc bridge
if (bridgeInfo.sourcePort != "transfer") {
throw generateError(`Only support IBC bridge on ${this.path.chainId}`);
}
return bridgeInfo;
}
getBridgeInfo(): BridgeMsgInfo {
let bridgeInfo: BridgeMsgInfo;
for (let action of this.path.actions) {
switch (action.type) {
case ActionType.Bridge: {
bridgeInfo = {
amount: action.tokenInAmount,
sourceChannel: action.bridgeInfo.channel,
sourcePort: action.bridgeInfo.port,
memo: this.memo,
receiver: this.receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: action.tokenIn,
toToken: action.tokenOut,
fromChain: this.path.chainId as NetworkChainId,
toChain: this.path.tokenOutChainId as NetworkChainId
};
return bridgeInfo; // Exit the loop after finding the bridge action
}
default:
throw generateError(`Only support bridge on ${this.path.chainId}`);
}
}
// check bridge type must be ibc bridge
if (bridgeInfo.sourcePort != "transfer") {
throw generateError(`Only support IBC bridge on ${this.path.chainId}`);
}
throw generateError(`No bridge action found in ${this.path.chainId}`);
}

return "";
};

//FIXME: calc minimum receive
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Address FIXME comment for minimum receive calculation

The FIXME comment indicates that the minimum receive calculation needs to be implemented. This is crucial for ensuring proper slippage handling in the swap operation.

Please implement the minimum receive calculation logic. Consider factors such as the input amount, expected output amount, and the provided slippage parameter.

Comment on lines +36 to +37
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling for potentially undefined addresses

The currentAddress is accessed from the addresses object using a dynamic key. To prevent potential undefined values and runtime errors, ensure that the key exists in the addresses object.

Consider adding a check:

let currentAddress = addresses[currentChain];
if (!currentAddress) {
  throw generateError(`Address not found for chain ${currentChain}`);
}

This will provide a clear error message if the address for the current chain is missing.

Comment on lines 260 to 261
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix os Obridge address for bridge to EVM");
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix operator precedence and typo in error message (similar to getProtoForPostAction)

The getPostAction method has similar issues to those found in getProtoForPostAction:

  1. There's an operator precedence issue in the IBC wasm transfer case.
  2. The error message for missing prefix or OBridge address contains a typo.

Apply these diffs to fix the issues:

  1. Fix operator precedence:
- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
  1. Fix typo in error message:
- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");

Also applies to: 269-269

Comment on lines 356 to 520
}
})
),
funds: []
})
};
}
// native token
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: ibcWasmContractAddress,
msg: toUtf8(
JSON.stringify({
transfer_to_remote: {
msg
}
})
),
funds: [
{
denom: this.path.tokenIn,
amount: this.path.tokenInAmount
}
]
})
};
}

throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
}

let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out;
let min_asset = isCw20Token(tokenOutOfSwap)
? {
cw20: {
amount: this.minimumReceive,
address: tokenOutOfSwap
}
}
: {
native: {
amount: this.minimumReceive,
denom: tokenOutOfSwap
}
};

// swap and action
let msg: ExecuteMsg = {
swap_and_action: {
affiliates: [],
min_asset,
post_swap_action: this.getPostAction(bridgeInfo),
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
user_swap: {
swap_exact_asset_in: { swap_venue_name: this.SWAP_VENUE_NAME, operations: swapOps }
}
}
};

// if asset info is native => send native way, else send cw20 way
if (isCw20Token(this.path.tokenIn)) {
return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: this.path.tokenIn,
msg: toUtf8(
JSON.stringify({
send: {
send: {
contract: this.ENTRY_POINT_CONTRACT,
amount: this.path.tokenInAmount,
msg: toBinary(msg)
}
}
})
),
funds: []
})
};
}
// native token

return {
typeUrl: "/cosmwasm.wasm.v1.MsgExecuteContract",
value: MsgExecuteContract.fromPartial({
sender: this.currentChainAddress,
contract: this.ENTRY_POINT_CONTRACT,
msg: toUtf8(JSON.stringify(msg)),
funds: [
{
denom: this.path.tokenIn,
amount: this.path.tokenInAmount
}
]
})
};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refactoring genExecuteMsg for improved maintainability

The genExecuteMsg method is quite long and handles multiple complex cases. While the implementation seems correct, its length and complexity might make it harder to maintain and test.

Consider refactoring this method into smaller, more focused helper methods for each case (IBC transfer, IBC wasm transfer, and swap and action). This would improve readability and make the code easier to maintain and test.

Example refactoring:

private genIbcTransferMsg(bridgeInfo: BridgeMsgInfo): EncodeObject {
  // Implementation for IBC transfer
}

private genIbcWasmTransferMsg(bridgeInfo: BridgeMsgInfo): EncodeObject {
  // Implementation for IBC wasm transfer
}

private genSwapAndActionMsg(swapOps: SwapOperation[], bridgeInfo?: BridgeMsgInfo): EncodeObject {
  // Implementation for swap and action
}

genExecuteMsg(): EncodeObject {
  let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo();

  if (swapOps.length === 0) {
    if (bridgeInfo.sourcePort === "transfer") {
      return this.genIbcTransferMsg(bridgeInfo);
    }
    if (bridgeInfo.sourcePort.startsWith("wasm")) {
      return this.genIbcWasmTransferMsg(bridgeInfo);
    }
    throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
  }

  return this.genSwapAndActionMsg(swapOps, bridgeInfo);
}

This refactoring would make the genExecuteMsg method more concise and easier to understand at a glance.

Comment on lines +188 to +261
it("Valid path with bridge only", () => {
const nextMemo = "{}";
let validPathBridgeOnly = {
chainId: "osmosis-1",
tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
tokenInAmount: "999000",
tokenOut: "uatom",
tokenOutAmount: "999000",
tokenOutChainId: "cosmoshub-4",
actions: [
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
tokenInAmount: "999000",
tokenOut: "uatom",
tokenOutAmount: "999000",
tokenOutChainId: "cosmoshub-4",
bridgeInfo: {
port: "transfer",
channel: "channel-0"
}
}
]
};
let osmosis = new OsmosisMsg(validPathBridgeOnly, "1", receiver, currentAddress, nextMemo);

let [swapOps, bridgeInfo] = osmosis.getSwapAndBridgeInfo();
expect(bridgeInfo).toEqual({
amount: "999000",
sourceChannel: "channel-0",
sourcePort: "transfer",
memo: nextMemo,
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
toToken: "uatom",
fromChain: "osmosis-1",
toChain: "cosmoshub-4"
});
expect(swapOps).toEqual([]);

let memoAsMiddleware = osmosis.genMemoAsMiddleware();
expect(memoAsMiddleware).toEqual({
receiver: currentAddress,
memo: JSON.stringify({
forward: {
receiver: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
port: "transfer",
channel: "channel-0",
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
retries: 2,
next: "{}"
}
})
});

let executeMsg = osmosis.genExecuteMsg();
expect(executeMsg).toEqual({
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: bridgeInfo.sourcePort,
sourceChannel: bridgeInfo.sourceChannel,
receiver: receiver,
token: {
amount: validPathBridgeOnly.tokenInAmount,
denom: validPathBridgeOnly.tokenIn
},
sender: currentAddress,
memo: nextMemo,
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify the Test for 'Valid path with bridge only'

In the test case for "Valid path with bridge only," the expect statements and variable assignments are quite extensive. Consider simplifying the test by extracting common setup code into helper functions or variables. This will improve readability and maintainability.

Comment on lines +369 to +373
try {
osmosis.genMemoAsMiddleware();
} catch (err) {
expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotIbcTransfer.chainId}`));
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ensure Errors Are Properly Caught in Negative Tests

In the "Invalid path not ibc transfer" test, if osmosis.genMemoAsMiddleware() does not throw an error as expected, the test will incorrectly pass. To prevent false positives, add a fail statement after the function call to ensure the test fails if no error is thrown.

Apply this diff to improve the test:

 try {
   osmosis.genMemoAsMiddleware();
+  fail("Expected an error to be thrown, but none was thrown.");
 } catch (err) {
   expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotIbcTransfer.chainId}`));
 }
📝 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
try {
osmosis.genMemoAsMiddleware();
} catch (err) {
expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotIbcTransfer.chainId}`));
}
try {
osmosis.genMemoAsMiddleware();
fail("Expected an error to be thrown, but none was thrown.");
} catch (err) {
expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotIbcTransfer.chainId}`));
}

Comment on lines +48 to +116
it.each<[BridgeMsgInfo, Action, string]>([
[
undefined,
{
transfer: {
to_address: receiver
}
},
""
],
[
{
amount: "217432",
sourceChannel: "channel-301",
sourcePort: "transfer",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "osmo",
toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78",
fromChain: "osmosis-1",
toChain: "cosmoshub-4"
},
{
ibc_transfer: {
ibc_info: {
source_channel: "channel-301",
receiver: receiver,
memo: "{}",
recover_address: currentAddress
}
}
},
""
],
[
{
amount: "217432",
sourceChannel: "channel-301",
sourcePort: "wasm.orai123",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "osmos",
toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78",
fromChain: "osmosis-1",
toChain: "cosmoshub-4"
},
{
ibc_transfer: {
ibc_info: {
source_channel: "channel-301",
receiver: receiver,
memo: "{}",
recover_address: currentAddress
}
}
},
""
]
])("osmosis test get post action", (bridgeInfo, expectedAction, expectedError) => {
let osmosis = new OsmosisMsg(validPath, "1", receiver, currentAddress);
try {
let postAction = osmosis.getPostAction(bridgeInfo);
expect(postAction).toEqual(expectedAction);
} catch (err) {
expect(err).toEqual(generateError(`Missing postAction for universalSwap on ${validPath.chainId}`));
}
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance Error Handling in Parameterized Tests

In the it.each block, the catch block assumes any error will be Missing postAction for universalSwap on osmosis-1, which might not always be the case. Additionally, since expectedError is provided (though currently empty), it's better to use it to assert specific error messages. This will make your tests more robust and accurate.

Apply this diff to improve error assertions:

 try {
   let postAction = osmosis.getPostAction(bridgeInfo);
   expect(postAction).toEqual(expectedAction);
 } catch (err) {
-  expect(err).toEqual(generateError(`Missing postAction for universalSwap on ${validPath.chainId}`));
+  expect(err).toEqual(expectedError ? generateError(expectedError) : err);
 }

Also, update the test cases with appropriate expectedError messages if an error is expected.

📝 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
it.each<[BridgeMsgInfo, Action, string]>([
[
undefined,
{
transfer: {
to_address: receiver
}
},
""
],
[
{
amount: "217432",
sourceChannel: "channel-301",
sourcePort: "transfer",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "osmo",
toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78",
fromChain: "osmosis-1",
toChain: "cosmoshub-4"
},
{
ibc_transfer: {
ibc_info: {
source_channel: "channel-301",
receiver: receiver,
memo: "{}",
recover_address: currentAddress
}
}
},
""
],
[
{
amount: "217432",
sourceChannel: "channel-301",
sourcePort: "wasm.orai123",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "osmos",
toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78",
fromChain: "osmosis-1",
toChain: "cosmoshub-4"
},
{
ibc_transfer: {
ibc_info: {
source_channel: "channel-301",
receiver: receiver,
memo: "{}",
recover_address: currentAddress
}
}
},
""
]
])("osmosis test get post action", (bridgeInfo, expectedAction, expectedError) => {
let osmosis = new OsmosisMsg(validPath, "1", receiver, currentAddress);
try {
let postAction = osmosis.getPostAction(bridgeInfo);
expect(postAction).toEqual(expectedAction);
} catch (err) {
expect(err).toEqual(generateError(`Missing postAction for universalSwap on ${validPath.chainId}`));
}
});
it.each<[BridgeMsgInfo, Action, string]>([
[
undefined,
{
transfer: {
to_address: receiver
}
},
""
],
[
{
amount: "217432",
sourceChannel: "channel-301",
sourcePort: "transfer",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "osmo",
toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78",
fromChain: "osmosis-1",
toChain: "cosmoshub-4"
},
{
ibc_transfer: {
ibc_info: {
source_channel: "channel-301",
receiver: receiver,
memo: "{}",
recover_address: currentAddress
}
}
},
""
],
[
{
amount: "217432",
sourceChannel: "channel-301",
sourcePort: "wasm.orai123",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "osmos",
toToken: "ibc/A2E2EEC9057A4A1C2C0A6A4C78B0239118DF5F278830F50B4A6BDD7A66506B78",
fromChain: "osmosis-1",
toChain: "cosmoshub-4"
},
{
ibc_transfer: {
ibc_info: {
source_channel: "channel-301",
receiver: receiver,
memo: "{}",
recover_address: currentAddress
}
}
},
""
]
])("osmosis test get post action", (bridgeInfo, expectedAction, expectedError) => {
let osmosis = new OsmosisMsg(validPath, "1", receiver, currentAddress);
try {
let postAction = osmosis.getPostAction(bridgeInfo);
expect(postAction).toEqual(expectedAction);
} catch (err) {
expect(err).toEqual(expectedError ? generateError(expectedError) : err);
}
});

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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between bfba1b8 and 267ffcf.

📒 Files selected for processing (1)
  • packages/universal-swap/tests/msg/oraichain-msg.spec.ts (1 hunks)
🧰 Additional context used
🔇 Additional comments (3)
packages/universal-swap/tests/msg/oraichain-msg.spec.ts (3)

1-47: LGTM: Imports and setup are well-structured.

The imports cover all necessary testing utilities and the required classes and functions for the tests. The validPath object is well-defined with comprehensive test data.


220-407: Address commented-out test cases as per previous review.

As mentioned in a previous review comment, there are several commented-out test cases at the end of this file. These appear to be for different scenarios involving the OsmosisMsg class.

To reiterate the previous suggestion:

  1. If these tests are no longer relevant, remove them entirely.
  2. If they are still needed, move them to a separate test file for OsmosisMsg.
  3. If they are work in progress, add a TODO comment explaining the plan for these tests.

Please take action on these commented-out tests to improve the maintainability of the test suite.


1-407: Overall, well-structured test suite with room for improvements.

This test file provides comprehensive coverage for the OraichainMsg class, which is commendable. The tests are generally well-written and cover important functionality. However, there are a few areas where improvements can be made:

  1. Consider enhancing the error handling test for better clarity.
  2. The large test case for "Valid path with swap + ibc bridge" could be split into smaller, more focused tests for better maintainability.
  3. The commented-out test cases at the end of the file should be addressed as per the previous review comment.

Implementing these suggestions will result in a more robust and maintainable test suite.

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: 6

🧹 Outside diff range and nitpick comments (1)
packages/universal-swap/tests/msg/oraichain-msg.spec.ts (1)

602-635: Remove or implement commented-out test case.

There's a large block of commented-out code for a test case "Invalid path not ibc transfer". This can lead to confusion and maintenance issues. Consider one of the following actions:

  1. If the test is no longer relevant, remove it entirely.
  2. If it's still needed, implement it in a separate file for OsmosisMsg tests.
  3. If it's a work in progress, add a TODO comment explaining the plan:
// TODO: Implement test for invalid IBC transfer path in OsmosisMsg
// This test will be moved to a separate file for OsmosisMsg tests

Keeping the codebase clean of unused code improves maintainability and readability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 267ffcf and 09ad0e0.

📒 Files selected for processing (1)
  • packages/universal-swap/tests/msg/oraichain-msg.spec.ts (1 hunks)
🧰 Additional context used
🪛 Gitleaks
packages/universal-swap/tests/msg/oraichain-msg.spec.ts

429-429: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


438-438: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


443-443: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


450-450: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


452-452: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


476-476: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


477-477: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


568-568: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


570-570: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


577-577: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


579-579: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (2)
packages/universal-swap/tests/msg/oraichain-msg.spec.ts (2)

1-7: LGTM: Imports and setup look good.

The imports and initial setup are well-organized and include all necessary dependencies for the tests.


1-635: Overall, a comprehensive test suite with room for improvement.

This test file provides thorough coverage of the OraichainMsg class functionality, which is commendable. It tests various scenarios including valid and invalid paths, different types of actions (swap, IBC bridge, IBC WASM bridge), and error cases.

Main points for improvement:

  1. Use parameterized tests to reduce redundancy across similar test cases.
  2. Extract common assertion logic into helper functions for better maintainability.
  3. Improve error handling tests by using more specific assertions.
  4. Consider splitting large test cases into smaller, more focused tests.
  5. Remove or implement the commented-out test case at the end of the file.
  6. Verify the usage of hardcoded addresses and consider using configuration files for such values.

Implementing these suggestions will make the test suite more robust, easier to maintain, and more aligned with best practices in test-driven development.

🧰 Tools
🪛 Gitleaks

429-429: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


438-438: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


443-443: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


450-450: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


452-452: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


476-476: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


477-477: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


568-568: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


570-570: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


577-577: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


579-579: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

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

🧹 Outside diff range and nitpick comments (1)
packages/universal-swap/tests/msg/oraichain-msg.spec.ts (1)

673-706: Remove or implement commented-out test case.

There is a commented-out test case at the end of the file that appears to be for the OsmosisMsg class, which is not the focus of this test file. Commented-out code can lead to confusion and maintenance issues. Consider one of the following actions:

  1. If this test is no longer relevant, remove it entirely.
  2. If it is still needed, move it to a separate test file for OsmosisMsg.
  3. If it is work in progress, add a TODO comment explaining the plan for this test.

Example TODO comment:

// TODO: Implement tests for OsmosisMsg in a separate file
// This test is temporarily commented out and will be moved to osmosis-msg.spec.ts

Addressing this will improve the clarity and maintainability of the test suite.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 09ad0e0 and 10dfe29.

📒 Files selected for processing (2)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/tests/msg/oraichain-msg.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/universal-swap/src/msg/chains/oraichain.ts
🧰 Additional context used
🪛 Gitleaks
packages/universal-swap/tests/msg/oraichain-msg.spec.ts

429-429: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


438-438: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


443-443: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


450-450: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


452-452: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


476-476: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


477-477: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


568-568: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


570-570: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


577-577: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


579-579: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


606-606: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


608-608: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


615-615: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


617-617: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


641-641: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


642-642: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (2)
packages/universal-swap/tests/msg/oraichain-msg.spec.ts (2)

1-7: LGTM: Import statements are appropriate.

The import statements cover all necessary functions and types for testing the OraichainMsg class. The use of Vitest for testing and the inclusion of utility functions from the project's common module are appropriate.


1-706: Overall assessment: Comprehensive tests with room for improvement.

The test suite for the OraichainMsg class is thorough and covers various scenarios, including swap operations, IBC transfers, and IBC WASM bridges. However, there are several areas where the tests can be improved:

  1. Test structure: Consider using parameterized tests to reduce redundancy and improve maintainability.
  2. Helper functions: Extract common assertion logic into reusable helper functions.
  3. Setup: Use beforeEach for common setup tasks.
  4. Constants: Replace hardcoded addresses and tokens with constants or configuration files.
  5. Error handling: Improve specificity in error message assertions.
  6. Commented code: Address the commented-out test case at the end of the file.

Implementing these suggestions will enhance the overall quality, readability, and maintainability of the test suite. Great job on the comprehensive coverage, and with these improvements, the tests will be even more robust and easier to maintain.

🧰 Tools
🪛 Gitleaks

429-429: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


438-438: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


443-443: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


450-450: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


452-452: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


476-476: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


477-477: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


568-568: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


570-570: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


577-577: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


579-579: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


606-606: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


608-608: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


615-615: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


617-617: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


641-641: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


642-642: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

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: 14

🧹 Outside diff range and nitpick comments (5)
packages/universal-swap/src/msg/chains/oraichain.ts (1)

23-521: Overall review summary and suggestions for improvement

The OraichainMsg class successfully implements various methods for handling token swaps and bridging operations within the Oraichain ecosystem. However, there are several areas where the code could be improved:

  1. Recurring issues:

    • Fix operator precedence issues in ternary operations.
    • Correct typos in error messages.
  2. Code organization:

    • Consider refactoring long methods (e.g., genMemoAsMiddleware, genExecuteMsg) into smaller, more focused helper methods.
    • Group related functionality together for better readability.
  3. Error handling:

    • Implement more robust error checking, especially when dealing with potentially undefined values.
  4. Naming conventions:

    • Fix the typo in the method name pasreConverterMsgToPoolId.
  5. Comments and documentation:

    • Add more inline comments to explain complex logic.
    • Consider adding JSDoc comments for better code documentation.
  6. Testing:

    • Ensure comprehensive unit tests are in place for all methods, especially those with complex logic.

By addressing these points, the code will become more maintainable, readable, and robust. Consider implementing these changes incrementally to improve the overall quality of the OraichainMsg class.

packages/universal-swap/src/helper.ts (4)

314-321: LGTM! Consider adding a type annotation for better clarity.

The new getAddress method is a well-structured addition to the UniversalSwapHelper class. It provides a clear way to convert between different address formats, which is useful for cross-chain operations.

Consider adding a type annotation for the return value to improve code clarity:

-  static getAddress = (prefix: string, { address60, address118 }, coinType: number = 118) => {
+  static getAddress = (prefix: string, { address60, address118 }: { address60: string, address118: string }, coinType: number = 118): string => {

Line range hint 322-412: Approved with suggestions for improvement.

The updates to addOraiBridgeRoute method enhance its functionality and flexibility. The new object parameter for addresses and the addition of alphaSmartRoute improve the method's capability to handle various routing scenarios.

Consider the following improvements:

  1. Extract the isAlphaIbcWasm logic into a separate private method for better readability and maintainability.
  2. Add comments explaining the purpose of the alphaSmartRoute parameter and the isAlphaIbcWasm case.
  3. Consider using TypeScript interfaces for the complex object parameters to improve type safety and developer experience.

Example refactoring for the isAlphaIbcWasm logic:

private static handleAlphaIbcWasm(alphaSmartRoute: RouterResponse, addresses: AddressesType): string {
  // ... extracted logic here ...
}

// In the main method:
if (swapOption.isAlphaIbcWasm) {
  swapRoute = this.handleAlphaIbcWasm(alphaSmartRoute, addresses);
}

Line range hint 786-826: Clarify the condition and consider using TypeScript interfaces.

The addition of useAlphaIbcWasm to the routerOption increases the flexibility of the handleSimulateSwap method. However, the condition for setting fromInfo and toInfo could be clearer.

  1. Clarify the condition for setting fromInfo and toInfo:
-    if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) {
+    if (query?.routerOption?.useAlphaIbcWasm || (!query?.routerOption?.useIbcWasm && !query?.routerOption?.useAlphaIbcWasm)) {

This change makes it explicit that fromInfo and toInfo are set to the original values when useAlphaIbcWasm is true, or when both useIbcWasm and useAlphaIbcWasm are false.

  1. Consider using TypeScript interfaces for complex parameters:
interface RouterOption {
  useAlphaSmartRoute?: boolean;
  useIbcWasm?: boolean;
  useAlphaIbcWasm?: boolean;
}

interface SimulateSwapQuery {
  originalFromInfo: TokenItemType;
  originalToInfo: TokenItemType;
  originalAmount: number;
  routerClient: OraiswapRouterReadOnlyInterface;
  routerOption?: RouterOption;
  routerConfig?: RouterConfigSmartRoute;
}

static handleSimulateSwap = async (query: SimulateSwapQuery): Promise<SimulateResponse> => {
  // ... existing code ...
}

These interfaces will improve type safety and make the code more self-documenting.


Line range hint 811-817: LGTM! Consider using object destructuring for improved readability.

The updates to the default values for routerConfig are good improvements that provide more specific and consistent defaults. The use of optional chaining is a good practice for safe property access.

Consider using object destructuring to make the code more concise and readable:

const {
  url = "https://osor.oraidex.io",
  path = "/smart-router/alpha-router",
  protocols = ["Oraidex", "OraidexV3"],
  dontAllowSwapAfter = ["Oraidex", "OraidexV3"],
  maxSplits = 10
} = query?.routerConfig ?? {};

const routerConfigDefault = {
  url,
  path,
  protocols,
  dontAllowSwapAfter,
  maxSplits
};

This approach reduces repetition and makes it clearer that these are default values being assigned.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 10dfe29 and 8ccebb3.

📒 Files selected for processing (3)
  • packages/universal-swap/src/helper.ts (10 hunks)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/tests/msg/oraichain-msg.spec.ts (1 hunks)
🧰 Additional context used
🪛 Gitleaks
packages/universal-swap/tests/msg/oraichain-msg.spec.ts

429-429: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


438-438: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


443-443: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


450-450: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


452-452: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


476-476: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


477-477: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


568-568: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


570-570: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


577-577: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


579-579: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


606-606: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


608-608: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


615-615: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


617-617: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


641-641: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


642-642: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


677-677: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


679-679: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


686-686: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


688-688: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (6)
packages/universal-swap/src/msg/chains/oraichain.ts (4)

230-278: ⚠️ Potential issue

Fix operator precedence and typo in error message (similar to getProtoForPostAction)

The getPostAction method has similar issues to those found in getProtoForPostAction:

  1. There's an operator precedence issue in the IBC wasm transfer case.
  2. The error message for missing prefix or OBridge address contains a typo.

Apply these diffs to fix the issues:

  1. Fix operator precedence:
- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
  1. Fix typo in error message:
- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");

356-520: 🛠️ Refactor suggestion

Consider refactoring genExecuteMsg for improved maintainability

The genExecuteMsg method is quite long and handles multiple complex cases. While the implementation seems correct, its length and complexity might make it harder to maintain and test.

Consider refactoring this method into smaller, more focused helper methods for each case (IBC transfer, IBC wasm transfer, and swap and action). This would improve readability and make the code easier to maintain and test.

Example refactoring:

private genIbcTransferMsg(bridgeInfo: BridgeMsgInfo): EncodeObject {
  // Implementation for IBC transfer
}

private genIbcWasmTransferMsg(bridgeInfo: BridgeMsgInfo): EncodeObject {
  // Implementation for IBC wasm transfer
}

private genSwapAndActionMsg(swapOps: SwapOperation[], bridgeInfo?: BridgeMsgInfo): EncodeObject {
  // Implementation for swap and action
}

genExecuteMsg(): EncodeObject {
  let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo();

  if (swapOps.length == 0) {
    if (bridgeInfo.sourcePort == "transfer") {
      return this.genIbcTransferMsg(bridgeInfo);
    }
    if (bridgeInfo.sourcePort.startsWith("wasm")) {
      return this.genIbcWasmTransferMsg(bridgeInfo);
    }
    throw generateError("Error on generate executeMsg on Oraichain: Only support ibc or ibc wasm bridge");
  }

  return this.genSwapAndActionMsg(swapOps, bridgeInfo);
}

This refactoring would make the genExecuteMsg method more concise and easier to understand at a glance.

Additionally, please address the recurring issues with operator precedence and typos in error messages, as mentioned in previous comments.


141-141: ⚠️ Potential issue

Fix typo in error message

There's a typo in the error message: "bride" should be "bridge".

Apply this diff to fix the typo:

- throw generateError("Only support swap + convert + bride on Oraichain");
+ throw generateError("Only support swap + convert + bridge on Oraichain");

Likely invalid or redundant comment.


201-228: ⚠️ Potential issue

Verify that bridgeInfo is defined before using in getProtoForPostAction

In the method genMemoForIbcWasm, bridgeInfo is passed to getProtoForPostAction without ensuring it's defined. If bridgeInfo is undefined, this could lead to runtime errors.

Add a check to ensure bridgeInfo is not undefined before using it:

let memo: Memo = {
  userSwap,
  minimumReceive: this.minimumReceive,
  timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
- postSwapAction: this.getProtoForPostAction(bridgeInfo),
+ postSwapAction: this.getProtoForPostAction(bridgeInfo || {}),
  recoveryAddr: this.currentChainAddress
};

Alternatively, handle the undefined case within getProtoForPostAction.

Likely invalid or redundant comment.

packages/universal-swap/tests/msg/oraichain-msg.spec.ts (2)

1-7: LGTM: Import statements are appropriate.

The import statements are well-organized and include all necessary dependencies for the test suite. They correctly import the OraichainMsg class which is the main subject of these tests.


1-726: Addressing static analysis hints: False positives for API keys.

The static analysis tool Gitleaks has flagged several lines in this file as potentially containing Generic API Keys. However, upon review, these appear to be false positives:

  1. The flagged lines contain blockchain addresses (e.g., contract addresses, token addresses) and other blockchain-related identifiers.
  2. These are not actual API keys, but rather public identifiers used in blockchain transactions and smart contracts.
  3. It's common for blockchain addresses to have a similar format to API keys, which likely triggered the false positive.

No action is required to address these flags. However, it's always a good practice to ensure that any sensitive information (if present) is properly handled and not exposed in test files.

🧰 Tools
🪛 Gitleaks

429-429: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


438-438: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


443-443: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


450-450: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


452-452: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


476-476: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


477-477: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


568-568: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


570-570: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


577-577: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


579-579: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


606-606: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


608-608: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


615-615: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


617-617: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


641-641: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


642-642: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


677-677: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


679-679: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


686-686: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


688-688: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +43 to +55
setMinimumReceiveForSwap(slippage: number = 0.01) {
if (slippage > 1) {
throw generateError("Slippage must be less than 1");
}
let [_, bridgeInfo] = this.getSwapAndBridgeInfo();

let returnAmount = bridgeInfo ? bridgeInfo.amount : this.path.tokenOutAmount;
let minimumReceive = new BigDecimal(1 - slippage).mul(returnAmount).toString();
if (minimumReceive.includes(".")) {
minimumReceive = minimumReceive.split(".")[0];
}
this.minimumReceive = minimumReceive;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using a more precise method for handling decimals

The current implementation truncates decimal places when calculating the minimum receive amount. This might lead to precision loss, especially for large transactions.

Consider using a library like big.js or decimal.js for more precise decimal arithmetic, or rounding to a fixed number of decimal places instead of truncating.

Example using big.js:

import Big from 'big.js';

// ...

let minimumReceive = new Big(1 - slippage).mul(returnAmount).round(0, Big.roundDown).toString();
this.minimumReceive = minimumReceive;

This approach ensures more accurate calculations and avoids potential issues with large numbers or small slippage values.

Comment on lines +282 to +350
genMemoAsMiddleware(): MiddlewareResponse {
let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo();

// we have 2 cases:
// - first case: only ibc transfer (build IBC forward middleware msg)
// - second case: swap + action => swap through osor entry point

if (swapOps.length == 0) {
// bridge only
if (bridgeInfo.sourcePort != "transfer") {
throw generateError("Error on generate memo as middleware: Only support ibc bridge");
}

// ibc bridge
return {
receiver: this.currentChainAddress,
memo: JSON.stringify({
forward: {
receiver: this.receiver,
port: bridgeInfo.sourcePort,
channel: bridgeInfo.sourceChannel,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
retries: 2,
next: this.memo
}
})
};
}

let tokenOutOfSwap = swapOps[swapOps.length - 1].denom_out;
let min_asset = isCw20Token(tokenOutOfSwap)
? {
cw20: {
amount: this.minimumReceive,
address: tokenOutOfSwap
}
}
: {
native: {
amount: this.minimumReceive,
denom: tokenOutOfSwap
}
};

let msg: ExecuteMsg = {
swap_and_action: {
user_swap: {
swap_exact_asset_in: {
swap_venue_name: this.SWAP_VENUE_NAME,
operations: swapOps
}
},
min_asset: min_asset,
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
post_swap_action: this.getPostAction(bridgeInfo),
affiliates: []
}
};

return {
receiver: this.ENTRY_POINT_CONTRACT,
memo: JSON.stringify({
wasm: {
contract: this.ENTRY_POINT_CONTRACT,
msg
}
})
};
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refactoring genMemoAsMiddleware for improved maintainability

The genMemoAsMiddleware method is quite long and handles multiple complex cases. While the implementation seems correct, its length and complexity might make it harder to maintain and test.

Consider refactoring this method into smaller, more focused helper methods for each case (IBC transfer and swap + action). This would improve readability and make the code easier to maintain and test.

Example refactoring:

private genIbcTransferMemo(bridgeInfo: BridgeMsgInfo): MiddlewareResponse {
  // Implementation for IBC transfer
}

private genSwapAndActionMemo(swapOps: SwapOperation[], bridgeInfo?: BridgeMsgInfo): MiddlewareResponse {
  // Implementation for swap and action
}

genMemoAsMiddleware(): MiddlewareResponse {
  let [swapOps, bridgeInfo] = this.getSwapAndBridgeInfo();

  if (swapOps.length == 0) {
    if (bridgeInfo.sourcePort != "transfer") {
      throw generateError("Error on generate memo as middleware: Only support ibc bridge");
    }
    return this.genIbcTransferMemo(bridgeInfo);
  }

  return this.genSwapAndActionMemo(swapOps, bridgeInfo);
}

This refactoring would make the genMemoAsMiddleware method more concise and easier to understand at a glance.

Comment on lines +149 to +195
getProtoForPostAction(bridgeInfo?: BridgeMsgInfo): Memo_PostAction {
// case 1: transfer to receiver
if (!bridgeInfo) {
return {
transferMsg: {
toAddress: this.receiver
}
};
}

// case 2: ibc transfer
if (bridgeInfo.sourcePort == "transfer") {
return {
ibcTransferMsg: {
sourceChannel: bridgeInfo.sourceChannel,
sourcePort: bridgeInfo.sourcePort,
receiver: bridgeInfo.receiver,
memo: bridgeInfo.memo,
recoverAddress: this.currentChainAddress
}
};
}

// case 3: ibc wasm transfer

if (bridgeInfo.sourcePort.startsWith("wasm")) {
// handle noble & evm case
let prefix = "";
let isBridgeToEvm = isEthAddress(this.receiver);
if (isBridgeToEvm) {
if (!this.destPrefix || !this.obridgeAddress)
throw generateError("Missing prefix or Obridge address for bridge to EVM");
prefix = this.destPrefix;
}

return {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: isBridgeToEvm ? this.obridgeAddress : this.receiver,
remoteDenom: prefix + bridgeInfo.toToken,
memo: isBridgeToEvm ? prefix + this.receiver : this.memo
}
};
}

throw generateError("Missing postAction for ibc wasm memo");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix operator precedence and typo in error message

  1. There's an operator precedence issue in the IBC wasm transfer case. The + operator has higher precedence than the ternary operator, which may lead to unexpected behavior.

  2. The error message for missing prefix or OBridge address contains a typo: "os" should be "or".

Apply these diffs to fix the issues:

  1. Fix operator precedence:
- remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
  1. Fix typo in error message:
- throw generateError("Missing prefix os Obridge address for bridge to EVM");
+ throw generateError("Missing prefix or OBridge address for bridge to EVM");

Committable suggestion was skipped due to low confidence.

Comment on lines +63 to +90
pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
// In Oraichain, conversion from native token to CW20 token always occurs
// TODO: Query the converter contract to determine the appropriate conversion method

if (isCw20Token(tokenIn)) {
// Convert in reverse
return toBinary({
contract: CONVERTER_CONTRACT,
msg: toBinary({
convert_reverse: {
from: {
native_token: {
denom: tokenOut
}
}
}
})
});
} else {
// Convert normally
return toBinary({
contract: CONVERTER_CONTRACT,
msg: toBinary({
convert: {}
})
});
}
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in method name and consider implementing TODO

  1. The method name pasreConverterMsgToPoolId contains a typo. It should be parseConverterMsgToPoolId.

  2. There's a TODO comment suggesting to query the converter contract to determine the appropriate conversion method. Consider implementing this feature to make the conversion process more dynamic and less prone to errors.

Apply this diff to fix the typo:

- pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
+ parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {

Would you like assistance in implementing the TODO for querying the converter contract?

📝 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
pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
// In Oraichain, conversion from native token to CW20 token always occurs
// TODO: Query the converter contract to determine the appropriate conversion method
if (isCw20Token(tokenIn)) {
// Convert in reverse
return toBinary({
contract: CONVERTER_CONTRACT,
msg: toBinary({
convert_reverse: {
from: {
native_token: {
denom: tokenOut
}
}
}
})
});
} else {
// Convert normally
return toBinary({
contract: CONVERTER_CONTRACT,
msg: toBinary({
convert: {}
})
});
}
};
parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => {
// In Oraichain, conversion from native token to CW20 token always occurs
// TODO: Query the converter contract to determine the appropriate conversion method
if (isCw20Token(tokenIn)) {
// Convert in reverse
return toBinary({
contract: CONVERTER_CONTRACT,
msg: toBinary({
convert_reverse: {
from: {
native_token: {
denom: tokenOut
}
}
}
})
});
} else {
// Convert normally
return toBinary({
contract: CONVERTER_CONTRACT,
msg: toBinary({
convert: {}
})
});
}
};


export class OraichainMsg extends ChainMsg {
SWAP_VENUE_NAME = "oraidex";
ENTRY_POINT_CONTRACT = "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn"; // FIXME: use mainnet
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

ENTRY_POINT_CONTRACT Still Requires Update for Mainnet

The ENTRY_POINT_CONTRACT constant in packages/universal-swap/src/msg/chains/oraichain.ts still contains the // FIXME: use mainnet comment. Please update it with the correct mainnet contract address before deploying to production.

  • File: packages/universal-swap/src/msg/chains/oraichain.ts, line 25
🔗 Analysis chain

Update ENTRY_POINT_CONTRACT for mainnet

The ENTRY_POINT_CONTRACT still contains a FIXME comment. Please update it with the correct mainnet contract address before deploying to production.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check if the ENTRY_POINT_CONTRACT has been updated
grep -n 'ENTRY_POINT_CONTRACT.*FIXME' packages/universal-swap/src/msg/chains/oraichain.ts

Length of output: 208

Comment on lines +423 to +562
let receiver = "0x0000000000000000000000000000000000000000";
const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2";
const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf";
let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, "oraib", oraiBridgeAddr);

let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo();
expect(bridgeInfo).toEqual({
amount: "936043",
sourceChannel: "channel-29",
sourcePort: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
toToken: "0x55d398326f99059fF775485246999027B3197955",
fromChain: "Oraichain",
toChain: "0x38"
});
expect(swapOps).toEqual([
{
denom_in: "orai",
denom_out: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
pool: "orai1c5s03c3l336dgesne7dylnmhszw8554tsyy9yt"
}
]);

let memoAsMiddleware = oraichainMsg.genMemoAsMiddleware();
expect(memoAsMiddleware).toEqual({
receiver: oraichainMsg.ENTRY_POINT_CONTRACT,
memo: JSON.stringify({
wasm: {
contract: oraichainMsg.ENTRY_POINT_CONTRACT,
msg: {
swap_and_action: {
user_swap: {
swap_exact_asset_in: {
swap_venue_name: oraichainMsg.SWAP_VENUE_NAME,
operations: [
{
denom_in: "orai",
denom_out: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
pool: "orai1c5s03c3l336dgesne7dylnmhszw8554tsyy9yt"
}
]
}
},
min_asset: {
cw20: { amount: "1", address: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh" }
},
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
post_swap_action: {
ibc_wasm_transfer: {
ibc_wasm_info: {
local_channel_id: "channel-29",
remote_address: oraiBridgeAddr,
remote_denom: "oraib0x55d398326f99059fF775485246999027B3197955",
memo: "oraib0x0000000000000000000000000000000000000000"
}
}
},
affiliates: []
}
}
}
})
});

let executeMsg = oraichainMsg.genExecuteMsg();
expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract");

let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm();
expect(memoForIbcWasm).toEqual({
receiver: currentAddress,
memo: Buffer.from(
Memo.encode({
userSwap: {
swapVenueName: oraichainMsg.SWAP_VENUE_NAME,
swapExactAssetIn: {
operations: swapOps.map((operation) => ({
poolId: operation.pool,
denomIn: operation.denom_in,
denomOut: operation.denom_out
}))
}
},
minimumReceive: "1",
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
postSwapAction: {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: oraiBridgeAddr,
remoteDenom: "oraib0x55d398326f99059fF775485246999027B3197955",
memo: "oraib0x0000000000000000000000000000000000000000"
}
},
recoveryAddr: currentAddress
}).finish()
).toString("base64")
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider using constants for hardcoded addresses and improving test structure.

  1. The test uses hardcoded addresses and tokens. While this is common in tests, ensure these don't represent real, sensitive data. Consider using constants or a configuration file for these values:
const TEST_TOKENS = {
  ORAI: 'orai',
  USDT_BSC: '0x55d398326f99059fF775485246999027B3197955',
  // ... other tokens
};

const TEST_ADDRESSES = {
  RECEIVER: '0x0000000000000000000000000000000000000000',
  CURRENT_ADDRESS: 'orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2',
  ORAI_BRIDGE: 'oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf',
  // ... other addresses
};
  1. Consider parameterizing this test case along with the others, as suggested in previous comments.

  2. Extract the setup of validPath into a separate function to improve readability:

function createValidPathWithSwapAndIbcWasmBridge() {
  return {
    chainId: "Oraichain",
    tokenIn: TEST_TOKENS.ORAI,
    // ... rest of the path setup
  };
}

These changes would improve maintainability and reduce the risk of exposing sensitive information in tests.

🧰 Tools
🪛 Gitleaks

429-429: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


438-438: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


443-443: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


450-450: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


452-452: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


476-476: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


477-477: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +564 to +600
it("Invalid path in gen memo as middleware with ibc wasm bridge", () => {
const nextMemo = "{}";
const invalidPath = {
chainId: "Oraichain",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "936043",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "936043",
tokenOutChainId: "0x38",
actions: [
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "936043",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "306106",
tokenOutChainId: "0x38",
bridgeInfo: {
port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
channel: "channel-29"
}
}
]
};

let receiver = "0x0000000000000000000000000000000000000000";
const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2";
const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf";
let oraichainMsg = new OraichainMsg(invalidPath, "1", receiver, currentAddress, nextMemo, "oraib", oraiBridgeAddr);

try {
oraichainMsg.genMemoAsMiddleware();
} catch (err) {
expect(err).toEqual(generateError(`Error on generate memo as middleware: Only support ibc bridge`));
}
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling test.

The error handling test is good, but it can be improved:

  1. Use a more specific error message:
const errorMessage = `Error on generate memo as middleware: Only support ibc bridge for chain ${invalidPath.chainId}`;
  1. Use expect().toThrow() for cleaner error testing:
expect(() => oraichainMsg.genMemoAsMiddleware()).toThrow(errorMessage);

These changes would make the test more robust and easier to understand.

🧰 Tools
🪛 Gitleaks

568-568: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


570-570: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


577-577: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


579-579: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +602 to +671
it("Valid path in genMemoForIbcWasm with ibc wasm bridge only", () => {
const nextMemo = "{}";
const validPath = {
chainId: "Oraichain",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "936043",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "306106",
tokenOutChainId: "0x38",
actions: [
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "936043",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "306106",
tokenOutChainId: "0x38",
bridgeInfo: {
port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
channel: "channel-29"
}
}
]
};

let receiver = "0x0000000000000000000000000000000000000000";
const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2";
const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf";
let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, "oraib", oraiBridgeAddr);

let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo();
expect(bridgeInfo).toEqual({
amount: "936043",
sourceChannel: "channel-29",
sourcePort: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
toToken: "0x55d398326f99059fF775485246999027B3197955",
fromChain: "Oraichain",
toChain: "0x38"
});
expect(swapOps).toEqual([]);

let executeMsg = oraichainMsg.genExecuteMsg();
expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract");

let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm();
expect(memoForIbcWasm).toEqual({
receiver: currentAddress,
memo: Buffer.from(
Memo.encode({
userSwap: undefined,
minimumReceive: "1",
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
postSwapAction: {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: oraiBridgeAddr,
remoteDenom: "oraib0x55d398326f99059fF775485246999027B3197955",
memo: "oraib0x0000000000000000000000000000000000000000"
}
},
recoveryAddr: currentAddress
}).finish()
).toString("base64")
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Apply consistent improvements across all test cases.

This test case is well-structured but could benefit from the same improvements suggested for previous test cases:

  1. Use constants for hardcoded addresses and tokens, as suggested earlier.

  2. Consider including this in the parameterized tests:

const testCases = [
  // ... previous cases
  { name: "ibc wasm bridge only", path: createValidPathWithIbcWasmBridgeOnly() },
];
  1. Extract common assertion logic into helper functions:
function assertMemoForIbcWasm(result, expected) {
  expect(result).toEqual({
    receiver: expected.receiver,
    memo: expect.any(String) // Use more specific matcher if possible
  });
  // Add more specific checks for the encoded memo content
}

Applying these improvements consistently across all test cases will enhance the overall quality and maintainability of the test suite.

🧰 Tools
🪛 Gitleaks

606-606: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


608-608: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


615-615: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


617-617: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


641-641: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


642-642: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +673 to +725
it("Valid path but missing obridge address or destPrefix on bridge to evm", () => {
const nextMemo = "{}";
const validPath = {
chainId: "Oraichain",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "936043",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "306106",
tokenOutChainId: "0x38",
actions: [
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "936043",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "306106",
tokenOutChainId: "0x38",
bridgeInfo: {
port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
channel: "channel-29"
}
}
]
};
let receiver = "0x0000000000000000000000000000000000000000";
const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2";
const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf";
const destPrefix = "oraib";
// missing oraibridge address
try {
let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, destPrefix);
oraichainMsg.genExecuteMsg();
} catch (err) {
expect(err).toEqual(generateError(`Missing prefix or Obridge address for bridge to EVM`));
}

// missing destPrefix
try {
let oraichainMsg = new OraichainMsg(
validPath,
"1",
receiver,
currentAddress,
nextMemo,
undefined,
oraiBridgeAddr
);
oraichainMsg.genExecuteMsg();
} catch (err) {
expect(err).toEqual(generateError(`Missing prefix or Obridge address for bridge to EVM`));
}
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve error handling tests with parameterization.

The error handling tests are good, but they can be improved:

  1. Use parameterized tests to reduce duplication:
const errorCases = [
  { name: 'missing obridge address', destPrefix: 'oraib', obridgeAddr: undefined },
  { name: 'missing destPrefix', destPrefix: undefined, obridgeAddr: 'oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf' }
];

it.each(errorCases)('should throw error when $name', ({ destPrefix, obridgeAddr }) => {
  const oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, destPrefix, obridgeAddr);
  expect(() => oraichainMsg.genExecuteMsg()).toThrow(generateError('Missing prefix or Obridge address for bridge to EVM'));
});
  1. Use expect().toThrow() for cleaner error testing.

  2. Consider adding a test case for when both obridge address and destPrefix are provided to ensure the happy path works as expected.

These changes would make the tests more concise, easier to maintain, and more comprehensive.

🧰 Tools
🪛 Gitleaks

677-677: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


679-679: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


686-686: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


688-688: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +1 to +726

let receiver = "0x0000000000000000000000000000000000000000";
const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2";
const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf";
let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, "oraib", oraiBridgeAddr);

let [swapOps, bridgeInfo] = oraichainMsg.getSwapAndBridgeInfo();
expect(bridgeInfo).toEqual({
amount: "936043",
sourceChannel: "channel-29",
sourcePort: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
memo: "{}",
receiver: receiver,
timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
fromToken: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
toToken: "0x55d398326f99059fF775485246999027B3197955",
fromChain: "Oraichain",
toChain: "0x38"
});
expect(swapOps).toEqual([]);

let executeMsg = oraichainMsg.genExecuteMsg();
expect(executeMsg.typeUrl).toEqual("/cosmwasm.wasm.v1.MsgExecuteContract");

let memoForIbcWasm = oraichainMsg.genMemoForIbcWasm();
expect(memoForIbcWasm).toEqual({
receiver: currentAddress,
memo: Buffer.from(
Memo.encode({
userSwap: undefined,
minimumReceive: "1",
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
postSwapAction: {
ibcWasmTransferMsg: {
localChannelId: bridgeInfo.sourceChannel,
remoteAddress: oraiBridgeAddr,
remoteDenom: "oraib0x55d398326f99059fF775485246999027B3197955",
memo: "oraib0x0000000000000000000000000000000000000000"
}
},
recoveryAddr: currentAddress
}).finish()
).toString("base64")
});
});

it("Valid path but missing obridge address or destPrefix on bridge to evm", () => {
const nextMemo = "{}";
const validPath = {
chainId: "Oraichain",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "936043",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "306106",
tokenOutChainId: "0x38",
actions: [
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "936043",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "306106",
tokenOutChainId: "0x38",
bridgeInfo: {
port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
channel: "channel-29"
}
}
]
};
let receiver = "0x0000000000000000000000000000000000000000";
const currentAddress = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2";
const oraiBridgeAddr = "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf";
const destPrefix = "oraib";
// missing oraibridge address
try {
let oraichainMsg = new OraichainMsg(validPath, "1", receiver, currentAddress, nextMemo, destPrefix);
oraichainMsg.genExecuteMsg();
} catch (err) {
expect(err).toEqual(generateError(`Missing prefix or Obridge address for bridge to EVM`));
}

// missing destPrefix
try {
let oraichainMsg = new OraichainMsg(
validPath,
"1",
receiver,
currentAddress,
nextMemo,
undefined,
oraiBridgeAddr
);
oraichainMsg.genExecuteMsg();
} catch (err) {
expect(err).toEqual(generateError(`Missing prefix or Obridge address for bridge to EVM`));
}
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Overall test suite review and suggestions for improvement.

The test suite for the OraichainMsg class is comprehensive and covers various scenarios. However, there are several areas where it could be improved:

  1. Parameterization: Many test cases follow similar patterns. Consider using more parameterized tests to reduce code duplication and make it easier to add new test cases.

  2. Helper Functions: Extract common assertion logic into helper functions to improve readability and reduce repetition.

  3. Constants: Replace hardcoded values (especially addresses and tokens) with constants or configuration objects to improve maintainability and reduce the risk of exposing sensitive information.

  4. Error Handling: Improve error handling tests by using more specific error message assertions and consistent testing patterns.

  5. Test Structure: Consider breaking down large test cases into smaller, more focused tests. This will make the tests easier to understand and maintain.

  6. Setup and Teardown: Use beforeEach and afterEach hooks to set up common objects and clean up after tests.

  7. Comments: Add more comments explaining the purpose of each test case and any complex setup or assertions.

Implementing these suggestions will significantly improve the quality, maintainability, and readability of the test suite.

🧰 Tools
🪛 Gitleaks

429-429: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


438-438: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


443-443: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


450-450: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


452-452: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


476-476: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


477-477: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


568-568: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


570-570: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


577-577: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


579-579: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


606-606: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


608-608: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


615-615: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


617-617: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


641-641: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


642-642: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


677-677: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


679-679: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


686-686: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


688-688: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

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

🧹 Outside diff range and nitpick comments (9)
packages/universal-swap/tests/helper.spec.ts (3)

589-593: Update to addOraiBridgeRoute test looks good, but consider adding more assertions.

The test case for addOraiBridgeRoute has been updated to use an object for the sourceReceiver and destReceiver parameters, which is good. However, you might want to add more assertions to test the other properties of the returned object.

Consider adding assertions for other properties of the returned object, such as:

expect(result.universalSwapType).toEqual("other-networks-to-oraichain");
expect(result.isSmartRouter).toEqual(false);

Line range hint 812-897: New test cases for generateSmartRouteForSwap look comprehensive, but consider adding error cases.

The new test cases for generateSmartRouteForSwap are well-structured and cover different scenarios. However, it might be beneficial to add test cases for error conditions.

Consider adding test cases for error conditions, such as:

  1. Invalid asset information
  2. Unsupported chain IDs
  3. Network errors (by mocking the querySmartRoute function to throw an error)

Example:

it("should throw an error for unsupported chain ID", async () => {
  await expect(
    UniversalSwapHelper.generateSmartRouteForSwap(
      ORAIX_INFO,
      "UnsupportedChain",
      NEUTARO_INFO,
      "Oraichain",
      "1",
      { url: "test" }
    )
  ).rejects.toThrow("Unsupported chain ID");
});

Line range hint 1-897: Overall good improvements, but consider adding more tests and improving test descriptions.

The changes to this file are generally good and improve the test coverage. However, there are a few suggestions to further enhance the quality of the tests:

  1. Add a test for the generateConvertCw20Erc20Message function, which is currently not covered.
  2. Use more descriptive test names to clearly indicate what each test is checking.

Here's an example of how you could add a test for generateConvertCw20Erc20Message:

it("should generate correct convert Cw20 to Erc20 message", () => {
  const currentBal = { [INJECTIVE_ORAICHAIN_DENOM]: "1000" };
  const tokenInfo = getTokenOnOraichain("injective-protocol");
  const toSend = coin(500, INJECTIVE_ORAICHAIN_DENOM);
  const result = universalHelper.generateConvertCw20Erc20Message(currentBal, tokenInfo, "orai123", toSend);
  
  expect(result.length).toBe(1);
  expect(result[0]).toHaveProperty('wasm');
  expect(result[0].wasm).toHaveProperty('execute');
  expect(result[0].wasm.execute).toHaveProperty('contract_addr', INJECTIVE_CONTRACT);
  // Add more specific assertions about the message content
});

Also, consider renaming some test descriptions to be more specific, for example:

"test-generateSmartRouteForSwap" -> "should generate correct smart route for swap from ORAIX to NEUTARO"
packages/universal-swap/src/helper.ts (4)

314-321: LGTM! Consider adding a brief comment explaining the purpose.

The new getAddress method effectively converts between different address formats. It's a useful addition to the UniversalSwapHelper class.

Consider adding a brief comment explaining the purpose of this method and the significance of the coin types (60 and 118) for better code readability and maintainability.


Line range hint 322-415: Approved. Consider improving error handling and code organization.

The updates to addOraiBridgeRoute method introduce support for new routing options, particularly the "alpha IBC WASM" case. The changes look good overall.

  1. Consider extracting the alpha IBC WASM handling logic (lines 374-411) into a separate private method for better code organization and readability.
  2. Improve error handling by providing more specific error messages, especially for the error thrown on line 379.
  3. Consider using optional chaining (?.) for safer property access, particularly when checking swapOption properties.

Line range hint 786-827: LGTM! Consider standardizing variable naming for consistency.

The updates to handleSimulateSwap method appropriately incorporate support for the new alpha IBC WASM functionality. The changes look good and improve the method's flexibility.

For consistency, consider renaming dontAllowSwapAfter to dontAlowSwapAfter to match the usage in line 612. Alternatively, fix the typo in line 612 to use dontAllowSwapAfter. Ensure consistent naming throughout the codebase.


Line range hint 1245-1309: Approved. Consider improving readability and adding comments.

The new flattenSmartRouters method effectively reorganizes complex route data into a flatter, more manageable structure. This is a valuable addition to the UniversalSwapHelper class.

  1. Consider breaking down the large function into smaller, more focused helper functions to improve readability and maintainability.
  2. Add comments explaining the purpose of key logic blocks, especially the conditions for handling different types of actions and paths.
  3. Consider using more descriptive variable names to make the code self-documenting. For example, isSwapType could be isSwapAction for clarity.
  4. Use TypeScript type annotations for the function parameters and return type to improve code clarity and catch potential type-related issues early.
packages/universal-swap/src/handler.ts (1)

Line range hint 1-1466: Suggest adding comprehensive unit tests and updating documentation

The changes to the UniversalSwapHandler class, particularly in the swapCosmosToOtherNetwork and processUniversalSwap methods, have introduced new parameters and more complex logic. To ensure the reliability of these changes:

  1. Consider adding more comprehensive unit tests to cover the new scenarios, especially for different combinations of swap types and chain IDs.
  2. Update the documentation for the UniversalSwapHandler class to reflect the new parameters and functionality, particularly for the swapCosmosToOtherNetwork and processUniversalSwap methods.

These steps will help maintain the code's reliability and make it easier for other developers to understand and use the updated functionality.

packages/universal-swap/src/universal-demos/alpha-ibc-new.ts (1)

156-157: Consider externalizing hardcoded simulation values.

Hardcoding simulatePrice and simulateAmount reduces flexibility and maintainability. Consider externalizing these values to a configuration file or environment variables.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 8ccebb3 and 9b516c6.

📒 Files selected for processing (4)
  • packages/universal-swap/src/handler.ts (5 hunks)
  • packages/universal-swap/src/helper.ts (9 hunks)
  • packages/universal-swap/src/universal-demos/alpha-ibc-new.ts (1 hunks)
  • packages/universal-swap/tests/helper.spec.ts (2 hunks)
🧰 Additional context used
🪛 Gitleaks
packages/universal-swap/src/universal-demos/alpha-ibc-new.ts

16-16: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


18-18: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


25-25: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


27-27: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


39-39: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


48-48: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (10)
packages/universal-swap/tests/helper.spec.ts (1)

602-611: Good update to addOraiBridgeRoute empty sourceReceiver test.

The test case for addOraiBridgeRoute with an empty sourceReceiver has been correctly updated to use the new object parameter structure. This change maintains consistency with the updated function signature.

packages/universal-swap/src/helper.ts (1)

838-838: LGTM! Consistent parameter naming.

The renaming of the amount parameter to amountSimulate in the simulateSwapUsingSmartRoute function call maintains consistency with the changes made in the handleSimulateSwap method.

packages/universal-swap/src/handler.ts (6)

1066-1075: Update method signature to include new parameters

The swapCosmosToOtherNetwork method now includes additional parameters such as alphaSmartRoutes and userSlippage. This change allows for more flexibility in the swap process.


1200-1211: Confirm correct parameter passing to addOraiBridgeRoute

The addOraiBridgeRoute function call has been updated with new parameters. Verify that all required parameters are passed correctly and in the right order.


1162-1163: Verify correct usage of new variables and chain IDs

New variables have been added to the method, including alphaSmartRoutes and userSlippage. Additionally, there are changes in how chain IDs are accessed, now using COSMOS_CHAIN_IDS enum. Ensure that these changes are consistent throughout the codebase.

#!/bin/bash
# Check for consistent usage of COSMOS_CHAIN_IDS
rg --type typescript 'COSMOS_CHAIN_IDS\.' -A 3

Also applies to: 1184-1187


Line range hint 1214-1221: Review the conditional logic for different swap types

The conditional logic for handling different swap types has been updated. Ensure that all cases are covered correctly and that the appropriate methods are called for each scenario.

#!/bin/bash
# Check for consistent handling of swap types
ast-grep --lang typescript --pattern 'if ($condition) { $body }'

1193-1197: Review the logic for handling Injective addresses

A new check has been added for Injective addresses. Ensure that this logic is correct and necessary for the swap process.

#!/bin/bash
# Verify the usage of COSMOS_CHAIN_IDS.INJECTVE
rg --type typescript 'COSMOS_CHAIN_IDS\.INJECTVE' -A 3

1105-1111: Ensure correct parameter usage in addOraiBridgeRoute

The addOraiBridgeRoute function now includes userSlippage and alphaSmartRoutes parameters. Make sure these new parameters are being used correctly within the function.

packages/universal-swap/src/universal-demos/alpha-ibc-new.ts (2)

134-134: Ensure secure handling of the mnemonic phrase.

Using process.env.MNEMONIC to access the mnemonic is acceptable, but please verify that the mnemonic is securely stored and not accidentally exposed or committed to the repository.


16-16: Verify that no sensitive information is exposed in token addresses.

Static analysis tools detected potential exposure of sensitive information at these lines. These appear to be public token addresses or identifiers. Please verify that no secrets, private keys, or sensitive data are inadvertently included in the code.

Also applies to: 18-18, 25-25, 27-27, 39-39, 48-48, 64-64, 76-76, 101-101, 117-117

🧰 Tools
🪛 Gitleaks

16-16: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +169 to +171
} catch (error) {
console.trace("error: ", error);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling and avoid exposing stack traces.

Using console.trace may expose sensitive information in logs and is more suitable for debugging purposes. Consider implementing proper error handling and logging mechanisms.

Apply this diff to improve error handling:

-      console.trace("error: ", error);
+      console.error("An error occurred during the universal swap:", error.message);
📝 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
} catch (error) {
console.trace("error: ", error);
}
} catch (error) {
console.error("An error occurred during the universal swap:", error.message);
}

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: 3

🧹 Outside diff range and nitpick comments (1)
packages/universal-swap/tests/msg/msgs.spec.ts (1)

1-155: Overall, good test coverage with room for structural improvements.

The test suite for generateMsgSwap is comprehensive and covers crucial scenarios, including error cases and a complex successful case. The use of detailed, realistic test data is commendable as it ensures the function is tested under conditions similar to production use.

To further enhance the test suite:

  1. Implement the structural improvements suggested in previous comments (separate it blocks, refactor large objects, use constants).
  2. Consider adding more test cases to cover edge cases or different combinations of input parameters.
  3. If not already present elsewhere, add tests for invalid input data (e.g., malformed route object, invalid addresses).
  4. Consider testing with different slippage values to ensure they're correctly applied.

These enhancements will result in a more robust, maintainable, and comprehensive test suite for this critical function.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9b516c6 and e09ac6e.

📒 Files selected for processing (4)
  • packages/universal-swap/src/msg/chains/cosmos.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/chains/osmosis.ts (1 hunks)
  • packages/universal-swap/tests/msg/msgs.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/universal-swap/src/msg/chains/cosmos.ts
  • packages/universal-swap/src/msg/chains/oraichain.ts
  • packages/universal-swap/src/msg/chains/osmosis.ts
🧰 Additional context used
🔇 Additional comments (1)
packages/universal-swap/tests/msg/msgs.spec.ts (1)

1-6: LGTM: Imports and test suite setup are well-structured.

The necessary imports are correctly included, and the test suite is properly set up using Vitest's describe function. This follows testing best practices.

Comment on lines +8 to +77
let route = {
swapAmount: "10000000",
returnAmount: "7340955",
paths: [
{
chainId: "cosmoshub-4",
tokenIn: "uatom",
tokenInAmount: "10000000",
tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
tokenOutAmount: "10000000",
tokenOutChainId: "osmosis-1",
actions: [
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "uatom",
tokenInAmount: "10000000",
tokenOut: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
tokenOutAmount: "10000000",
tokenOutChainId: "osmosis-1",
bridgeInfo: {
port: "transfer",
channel: "channel-141"
}
}
]
},
{
chainId: "osmosis-1",
tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
tokenInAmount: "10000000",
tokenOut: "orai",
tokenOutAmount: "7340955",
tokenOutChainId: "Oraichain",
actions: [
{
type: "Swap",
protocol: "Osmosis",
tokenIn: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
tokenInAmount: "10000000",
tokenOut: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D",
tokenOutAmount: "7340955",
swapInfo: [
{
poolId: "1135",
tokenOut: "uosmo"
},
{
poolId: "2173",
tokenOut: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D"
}
]
},
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D",
tokenInAmount: "7340955",
tokenOut: "orai",
tokenOutAmount: "7340955",
tokenOutChainId: "Oraichain",
bridgeInfo: {
port: "transfer",
channel: "channel-216"
}
}
]
}
]
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refactoring the route object setup.

The route object is quite complex and lengthy. To improve readability and maintainability of the test file, consider one of the following options:

  1. Move this object to a separate file (e.g., testData.ts) and import it.
  2. Create a factory function that generates this object, allowing for easy customization in different test cases.

This refactoring would make the test file more focused on the actual test logic rather than data setup.

Comment on lines +79 to +92
// case 1: missing receiver address
try {
let res = generateMsgSwap(route, 0.1, {});
} catch (err) {
expect(err).toEqual(generateError(`Missing receiver when build msg in osmosis-1`));
}

// case 2: missing current chain address

try {
let res = generateMsgSwap(route, 0.1, { Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" });
} catch (err) {
expect(err).toEqual(generateError(`Missing address of osmosis-1`));
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve test structure and error assertion.

While the error scenarios are well-chosen, the test structure can be improved:

  1. Use separate it blocks for each test case. This improves test isolation and makes it easier to identify which specific case failed.
  2. Instead of using try-catch blocks, leverage Vitest's expect().toThrow() for cleaner error testing.

Here's an example of how you could refactor this:

it("should throw an error when receiver address is missing", () => {
  expect(() => generateMsgSwap(route, 0.1, {})).toThrow(
    "Missing receiver when build msg in osmosis-1"
  );
});

it("should throw an error when current chain address is missing", () => {
  expect(() => 
    generateMsgSwap(route, 0.1, { Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" })
  ).toThrow("Missing address of osmosis-1");
});

This approach makes the tests more readable and follows testing best practices.

Comment on lines +94 to +154
let res = generateMsgSwap(route, 0.1, {
Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
"cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
"osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
});
expect(res).toEqual({
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: "transfer",
sourceChannel: "channel-141",
receiver: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7",
token: { amount: "10000000", denom: "uatom" },
sender: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
memo: JSON.stringify({
wasm: {
contract: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7",
msg: {
swap_and_action: {
user_swap: {
swap_exact_asset_in: {
swap_venue_name: "osmosis-poolmanager",
operations: [
{
denom_in: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
denom_out: "uosmo",
pool: "1135"
},
{
denom_in: "uosmo",
denom_out: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D",
pool: "2173"
}
]
}
},
min_asset: {
native: {
amount: "6606859",
denom: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D"
}
},
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
post_swap_action: {
ibc_transfer: {
ibc_info: {
source_channel: "channel-216",
receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
memo: "",
recover_address: "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
}
}
},
affiliates: []
}
}
}
}),
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance test readability and maintainability.

The successful case test is comprehensive, but it can be improved:

  1. Break down the large assertion into smaller, more specific checks. This will make it easier to identify which part of the output is incorrect if the test fails.

  2. Use constants for repeated values (like addresses and channel IDs) to improve maintainability.

  3. Consider using snapshot testing for the large expected object structure.

Here's an example of how you could refactor part of this:

const ORAICHAIN_ADDRESS = "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2";
const COSMOS_HUB_ADDRESS = "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e";
const OSMOSIS_ADDRESS = "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t";

it("should generate correct swap message for a valid input", () => {
  const result = generateMsgSwap(route, 0.1, {
    Oraichain: ORAICHAIN_ADDRESS,
    "cosmoshub-4": COSMOS_HUB_ADDRESS,
    "osmosis-1": OSMOSIS_ADDRESS
  });

  expect(result.typeUrl).toBe("/ibc.applications.transfer.v1.MsgTransfer");
  expect(result.value.sourcePort).toBe("transfer");
  expect(result.value.sourceChannel).toBe("channel-141");
  // ... more specific assertions ...

  // For the complex nested structure, consider using snapshot testing
  expect(result).toMatchSnapshot();
});

This approach will make the test more readable, maintainable, and easier to debug.

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: 4

🧹 Outside diff range and nitpick comments (1)
packages/universal-swap/tests/msg/msgs.spec.ts (1)

1-597: Overall assessment: Comprehensive tests with room for structural improvements

This test file provides thorough coverage of the generateMsgSwap function across various swap scenarios. The tests are well-thought-out and cover important use cases. However, there are several opportunities for improvement:

  1. Reduce code duplication by extracting common setup logic and using parameterized tests.
  2. Improve readability by breaking down large test cases into smaller, more focused ones.
  3. Enhance maintainability by using helper functions or fixtures for complex data structures.
  4. Increase consistency in error case testing across all scenarios.
  5. Consider using snapshot testing for large expected objects to simplify assertions.

Implementing these suggestions would result in a more robust, maintainable, and developer-friendly test suite. The core strength of comprehensive coverage would be retained while significantly improving the overall structure and readability of the tests.

🧰 Tools
🪛 Gitleaks

230-230: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


368-368: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


377-377: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


389-389: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


391-391: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


398-398: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


400-400: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


406-406: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


471-471: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


480-480: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


492-492: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


494-494: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


501-501: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


517-517: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


522-522: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


529-529: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


531-531: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between e09ac6e and 1e23583.

📒 Files selected for processing (1)
  • packages/universal-swap/tests/msg/msgs.spec.ts (1 hunks)
🧰 Additional context used
🪛 Gitleaks
packages/universal-swap/tests/msg/msgs.spec.ts

230-230: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


368-368: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


377-377: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


389-389: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


391-391: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


398-398: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


400-400: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


406-406: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


471-471: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


480-480: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


492-492: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


494-494: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


501-501: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


517-517: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


522-522: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


529-529: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


531-531: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (1)
packages/universal-swap/tests/msg/msgs.spec.ts (1)

1-7: LGTM: File structure and imports are well-organized.

The test file is properly structured with appropriate imports from Vitest and other necessary modules. The use of a describe block to group related tests is a good practice.

Comment on lines +8 to +155
{
poolId: "2173",
tokenOut: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D"
}
]
},
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D",
tokenInAmount: "7340955",
tokenOut: "orai",
tokenOutAmount: "7340955",
tokenOutChainId: "Oraichain",
bridgeInfo: {
port: "transfer",
channel: "channel-216"
}
}
]
}
]
};

// case 1: missing receiver address
try {
let res = generateMsgSwap(route, 0.1, {});
} catch (err) {
expect(err).toEqual(generateError(`Missing receiver when build msg in osmosis-1`));
}

// case 2: missing current chain address

try {
let res = generateMsgSwap(route, 0.1, { Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" });
} catch (err) {
expect(err).toEqual(generateError(`Missing address of osmosis-1`));
}

let res = generateMsgSwap(route, 0.1, {
Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
"cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
"osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
});
expect(res).toEqual({
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: "transfer",
sourceChannel: "channel-141",
receiver: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7",
token: { amount: "10000000", denom: "uatom" },
sender: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
memo: JSON.stringify({
wasm: {
contract: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7",
msg: {
swap_and_action: {
user_swap: {
swap_exact_asset_in: {
swap_venue_name: "osmosis-poolmanager",
operations: [
{
denom_in: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
denom_out: "uosmo",
pool: "1135"
},
{
denom_in: "uosmo",
denom_out: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D",
pool: "2173"
}
]
}
},
min_asset: {
native: {
amount: "6606859",
denom: "ibc/161D7D62BAB3B9C39003334F1671208F43C06B643CC9EDBBE82B64793C857F1D"
}
},
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
post_swap_action: {
ibc_transfer: {
ibc_info: {
source_channel: "channel-216",
receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
memo: "",
recover_address: "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
}
}
},
affiliates: []
}
}
}
}),
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve test structure and readability

While this test case is comprehensive, consider the following improvements:

  1. Move the large route object to a separate fixture or helper function to reduce clutter in the test body.
  2. Separate error cases and the successful case into different it blocks for better isolation and clarity.
  3. Consider using snapshot testing for the large expected object in the successful case to simplify assertions.

Example refactoring:

import { getTestRoute } from './testFixtures';

describe("build universal swap msg from cosmos-base ecosystem", () => {
  const route = getTestRoute();

  it("should throw an error when receiver address is missing", () => {
    expect(() => generateMsgSwap(route, 0.1, {})).toThrow(
      "Missing receiver when build msg in osmosis-1"
    );
  });

  it("should throw an error when current chain address is missing", () => {
    expect(() => 
      generateMsgSwap(route, 0.1, { Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" })
    ).toThrow("Missing address of osmosis-1");
  });

  it("should generate correct swap message for a valid input", () => {
    const result = generateMsgSwap(route, 0.1, {
      Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
      "cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
      "osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
    });

    expect(result).toMatchSnapshot();
  });
});

This structure improves readability and makes it easier to maintain and debug individual test cases.

Comment on lines 157 to 357
channel: "channel-29"
}
}
]
}
]
};

let res = generateMsgSwap(route, 0.1, {
"0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797",
"oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
"cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
"osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
});

expect(res).toEqual({
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: "transfer",
sourceChannel: "channel-141",
receiver: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7",
token: { amount: "1000000", denom: "uatom" },
sender: "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
memo: JSON.stringify({
wasm: {
contract: "osmo1h3jkejkcpthl45xrrm5geed3eq75p5rgfce9taufkwfr89k63muqweu2y7",
msg: {
swap_and_action: {
user_swap: {
swap_exact_asset_in: {
swap_venue_name: "osmosis-poolmanager",
operations: [
{
denom_in: "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
denom_out: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4",
pool: "1282"
},
{
denom_in: "ibc/498A0751C798A0D9A389AA3691123DADA57DAA4FE165D5C75894505B876BA6E4",
denom_out: "uosmo",
pool: "1464"
}
]
}
},
min_asset: { native: { amount: "7564864", denom: "uosmo" } },
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
post_swap_action: {
ibc_transfer: {
ibc_info: {
source_channel: "channel-216",
receiver: "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn",
memo: JSON.stringify({
wasm: {
contract: "orai13mgxn93pjvd7eermj4ghet8assxdqttxugwk25rasuuqq2g5nczq43eesn",
msg: {
swap_and_action: {
user_swap: {
swap_exact_asset_in: {
swap_venue_name: "oraidex",
operations: [
{
denom_in: "ibc/9C4DCD21B48231D0BC2AC3D1B74A864746B37E4292694C93C617324250D002FC",
denom_out: "orai",
pool: "orai1d37artrk4tkhz2qyjmaulc2jzjkx7206tmpfug"
}
]
}
},
min_asset: { native: { amount: "661473", denom: "orai" } },
timeout_timestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
post_swap_action: {
ibc_wasm_transfer: {
ibc_wasm_info: {
local_channel_id: "channel-29",
remote_address: "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
remote_denom: "oraib0xA325Ad6D9c92B55A3Fc5aD7e412B1518F96441C0",
memo: "oraib0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797"
}
}
},
affiliates: []
}
}
}
}),
recover_address: "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
}
}
},
affiliates: []
}
}
}
}),
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance test coverage and reduce duplication

This test case has similar structure issues to the previous one. Consider the following improvements:

  1. Extract the route object creation to a shared helper function that can be parameterized for different scenarios.
  2. Add error case testing to ensure robust coverage.
  3. Use snapshot testing for the large expected object to simplify the test.

Example refactoring:

import { createTestRoute } from './testHelpers';

describe("build universal swap msg from cosmos to evm", () => {
  const route = createTestRoute('cosmos', 'evm');

  it("should generate correct swap message for a valid input", () => {
    const result = generateMsgSwap(route, 0.1, {
      "0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797",
      "oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
      Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
      "cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
      "osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
    });

    expect(result).toMatchSnapshot();
  });

  it("should throw an error when required addresses are missing", () => {
    expect(() => generateMsgSwap(route, 0.1, {})).toThrow();
  });
});

This approach reduces duplication, improves maintainability, and ensures better test coverage.

🧰 Tools
🪛 Gitleaks

230-230: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +359 to +460
swapAmount: "1000000000",
returnAmount: "996112805",
paths: [
{
chainId: "noble-1",
tokenIn: "uusdc",
tokenInAmount: "1000000000",
tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
tokenOutAmount: "997121725",
tokenOutChainId: "Oraichain",
actions: [
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "uusdc",
tokenInAmount: "1000000000",
tokenOut: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
tokenOutAmount: "997121725",
tokenOutChainId: "Oraichain",
bridgeInfo: {
port: "transfer",
channel: "channel-34"
}
}
]
},
{
chainId: "Oraichain",
tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
tokenInAmount: "997121725",
tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenOutAmount: "996112805",
tokenOutChainId: "Oraichain",
actions: [
{
type: "Swap",
protocol: "OraidexV3",
tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
tokenInAmount: "997121725",
tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenOutAmount: "996112805",
swapInfo: [
{
poolId:
"orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd-500000000-10",
tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh"
}
]
}
]
}
]
};

let res = generateMsgSwap(route, 0.1, {
"0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797",
"oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
"cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
"osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t",
"noble-1": "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh"
});

expect(res).toEqual({
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: "transfer",
sourceChannel: "channel-34",
receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
token: { amount: "1000000000", denom: "uusdc" },
sender: "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh",
memo: Buffer.from(
Memo.encode({
userSwap: {
swapVenueName: "oraidex",
swapExactAssetIn: {
operations: [
{
poolId:
"orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd-500000000-10",
denomIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
denomOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh"
}
]
}
},
minimumReceive: "896501524",
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
postSwapAction: {
transferMsg: {
toAddress: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"
}
},
recoveryAddr: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"
}).finish()
).toString("base64"),
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve test specificity and structure

This test case continues the pattern of the previous ones. Consider these improvements:

  1. Make the test description more specific, e.g., "should correctly generate swap message from Noble to Oraichain".
  2. Extract the route object to a helper function or fixture.
  3. Add error case testing.
  4. Use snapshot testing for the large expected object.

Example refactoring:

import { createTestRoute } from './testHelpers';

describe("build universal swap msg from Noble", () => {
  const route = createTestRoute('noble', 'oraichain');

  it("should correctly generate swap message from Noble to Oraichain", () => {
    const result = generateMsgSwap(route, 0.1, {
      "0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797",
      "oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
      Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
      "cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
      "osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t",
      "noble-1": "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh"
    });

    expect(result).toMatchSnapshot();
  });

  it("should throw an error when Noble address is missing", () => {
    const addresses = {
      // ... other addresses
    };
    delete addresses['noble-1'];
    expect(() => generateMsgSwap(route, 0.1, addresses)).toThrow();
  });
});

This structure improves test specificity, reduces duplication, and ensures better error case coverage.

🧰 Tools
🪛 Gitleaks

368-368: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


377-377: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


389-389: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


391-391: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


398-398: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


400-400: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


406-406: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +462 to +597
{
type: "Swap",
protocol: "Oraidex",
tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
tokenInAmount: "997121083",
tokenOut: "orai",
tokenOutAmount: "141853616",
swapInfo: [
{
poolId: "orai19ttg0j7w5kr83js32tmwnwxxdq9rkmw4m3d7mn2j2hkpugwwa4tszwsnkg",
tokenOut: "orai"
}
]
},
{
type: "Swap",
protocol: "OraidexV3",
tokenIn: "orai",
tokenInAmount: "141853616",
tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenOutAmount: "888072940",
swapInfo: [
{
poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100",
tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh"
}
]
},
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "888072940",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "886558424",
tokenOutChainId: "0x38",
bridgeInfo: {
port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
channel: "channel-29"
}
}
]
}
]
};

let res = generateMsgSwap(route, 0.1, {
"0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797",
"oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
"cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
"osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t",
"noble-1": "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh"
});

expect(res).toEqual({
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: "transfer",
sourceChannel: "channel-34",
receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
token: { amount: "1000000000", denom: "uusdc" },
sender: "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh",
memo: Buffer.from(
Memo.encode({
userSwap: {
swapVenueName: "oraidex",
swapExactAssetIn: {
operations: [
{
poolId: "orai19ttg0j7w5kr83js32tmwnwxxdq9rkmw4m3d7mn2j2hkpugwwa4tszwsnkg",
denomIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
denomOut: "orai"
},
{
poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100",
denomIn: "orai",
denomOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh"
}
]
}
},
minimumReceive: "799265646",
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
postSwapAction: {
ibcWasmTransferMsg: {
localChannelId: "channel-29",
remoteAddress: "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
remoteDenom: "oraib0x55d398326f99059fF775485246999027B3197955",
memo: "oraib0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797"
}
},
recoveryAddr: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"
}).finish()
).toString("base64"),
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
});
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor test suite for improved structure and coverage

This test case, like the others, would benefit from the previously suggested improvements. Additionally, consider these overall enhancements for the entire test suite:

  1. Create a shared setup function for common elements across tests.
  2. Use parameterized tests to cover multiple scenarios with less code duplication.
  3. Add a summary comment at the beginning of the file outlining the scenarios covered.

Example refactoring:

import { createTestRoute, createAddresses } from './testHelpers';

/**
 * Test suite for generateMsgSwap function
 * Covers the following scenarios:
 * - Swap from cosmos-base ecosystem
 * - Swap from cosmos to EVM
 * - Swap from Noble
 * - Swap from Noble to EVM
 */
describe("generateMsgSwap", () => {
  const testCases = [
    { from: 'cosmos-base', to: 'osmosis', description: 'from cosmos-base ecosystem' },
    { from: 'cosmos', to: 'evm', description: 'from cosmos to EVM' },
    { from: 'noble', to: 'oraichain', description: 'from Noble' },
    { from: 'noble', to: 'evm', description: 'from Noble to EVM' },
  ];

  testCases.forEach(({ from, to, description }) => {
    describe(`Swap ${description}`, () => {
      const route = createTestRoute(from, to);
      const addresses = createAddresses(from, to);

      it("should generate correct swap message for valid input", () => {
        const result = generateMsgSwap(route, 0.1, addresses);
        expect(result).toMatchSnapshot();
      });

      it("should throw an error when required address is missing", () => {
        const incompleteAddresses = { ...addresses };
        delete incompleteAddresses[Object.keys(incompleteAddresses)[0]];
        expect(() => generateMsgSwap(route, 0.1, incompleteAddresses)).toThrow();
      });
    });
  });
});

This refactoring significantly reduces code duplication, improves maintainability, and ensures consistent coverage across different swap scenarios. It also provides a clear overview of the test cases at the beginning of the file.

🧰 Tools
🪛 Gitleaks

471-471: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


480-480: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


492-492: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


494-494: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


501-501: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


517-517: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


522-522: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


529-529: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


531-531: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

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: 6

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between db5622b and 34ce351.

📒 Files selected for processing (3)
  • packages/universal-swap/src/msg/chains/oraichain.ts (1 hunks)
  • packages/universal-swap/src/msg/msgs.ts (1 hunks)
  • packages/universal-swap/tests/msg/msgs.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/universal-swap/src/msg/chains/oraichain.ts
🧰 Additional context used
🪛 Gitleaks
packages/universal-swap/tests/msg/msgs.spec.ts

230-230: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


368-368: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


377-377: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


389-389: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


391-391: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


398-398: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


400-400: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


406-406: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


471-471: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


480-480: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


492-492: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


494-494: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


501-501: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


517-517: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


522-522: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


529-529: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


531-531: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

🔇 Additional comments (4)
packages/universal-swap/tests/msg/msgs.spec.ts (4)

8-155: 🛠️ Refactor suggestion

Refactor test structure for improved readability and maintainability

The existing comments about refactoring the test structure are valid and should be implemented. Here are some specific suggestions:

  1. Extract the route object to a separate file or create a factory function for it.
  2. Refactor error cases to use expect().toThrow() instead of try-catch blocks.
  3. Break down the large expect statement in the successful case into smaller, more specific checks.

Example implementation for error cases:

it("should throw an error when receiver address is missing", () => {
  expect(() => generateMsgSwap(route, 0.1, {})).toThrow(
    "Missing receiver when build msg in osmosis-1"
  );
});

it("should throw an error when current chain address is missing", () => {
  expect(() => 
    generateMsgSwap(route, 0.1, { Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2" })
  ).toThrow("Missing address of osmosis-1");
});

For the successful case, consider using snapshot testing for the large expected object and add more specific assertions for critical fields:

it("should generate correct swap message for a valid input", () => {
  const result = generateMsgSwap(route, 0.1, {
    Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
    "cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
    "osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t"
  });

  expect(result.typeUrl).toBe("/ibc.applications.transfer.v1.MsgTransfer");
  expect(result.value.sourcePort).toBe("transfer");
  expect(result.value.sourceChannel).toBe("channel-141");
  // Add more specific assertions for critical fields

  // Use snapshot testing for the entire object
  expect(result).toMatchSnapshot();
});

These changes will improve the test's readability, maintainability, and make it easier to debug failures.


157-357: 🛠️ Refactor suggestion

Apply similar refactoring to improve test structure

The suggestions from the previous test case apply here as well. To avoid duplication, I won't repeat the entire refactoring suggestion. However, there are some unique aspects to consider for this test case:

  1. This test covers a swap from cosmos to EVM, which might require additional assertions or checks specific to EVM interactions.

  2. Consider adding a test for the EVM-specific parts of the swap message, such as:

expect(result.value.memo).toContain("0x38"); // Check for EVM chain ID
expect(result.value.memo).toContain("0xA325Ad6D9c92B55A3Fc5aD7e412B1518F96441C0"); // Check for EVM token address
  1. Ensure that the route object for this test case is also extracted or generated using a factory function, possibly with parameters to specify the source and destination chain types (e.g., cosmos and EVM).

By implementing these suggestions along with the refactoring proposed in the previous comment, you'll significantly improve the structure and readability of this test case.

🧰 Tools
🪛 Gitleaks

230-230: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


359-460: 🛠️ Refactor suggestion

Refactor test and address potential security concerns

  1. Apply the refactoring suggestions from previous test cases to improve the overall structure and readability of this test.

  2. For the Memo.encode usage, consider adding specific tests to ensure the encoded memo contains the expected data:

const decodedMemo = Memo.decode(Buffer.from(result.value.memo, 'base64'));
expect(decodedMemo.userSwap.swapVenueName).toBe("oraidex");
expect(decodedMemo.minimumReceive).toBe("896501524");
// Add more specific checks for the memo contents
  1. Regarding the static analysis warnings about potential API key exposure:

The warnings are likely false positives, as the flagged lines contain contract addresses or token identifiers, not API keys. However, it's crucial to ensure that these values are not sensitive or meant to be kept secret. If they are public contract addresses or token identifiers, you can safely ignore these warnings.

To mitigate any potential risks:

  • Double-check that none of these values are actually sensitive data or API keys.
  • Consider using constants or configuration files for these addresses to make it clear they are not sensitive data.
  • If any of these turn out to be sensitive, move them to environment variables or secure configuration management.

Example:

const NOBLE_USDC_ADDRESS = "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd";
// Use NOBLE_USDC_ADDRESS in your test instead of the raw string

By implementing these suggestions, you'll improve the test structure, enhance the verification of the Memo.encode functionality, and address potential security concerns.

🧰 Tools
🪛 Gitleaks

368-368: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


377-377: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


389-389: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


391-391: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


398-398: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


400-400: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


406-406: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


462-597: 🛠️ Refactor suggestion

Refactor test and enhance Noble-to-EVM swap verification

  1. Apply the refactoring suggestions from previous test cases to improve the overall structure and readability of this test.

  2. Add specific checks for the Noble-to-EVM swap scenario:

const decodedMemo = Memo.decode(Buffer.from(result.value.memo, 'base64'));
expect(decodedMemo.userSwap.swapVenueName).toBe("oraidex");
expect(decodedMemo.postSwapAction.ibcWasmTransferMsg.remoteDenom).toContain("0x55d398326f99059fF775485246999027B3197955");
expect(decodedMemo.postSwapAction.ibcWasmTransferMsg.memo).toContain("0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797");
// Add more specific checks for Noble and EVM related fields
  1. Address the static analysis warnings consistently with the previous case:
  • Confirm that the flagged values are indeed public addresses or identifiers and not sensitive data.
  • Use constants or configuration files for these values to improve clarity and maintainability.

Example:

const EVM_USDT_ADDRESS = "0x55d398326f99059fF775485246999027B3197955";
const EVM_RECEIVER_ADDRESS = "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797";
// Use these constants in your test setup and assertions
  1. Consider adding a test helper function to generate route objects for different scenarios:
function generateTestRoute(from: string, to: string, amount: string): RouteObject {
  // Logic to generate route based on 'from' and 'to' parameters
}

// Usage in test
const route = generateTestRoute("noble", "evm", "1000000000");

This approach will make it easier to create and maintain different test scenarios.

By implementing these suggestions, you'll improve the test structure, enhance the verification of Noble-to-EVM swaps, and consistently address potential security concerns across all test cases.

🧰 Tools
🪛 Gitleaks

471-471: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


480-480: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


492-492: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


494-494: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


501-501: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


517-517: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


522-522: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


529-529: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


531-531: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

Comment on lines +11 to +25
const getDestPrefixForBridgeToEvmOnOrai = (chainId: string): string => {
const prefixMap: { [key: string]: string } = {
"0x01": ORAI_BRIDGE_EVM_DENOM_PREFIX,
"0x38": ORAI_BRIDGE_EVM_DENOM_PREFIX,
"0x2b6653dc": ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX
};

const prefix = prefixMap[chainId];
if (prefix) {
return prefix;
} else if (chainId.startsWith("0x")) {
throw generateError(`Don't support bridge from Oraichain to ${chainId}`);
}
return "";
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling and return value in getDestPrefixForBridgeToEvmOnOrai

The function can be improved for better error handling and clarity:

  1. Instead of returning an empty string for non-EVM chains, consider throwing an error to prevent silent failures.
  2. The error message for unsupported EVM chains can be rephrased for clarity.

Here's a suggested improvement:

const getDestPrefixForBridgeToEvmOnOrai = (chainId: string): string => {
  const prefixMap: { [key: string]: string } = {
    "0x01": ORAI_BRIDGE_EVM_DENOM_PREFIX,
    "0x38": ORAI_BRIDGE_EVM_DENOM_PREFIX,
    "0x2b6653dc": ORAI_BRIDGE_EVM_TRON_DENOM_PREFIX
  };

  const prefix = prefixMap[chainId];
  if (prefix) {
    return prefix;
  } else if (chainId.startsWith("0x")) {
    throw generateError(`Bridging from Oraichain to ${chainId} is not supported`);
  }
  throw generateError(`Unexpected chain ID: ${chainId}`);
};

This change ensures that the function always returns a valid prefix or throws an error, improving its reliability.

Comment on lines +27 to +71
const buildMemoSwap = (
path: Path,
receiver: string,
memo: string,
addresses: { [chainId: string]: string },
slippage: number = 0.01,
previousChain?: string
): MiddlewareResponse => {
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["oraibridge-subnet-2"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
oraichainMsg.setMinimumReceiveForSwap(slippage);
// we have 2 cases:
// - Previous chain use IBC bridge to Oraichain
// - Previous chain use IBC Wasm bridge to Oraichain (EVM, noble)
let msgInfo =
previousChain && (previousChain == "noble-1" || previousChain.startsWith("0x"))
? oraichainMsg.genMemoForIbcWasm()
: oraichainMsg.genMemoAsMiddleware();
return msgInfo;
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
let msgInfo = cosmosMsg.genMemoAsMiddleware();
return msgInfo;
}
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor buildMemoSwap for improved maintainability and error handling

  1. Address the TODO comment about calculating the minimum receive amount.
  2. Add error handling for potentially undefined addresses[currentChain].
  3. Use constants for chain IDs to improve maintainability.
  4. Consider splitting the function into smaller, more focused functions.

Example improvements:

const CHAIN_ID_ORAICHAIN = "Oraichain";
const CHAIN_ID_OSMOSIS = "osmosis-1";

const buildMemoSwap = (
  path: Path,
  receiver: string,
  memo: string,
  addresses: { [chainId: string]: string },
  slippage: number = 0.01,
  previousChain?: string
): MiddlewareResponse => {
  let currentChain = path.chainId;
  let currentAddress = addresses[currentChain];
  if (!currentAddress) {
    throw generateError(`Address not found for chain ${currentChain}`);
  }

  // TODO: Implement minimum receive amount calculation
  // const minimumReceive = calculateMinimumReceive(path, slippage);

  switch (currentChain) {
    case CHAIN_ID_ORAICHAIN:
      return buildOraichainMemo(path, receiver, memo, currentAddress, addresses, slippage, previousChain);
    case CHAIN_ID_OSMOSIS:
      return buildOsmosisMemo(path, receiver, memo, currentAddress, slippage);
    default:
      return buildDefaultMemo(path, receiver, memo, currentAddress, slippage, currentChain);
  }
};

// Implement buildOraichainMemo, buildOsmosisMemo, and buildDefaultMemo as separate functions

This refactoring improves readability, maintainability, and error handling while addressing the main issues in the original function.

Comment on lines +73 to +107
const buildExecuteMsg = (
path: Path,
receiver: string,
memo: string,
addresses: { [chainId: string]: string },
slippage: number = 0.01
): EncodeObject => {
let currentChain = path.chainId;
let currentAddress = addresses[currentChain];
switch (currentChain) {
case "Oraichain": {
let prefix = getDestPrefixForBridgeToEvmOnOrai(path.tokenOutChainId);
let oBridgeAddress = addresses["oraibridge-subnet-2"];
let oraichainMsg = new OraichainMsg(path, "1", receiver, currentAddress, memo, prefix, oBridgeAddress);
oraichainMsg.setMinimumReceiveForSwap(slippage);
return oraichainMsg.genExecuteMsg();
}
case "osmosis-1": {
let cosmosMsg = new OsmosisMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
return cosmosMsg.genExecuteMsg();
}

default: {
// currently, we don't support universal swap on EVM
// default cosmos case
if (currentChain.startsWith("0x")) {
throw generateError("Don't support universal swap in EVM");
}
let cosmosMsg = new CosmosMsg(path, "1", receiver, currentAddress, memo);
cosmosMsg.setMinimumReceiveForSwap(slippage);
return cosmosMsg.genExecuteMsg();
}
}
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Refactor buildExecuteMsg to reduce duplication and improve maintainability

  1. Address the TODO comment about calculating the minimum receive amount.
  2. There's significant code duplication between buildMemoSwap and buildExecuteMsg. Consider extracting common logic into shared helper functions.
  3. Add error handling for potentially undefined addresses[currentChain].

Apply these changes to address the issues:

  1. Implement the minimum receive calculation or add a more specific TODO with acceptance criteria.
  2. Extract common logic into a shared helper function:
    function handleChainSpecificLogic(
      chainId: string,
      path: Path,
      receiver: string,
      currentAddress: string,
      memo: string,
      slippage: number,
      addresses: { [chainId: string]: string },
      isExecuteMsg: boolean
    ) {
      // Common logic here
    }
  3. Add error handling for undefined addresses:
    if (!currentAddress) {
      throw generateError(`Address not found for chain ${currentChain}`);
    }

Also, consider using the chain ID constants suggested in the previous comment to improve consistency and maintainability.

Comment on lines +109 to +128
export const generateMsgSwap = (
route: Route,
slippage: number = 0.01,
addresses: { [chainId: string]: string }
): EncodeObject => {
if (route.paths.length == 0) {
throw generateError("Require at least 1 action");
}
let memo: string = "";
let receiver = addresses[route.paths.at(-1)?.tokenOutChainId];

// generate memo for univeral swap
for (let i = route.paths.length - 1; i > 0; i--) {
let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses, slippage, route.paths[i - 1].chainId);
memo = swapInfo.memo;
receiver = swapInfo.receiver;
}

return buildExecuteMsg(route.paths[0], receiver, memo, addresses, slippage);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve generateMsgSwap for better compatibility and error handling

  1. Replace Array.prototype.at(-1) with a more compatible approach:

    let lastPath = route.paths[route.paths.length - 1];
    let receiver = addresses[lastPath?.tokenOutChainId];
  2. Add error handling for undefined addresses:

    if (!receiver) {
      throw generateError(`Address not found for the last path's tokenOutChainId`);
    }
  3. Consider adding input validation for the route parameter:

    if (!route.paths.length) {
      throw generateError("Route must contain at least one path");
    }

These changes will make the function more robust, compatible across different JavaScript environments, and less prone to runtime errors.

Comment on lines +130 to +153
export const generateMemoSwap = (
route: Route,
slippage: number = 0.01,
addresses: { [chainId: string]: string },
previousChain?: string
): MiddlewareResponse => {
if (route.paths.length == 0) {
return {
memo: "",
receiver: ""
};
}

let memo: string = "";
let receiver = addresses[route.paths.at(-1)?.tokenOutChainId];

// generate memo for univeral swap
for (let i = route.paths.length - 1; i > 0; i--) {
let swapInfo = buildMemoSwap(route.paths[i], receiver, memo, addresses, slippage, route.paths[i - 1].chainId);
memo = swapInfo.memo;
receiver = swapInfo.receiver;
}
return buildMemoSwap(route.paths[0], receiver, memo, addresses, slippage, previousChain);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve generateMemoSwap for better compatibility, error handling, and input validation

  1. Replace Array.prototype.at(-1) with a more compatible approach:

    let lastPath = route.paths[route.paths.length - 1];
    let receiver = addresses[lastPath?.tokenOutChainId];
  2. Add error handling for undefined addresses:

    if (!receiver) {
      throw generateError(`Address not found for the last path's tokenOutChainId`);
    }
  3. Improve input validation:

    if (!route.paths.length) {
      return {
        memo: "",
        receiver: ""
      };
    }
    
    const lastPath = route.paths[route.paths.length - 1];
    if (!lastPath || !lastPath.tokenOutChainId) {
      throw generateError("Invalid route: Last path or tokenOutChainId is missing");
    }

These changes will make the function more robust, compatible across different JavaScript environments, and less prone to runtime errors.

Comment on lines +1 to +597
{
type: "Swap",
protocol: "Oraidex",
tokenIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
tokenInAmount: "997121083",
tokenOut: "orai",
tokenOutAmount: "141853616",
swapInfo: [
{
poolId: "orai19ttg0j7w5kr83js32tmwnwxxdq9rkmw4m3d7mn2j2hkpugwwa4tszwsnkg",
tokenOut: "orai"
}
]
},
{
type: "Swap",
protocol: "OraidexV3",
tokenIn: "orai",
tokenInAmount: "141853616",
tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenOutAmount: "888072940",
swapInfo: [
{
poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100",
tokenOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh"
}
]
},
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh",
tokenInAmount: "888072940",
tokenOut: "0x55d398326f99059fF775485246999027B3197955",
tokenOutAmount: "886558424",
tokenOutChainId: "0x38",
bridgeInfo: {
port: "wasm.orai195269awwnt5m6c843q6w7hp8rt0k7syfu9de4h0wz384slshuzps8y7ccm",
channel: "channel-29"
}
}
]
}
]
};

let res = generateMsgSwap(route, 0.1, {
"0x38": "0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797",
"oraibridge-subnet-2": "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
Oraichain: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
"cosmoshub-4": "cosmos1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y0ppr3e",
"osmosis-1": "osmo1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y86jn8t",
"noble-1": "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh"
});

expect(res).toEqual({
typeUrl: "/ibc.applications.transfer.v1.MsgTransfer",
value: {
sourcePort: "transfer",
sourceChannel: "channel-34",
receiver: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2",
token: { amount: "1000000000", denom: "uusdc" },
sender: "noble1hvr9d72r5um9lvt0rpkd4r75vrsqtw6y8z5tfh",
memo: Buffer.from(
Memo.encode({
userSwap: {
swapVenueName: "oraidex",
swapExactAssetIn: {
operations: [
{
poolId: "orai19ttg0j7w5kr83js32tmwnwxxdq9rkmw4m3d7mn2j2hkpugwwa4tszwsnkg",
denomIn: "orai15un8msx3n5zf9ahlxmfeqd2kwa5wm0nrpxer304m9nd5q6qq0g6sku5pdd",
denomOut: "orai"
},
{
poolId: "orai-orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh-3000000000-100",
denomIn: "orai",
denomOut: "orai12hzjxfh77wl572gdzct2fxv2arxcwh6gykc7qh"
}
]
}
},
minimumReceive: "799265646",
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT),
postSwapAction: {
ibcWasmTransferMsg: {
localChannelId: "channel-29",
remoteAddress: "oraib1hvr9d72r5um9lvt0rpkd4r75vrsqtw6ytnnvpf",
remoteDenom: "oraib0x55d398326f99059fF775485246999027B3197955",
memo: "oraib0x8c7E0A841269a01c0Ab389Ce8Fb3Cf150A94E797"
}
},
recoveryAddr: "orai1hvr9d72r5um9lvt0rpkd4r75vrsqtw6yujhqs2"
}).finish()
).toString("base64"),
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
}
});
});
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Overall test suite improvements and next steps

This test suite provides comprehensive coverage for various swap scenarios in the generateMsgSwap function. However, there are several areas where the tests can be improved for better readability, maintainability, and security:

  1. Test structure refactoring: Extract complex route objects to separate files or create factory functions. This will significantly reduce the complexity within each test case.

  2. Error case testing: Refactor error cases to use expect().toThrow() instead of try-catch blocks for cleaner and more idiomatic test code.

  3. Assertion improvements: Break down large expect statements into smaller, more specific checks. Consider using snapshot testing for complex objects.

  4. Security considerations: Address the static analysis warnings by confirming that flagged values are not sensitive data. Use constants or configuration files for addresses and identifiers to improve clarity.

  5. Consistency: Apply the suggested improvements consistently across all test cases.

  6. Helper functions: Implement helper functions for common operations like route generation and memo decoding to reduce duplication and improve maintainability.

Next steps:

  1. Implement the suggested refactorings for one test case.
  2. Review the changes and adjust as necessary.
  3. Apply similar refactoring to the remaining test cases.
  4. Add any missing test scenarios or edge cases.
  5. Run the refactored tests and ensure all pass.
  6. Consider adding integration tests if not already present, to complement these unit tests.

By implementing these improvements, you'll create a more robust, readable, and maintainable test suite for the generateMsgSwap function.

🧰 Tools
🪛 Gitleaks

230-230: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


253-253: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


368-368: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


377-377: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


389-389: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


391-391: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


398-398: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


400-400: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


406-406: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


471-471: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


480-480: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


492-492: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


494-494: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


501-501: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


517-517: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


522-522: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


529-529: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)


531-531: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.

(generic-api-key)

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

🧹 Outside diff range and nitpick comments (4)
packages/universal-swap/src/helper.ts (4)

314-321: LGTM! Consider adding a brief comment explaining the purpose.

The new getAddress method effectively converts between different address formats. It's a useful addition for handling cross-chain operations.

Consider adding a brief comment explaining the purpose of this method and when it should be used. This will help other developers understand its role in the codebase.


Line range hint 323-415: LGTM! Consider adding error handling for edge cases.

The changes to addOraiBridgeRoute improve its flexibility and add support for alpha IBC WASM routes. The new structure with the addresses object enhances readability.

Consider adding error handling for cases where required properties in the addresses object might be missing, especially for the alpha IBC WASM case. This will make the function more robust against potential runtime errors.


Line range hint 786-826: Consider clarifying the condition for IBC WASM usage.

The addition of useAlphaIbcWasm provides more flexibility in handling different swap scenarios. However, the condition for determining when to use the original token info might be confusing.

The condition !query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm could lead to unexpected behavior. Consider refactoring this to make the logic more explicit:

-    if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) {
+    if (query?.routerOption?.useAlphaIbcWasm || (query?.routerOption?.useIbcWasm === false)) {
      fromInfo = query.originalFromInfo;
      toInfo = query.originalToInfo;
    }

This change makes it clear that we use the original token info when alpha IBC WASM is used or when IBC WASM is explicitly disabled.


636-637: LGTM! Consider allowing customization of default values.

The changes to the default values for dontAllowSwapAfter and maxSplits in generateSmartRouteForSwap provide more specific configuration for the Oraidex protocol.

Consider extracting these default values into constants at the top of the file. This would make it easier to adjust these values in the future if needed:

const DEFAULT_DONT_ALLOW_SWAP_AFTER = ["Oraidex", "OraidexV3"];
const DEFAULT_MAX_SPLITS = 10;

// Then in the method:
dontAllowSwapAfter: routerConfig.dontAllowSwapAfter ?? DEFAULT_DONT_ALLOW_SWAP_AFTER,
maxSplits: routerConfig.maxSplits ?? DEFAULT_MAX_SPLITS

This approach increases flexibility and maintainability of the code.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 34ce351 and 00ab354.

📒 Files selected for processing (1)
  • packages/universal-swap/src/helper.ts (8 hunks)
🧰 Additional context used

Comment on lines +610 to +613
maxSplits: routerConfig.maxSplits,
swapConfig: {
dontAlowSwapAfter: routerConfig.dontAllowSwapAfter
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in property name 'dontAlowSwapAfter'.

The addition of maxSplits and dontAlowSwapAfter properties provides more control over the swap configuration. However, there's a typo in one of the property names.

Correct the typo in the property name:

-          dontAlowSwapAfter: routerConfig.dontAllowSwapAfter
+          dontAllowSwapAfter: routerConfig.dontAllowSwapAfter

This will ensure consistency with the property name used in routerConfig.

📝 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
maxSplits: routerConfig.maxSplits,
swapConfig: {
dontAlowSwapAfter: routerConfig.dontAllowSwapAfter
}
maxSplits: routerConfig.maxSplits,
swapConfig: {
dontAllowSwapAfter: routerConfig.dontAllowSwapAfter
}

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: 4

🧹 Outside diff range and nitpick comments (8)
packages/universal-swap/src/helper.ts (7)

314-340: New address handling methods added

The new static methods getAddress and generateAddress are valuable additions for handling different address formats across Cosmos-based chains. They enhance the class's versatility in managing cross-chain operations.

A minor suggestion for improvement:

Consider adding JSDoc comments to these methods to provide clear documentation on their purpose, parameters, and return values. This would enhance the code's readability and maintainability.

For example:

/**
 * Converts an address to a specific Bech32 format.
 * @param {string} prefix - The Bech32 prefix for the target chain.
 * @param {{ address60: string, address118: string }} addresses - Object containing addresses in different formats.
 * @param {number} [coinType=118] - The coin type to use (default: 118).
 * @returns {string} The converted Bech32 address.
 */
static getAddress = (prefix: string, { address60, address118 }, coinType: number = 118) => {
  // ... existing implementation ...
};

Line range hint 342-389: Enhanced addOraiBridgeRoute method with improved flexibility

The updates to the addOraiBridgeRoute method significantly improve its flexibility and capability to handle various routing scenarios. The use of object destructuring for addresses enhances code readability.

Suggestions for further improvement:

  1. Consider adding type definitions for the addresses object and the swapOption object to improve type safety and code clarity.
  2. The alphaSmartRoute parameter is used without a null check. Consider adding a null check or making it a required parameter if it's always expected to be present.

Example type definitions:

type AddressesInput = {
  obridgeAddress?: string;
  sourceReceiver: string;
  injAddress?: string;
  destReceiver?: string;
};

type SwapOption = {
  isSourceReceiverTest?: boolean;
  useIbcWasm?: boolean;
  ibcInfoTestMode?: boolean;
  isAlphaIbcWasm?: boolean;
};

static addOraiBridgeRoute = async (
  addresses: AddressesInput,
  fromToken: TokenItemType,
  toToken: TokenItemType,
  minimumReceive: string,
  userSlippage: number,
  swapOption: SwapOption,
  alphaSmartRoute?: RouterResponse
): Promise<SwapRoute> => {
  // ... existing implementation ...
};

390-430: New alpha IBC WASM routing scenario added

The addition of the useAlphaIbcWasm case enhances the method's capability to handle more complex routing scenarios. The implementation correctly processes the routes and generates the appropriate memo using the generateMemoSwap function.

Suggestions for improvement:

  1. Consider extracting the logic for generating receiver addresses into a separate helper method to improve code readability and maintainability.
  2. Add comments explaining the purpose and expected structure of the alphaSmartRoute object to improve code clarity.

Example of a helper method:

private static generateReceiverAddresses(sourceReceiver: string, injAddress: string, obridgeAddress: string) {
  return {
    [COSMOS_CHAIN_ID_COMMON.ORAICHAIN_CHAIN_ID]: sourceReceiver,
    [COSMOS_CHAIN_ID_COMMON.COSMOSHUB_CHAIN_ID]: this.getAddress("cosmos", {
      address60: injAddress,
      address118: obridgeAddress
    }),
    // ... other chain IDs ...
  };
}

Then use it in the main method:

const receiverAddresses = this.generateReceiverAddresses(addresses.sourceReceiver, addresses.injAddress, addresses.obridgeAddress);

Line range hint 804-845: Enhanced handleSimulateSwap method with new routing options

The updates to the handleSimulateSwap method improve its flexibility by adding support for new routing scenarios, particularly the alpha IBC WASM case. The changes to routerConfigDefault ensure consistency with the new routing options.

Suggestions for improvement:

  1. Consider using TypeScript's optional chaining operator (?.) to simplify the null checks in the routerConfigDefault object initialization.
  2. Add comments explaining the purpose of the new useAlphaIbcWasm option and its implications on the simulation process.

Example of using optional chaining:

const routerConfigDefault = {
  url: query?.routerConfig?.url ?? "https://osor.oraidex.io",
  path: query?.routerConfig?.path ?? "/smart-router/alpha-router",
  protocols: query?.routerConfig?.protocols ?? ["Oraidex", "OraidexV3"],
  dontAllowSwapAfter: query?.routerConfig?.dontAllowSwapAfter ?? ["Oraidex", "OraidexV3"],
  maxSplits: query?.routerConfig?.maxSplits ?? 10
};

Line range hint 1265-1297: Updated generateMsgsSmartRouterV2withV3 to support V3 pools

The changes to generateMsgsSmartRouterV2withV3 method successfully incorporate support for V3 pool swaps while maintaining compatibility with V2 pools. The method now correctly differentiates between V2 and V3 swaps based on the pool ID structure.

Suggestions for improvement:

  1. Consider adding type annotations for the routes and offerInfo parameters to improve type safety and code clarity.
  2. Extract the logic for determining V2 vs V3 swap into a separate helper function to improve readability and maintainability.

Example of a helper function:

private static isV3Swap(poolId: string): boolean {
  const [tokenX, tokenY, fee, tickSpacing] = poolId.split("-");
  return !!(tokenX && tokenY && fee && tickSpacing);
}

// Usage in the main function:
if (this.isV3Swap(swap.poolId)) {
  // Handle V3 swap
} else {
  // Handle V2 swap
}

Line range hint 1302-1357: New flattenSmartRouters method for simplifying route structures

The addition of the flattenSmartRouters method is a valuable enhancement that simplifies complex nested route structures. This will likely improve the efficiency of subsequent processing steps in the swap operation pipeline.

Suggestions for improvement:

  1. Consider adding type annotations for the routers parameter and the return type to improve type safety and code clarity.
  2. The method is quite complex. Consider breaking it down into smaller, more focused helper methods to improve readability and maintainability.
  3. Add comments explaining the purpose and structure of the flattened routes to aid in understanding the transformation process.

Example of adding type annotations and a helper method:

interface FlattenedRoute {
  type: string;
  swapInfo: any[];
  tokenIn: string;
  tokenOut: string;
  tokenInAmount: string;
  tokenOutAmount: string;
  path: number;
  chainId: string;
  isLastPath: boolean;
}

static flattenSmartRouters(routers: Route[]): FlattenedRoute[] {
  let routesFlatten: FlattenedRoute[] = [];
  routers.forEach((routes, i) => {
    routes.paths.forEach((path, pathIndex) => {
      this.flattenPath(path, i, pathIndex, routes.paths.length, routesFlatten);
    });
  });
  return routesFlatten;
}

private static flattenPath(path: any, routeIndex: number, pathIndex: number, totalPaths: number, routesFlatten: FlattenedRoute[]): void {
  // ... logic for flattening a single path ...
}

Line range hint 1-1357: Comprehensive enhancements to UniversalSwapHelper class

The changes made to the UniversalSwapHelper class in packages/universal-swap/src/helper.ts significantly enhance its capabilities for handling complex swap operations across various blockchain networks. Key improvements include:

  1. Support for alpha IBC WASM routing
  2. Enhanced address handling for Cosmos-based chains
  3. Improved flexibility in the addOraiBridgeRoute method
  4. Support for V3 pools in swap message generation
  5. New method for flattening complex route structures

These changes collectively improve the class's ability to handle diverse swap scenarios and routing options, making it more versatile and powerful.

Recommendations for future improvements:

  1. Consider breaking down this large class into smaller, more focused classes or modules to improve maintainability and adherence to the Single Responsibility Principle.
  2. Implement comprehensive unit tests for the new methods and updated functionality to ensure reliability and ease of future modifications.
  3. Review and update the class's documentation to reflect the new capabilities and usage patterns introduced by these changes.

Overall, these changes represent a significant and positive evolution of the UniversalSwapHelper class, enhancing its utility in managing complex cross-chain swap operations.

packages/universal-swap/src/handler.ts (1)

Line range hint 1-1500: Overall improvements to UniversalSwapHandler class

The UniversalSwapHandler class has undergone several improvements:

  1. Standardization of chain IDs using COSMOS_CHAIN_IDS constants.
  2. Integration of alpha smart router functionality for potentially more efficient swaps.
  3. Enhanced flexibility in swap operations with new parameters like userSlippage and alphaSmartRoutes.
  4. Minor refactoring across multiple methods for consistency and preparedness for future features.

These changes generally improve the code quality and expand the capabilities of the swap handler. However, consider the following:

  1. Ensure comprehensive testing of all modified methods, especially with edge cases.
  2. Update documentation to reflect new parameters and functionality.
  3. Consider adding more inline comments to explain complex logic, particularly in the new alpha smart router related code.

Consider breaking down this large class into smaller, more focused classes or modules to improve maintainability. For example, you could separate the routing logic, message generation, and network-specific operations into distinct modules.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 00ab354 and d6bca87.

📒 Files selected for processing (3)
  • packages/universal-swap/src/handler.ts (10 hunks)
  • packages/universal-swap/src/helper.ts (8 hunks)
  • packages/universal-swap/src/universal-demos/alpha-ibc-new.ts (1 hunks)
🧰 Additional context used
🔇 Additional comments (6)
packages/universal-swap/src/universal-demos/alpha-ibc-new.ts (1)

89-94: Improve error handling

The current error handling uses console.trace, which may expose sensitive information in logs. This issue was flagged in a previous review and should be addressed.

As suggested in the previous review, consider implementing proper error handling:

-      console.trace("error: ", error);
+      console.error("An error occurred during the universal swap:", error.message);

Additionally, consider adding more structured error handling:

try {
  const result = await universalHandler.processUniversalSwap();
  console.log("Swap completed successfully:", result);
} catch (error) {
  if (error instanceof Error) {
    console.error("Swap failed:", error.message);
  } else {
    console.error("An unknown error occurred during the swap");
  }
}
packages/universal-swap/src/helper.ts (1)

50-51: New imports added for enhanced functionality

The addition of COSMOS_CHAIN_ID_COMMON, generateMemoSwap, and generateMsgSwap imports suggests new capabilities for handling Cosmos-based chain operations and memo generation. These additions align well with the class's purpose of managing universal swaps across different blockchain networks.

Also applies to: 86-87

packages/universal-swap/src/handler.ts (4)

63-64: New imports added

The addition of COSMOS_CHAIN_IDS from @oraichain/common and generateMsgSwap from ./msg/msgs suggests new functionality or refactoring. These imports enhance the standardization of chain IDs and potentially improve the message generation process for swaps.


1152-1175: New method alphaSmartRouterSwapNewMsg added

This new method introduces specialized handling for alpha smart router swaps:

  1. It supports multiple swap types: cosmos-to-others, oraichain-to-oraichain, and oraichain-to-cosmos.
  2. It uses the new generateMsgSwap function for efficient message generation.
  3. For non-supported swap types, it falls back to this.transferAndSwap.

This addition potentially improves the efficiency and flexibility of smart router swaps. However, ensure that:

  1. The generateMsgSwap function is properly implemented and tested.
  2. Error handling is adequate for unsupported swap types.
  3. The integration with existing swap logic is smooth and doesn't introduce conflicts.

To verify the correct implementation of generateMsgSwap, run:

#!/bin/bash
# Check the implementation of generateMsgSwap
rg -U 'function generateMsgSwap' --type typescript

1179-1180: Integration of alpha smart router in processUniversalSwap

The processUniversalSwap method has been updated to incorporate alpha smart router functionality:

  1. New parameters like alphaSmartRoutes and userSlippage are now used.
  2. A new condition checks for alphaSmartRoutes and isAlphaIbcWasm to determine the swap method.
  3. The new alphaSmartRouterSwapNewMsg method is called when conditions are met.

These changes allow for more flexible and potentially more efficient swap routing. However, consider the following:

  1. Ensure that the integration doesn't break existing swap flows.
  2. Verify that error handling is comprehensive, especially for edge cases in the new routing logic.
  3. Consider adding logging or monitoring to track the usage and performance of the new alpha smart router functionality.

To verify the correct handling of alpha smart routes, run:

#!/bin/bash
# Check the usage of alphaSmartRoutes in the codebase
rg -U 'alphaSmartRoutes' --type typescript

Also applies to: 1231-1238


1058-1067: Enhanced flexibility in swapCosmosToOtherNetwork method

The method has been updated to include more parameters and handle new scenarios:

  1. New parameters like alphaSmartRoutes and userSlippage have been added.
  2. Support for Injective chain has been added with the injAddress variable.
  3. The method now uses COSMOS_CHAIN_IDS constants for better standardization.

These changes improve the method's flexibility and potentially support more complex swap scenarios. However, ensure that all new parameters are properly validated and handled throughout the method.

To verify the correct usage of new parameters, run:

Also applies to: 1097-1103, 1201-1204, 1210-1214

Comment on lines +1 to +54
import "dotenv/config";
import { CosmosWalletImpl } from "./offline-wallet";
import { UniversalSwapHandler } from "../handler";
import { cosmosTokens, flattenTokens, generateError, getTokenOnOraichain, toAmount } from "@oraichain/oraidex-common";

const router = {
swapAmount: "100000",
returnAmount: "9983",
routes: [
{
swapAmount: "100000",
returnAmount: "9983",
paths: [
{
chainId: "osmosis-1",
tokenIn: "uosmo",
tokenInAmount: "100000",
tokenOut: "utia",
tokenOutAmount: "9983",
tokenOutChainId: "celestia",
actions: [
{
type: "Swap",
protocol: "Osmosis",
tokenIn: "uosmo",
tokenInAmount: "100000",
tokenOut: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877",
tokenOutAmount: "9983",
swapInfo: [
{
poolId: "1347",
tokenOut: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877"
}
]
},
{
type: "Bridge",
protocol: "Bridge",
tokenIn: "ibc/D79E7D83AB399BFFF93433E54FAA480C191248FC556924A2A8351AE2638B3877",
tokenInAmount: "9983",
tokenOut: "utia",
tokenOutAmount: "9983",
tokenOutChainId: "celestia",
bridgeInfo: {
port: "transfer",
channel: "channel-6994"
}
}
]
}
]
}
]
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider parameterizing the router object

The router object contains hard-coded values for swap amounts, return amounts, and other details. To improve flexibility and reusability, consider parameterizing this object or loading it from a configuration file. This would allow for easier updates and testing with different scenarios.

Here's a suggestion to parameterize the router object:

import routerConfig from './routerConfig.json';

const createRouter = (swapAmount: string, returnAmount: string) => ({
  swapAmount,
  returnAmount,
  routes: routerConfig.routes.map(route => ({
    ...route,
    swapAmount,
    returnAmount
  }))
});

const router = createRouter("100000", "9983");

This approach allows you to easily create router objects with different swap and return amounts while keeping the complex structure in a separate configuration file.

Comment on lines +56 to +61
const alphaSwapToOraichain = async () => {
const wallet = new CosmosWalletImpl(process.env.MNEMONIC);
const sender = await wallet.getKeplrAddr("osmosis-1");

const fromAmount = 0.1;
console.log("sender: ", sender);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Enhance error handling for wallet initialization

The wallet initialization using the mnemonic from environment variables is good practice. However, there's no error handling if the mnemonic is not set or invalid.

Consider adding a check for the mnemonic:

if (!process.env.MNEMONIC) {
  throw new Error("MNEMONIC environment variable is not set");
}
const wallet = new CosmosWalletImpl(process.env.MNEMONIC);

Comment on lines +62 to +66
const originalFromToken = flattenTokens.find((t) => t.coinGeckoId === "osmosis" && t.chainId === "osmosis-1");
const originalToToken = flattenTokens.find((t) => t.coinGeckoId === "celestia" && t.chainId === "celestia");

if (!originalToToken) throw generateError("Could not find original to token");
if (!originalFromToken) throw generateError("Could not find original from token");
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve token finding logic

The current token finding logic uses find which returns undefined if no match is found. The subsequent null checks are good, but we can make this more robust.

Consider using a more descriptive error message and potentially creating a utility function for finding tokens:

function findTokenOrThrow(predicate: (t: Token) => boolean, errorMessage: string): Token {
  const token = flattenTokens.find(predicate);
  if (!token) throw new Error(errorMessage);
  return token;
}

const originalFromToken = findTokenOrThrow(
  (t) => t.coinGeckoId === "osmosis" && t.chainId === "osmosis-1",
  "Could not find Osmosis token on Osmosis chain"
);

const originalToToken = findTokenOrThrow(
  (t) => t.coinGeckoId === "celestia" && t.chainId === "celestia",
  "Could not find Celestia token on Celestia chain"
);

This approach provides more specific error messages and ensures type safety.

Comment on lines +97 to +99
(() => {
alphaSwapToOraichain();
})();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance IIFE implementation

The current IIFE doesn't handle potential errors from alphaSwapToOraichain. Consider adding error handling and potentially making this function more flexible.

Here's a suggestion to improve the IIFE:

(async () => {
  try {
    await alphaSwapToOraichain();
    console.log("Swap process completed successfully");
  } catch (error) {
    console.error("Swap process failed:", error instanceof Error ? error.message : "Unknown error");
    process.exit(1);
  }
})();

This implementation adds error handling and ensures that the process exits with a non-zero status code if an error occurs, which is useful for scripting and CI/CD pipelines.

@trung2891 trung2891 closed this Oct 9, 2024
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