-
Notifications
You must be signed in to change notification settings - Fork 10
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
Conversation
WalkthroughThe pull request introduces several changes to the Changes
Suggested reviewers
Poem
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? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Code Coverage Summary
Diff against main
Results for commit: 34ce351 Minimum allowed coverage is ♻️ This comment has been updated with latest results |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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
tomemo
might lead to issues when thememo
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 messageThe 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 messageSimilar 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 messageConsistent 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 messageThere'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 timeoutThere's a
FIXME
comment indicating uncertainty about using nanoseconds with au64
type:timeout: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), // FIXME: should we use nano with an u64 type? -> probably quite big for a u64Would 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' methodThe 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 queryThere'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' methodThe 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
⛔ 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
andisCw20Token
(suggested to be renamed toisValidBech32Address
) 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:
- Enhancing documentation for clarity and completeness.
- Improving error handling for consistency and internationalization.
- 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 additionThe
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 interfaceExporting 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 interfaceThe AI-generated summary mentions an update to the
UniversalSwapData
interface, adding an optionalaffiliates
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 theUniversalSwapData
interfaceThe previous search did not locate the
UniversalSwapData
interface, possibly due to an unrecognized file type. To confirm the addition of theaffiliates
property, please run the following command:
Verified the addition of
affiliates
in theUniversalSwapData
interfaceThe
affiliates
property has been successfully added to theUniversalSwapData
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.tsLength 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 addressThe
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 comparisonFor 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 newActionType
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 unhandledActionType
usages:✅ Verification successful
All ActionType values are properly handled in
getSwapAndBridgeInfo
The
ActionType
enum includesBridge
,Swap
, andConvert
, all of which are explicitly handled in thegetSwapAndBridgeInfo
method. Additionally, a default case ensures that any futureActionType
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 10Length 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 20Length of output: 444
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; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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),
amount: bridgeInfo.fromToken, | ||
denom: this.path.tokenInAmount |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
}
]
// 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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => { | |
parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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 addressThe
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:
- Initialize the
ENTRY_POINT_CONTRACT
property or provide a method to set it.- Fix the typo in the error message for unsupported actions.
- Initialize the
bridgeInfo
variable to avoid potential undefined errors.- Update error messages to consistently reference Osmosis instead of Oraichain.
- Fix the incorrect array access using 'slice' in the
genMemoAsMiddleware
method.- Correct the JSON structure for CW20 token send messages.
- Remove unnecessary wrapping of 'msg' in JSON for native tokens.
- 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 commentThere'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 documentationThe
OraichainMsg
class effectively handles complex logic for swaps and bridges in the Oraichain ecosystem. However, there are some areas where it could be improved:
- Consider breaking down larger methods (like
genExecuteMsg
) into smaller, more focused helper methods to improve readability and maintainability.- Add more inline comments explaining complex logic, especially in methods like
getSwapAndBridgeInfo
andgenExecuteMsg
.- Consider creating utility functions for common operations (like handling IBC transfers) to reduce code duplication across methods.
- 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
📒 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 goodThe 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' parameterThe
memo
parameter is currently typed asstring
but assignedundefined
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?: stringThis change makes the
memo
parameter optional and allows it to be either a string or undefined.
29-54
: Improve robustness ofgetBridgeInfo
methodThere are a couple of potential issues in this method:
- The loop continues after finding a bridge action, which could lead to
bridgeInfo
being overwritten.- There's no check to ensure
bridgeInfo
is defined before returning it.To address these issues:
- Add a
break
statement after theswitch
block to exit the loop after finding the first bridge action:} } + break; // Exit the loop after finding the bridge action }
- 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 goodThe 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 undefinedbridgeInfo
ingenMemoAsMiddleware
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 undefinedbridgeInfo
ingenExecuteMsg
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 neededThe
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:
- Adjusting the type of the
memo
parameter in the constructor.- Improving the robustness of the
getBridgeInfo
method.- Handling potential undefined
bridgeInfo
ingenMemoAsMiddleware
andgenExecuteMsg
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 issueFix 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 issueSwap 'amount' and 'denom' in the funds array for native tokens
In the native token case, the
amount
anddenom
fields in thefunds
array are swapped. Theamount
should bethis.path.tokenInAmount
, and thedenom
should bebridgeInfo.fromToken
.funds: [ { - amount: bridgeInfo.fromToken, - denom: this.path.tokenInAmount + amount: this.path.tokenInAmount, + denom: bridgeInfo.fromToken } ]Likely invalid or redundant comment.
138-138
:⚠️ Potential issueFix incorrect array access using 'slice'
The code uses
slice
incorrectly to access the last element ofswapOps
. Theslice
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 initializationThe 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 logicThe 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 generationThe
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 transfersThe
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 generationThe
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 generationThe
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 issueFix 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 issueFix typo in method name
The method name
pasreConverterMsgToPoolId
contains a typo. It should beparseConverterMsgToPoolId
.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 issueFix 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.
SWAP_VENUE_NAME = "osmosis-poolmanager"; | ||
ENTRY_POINT_CONTRACT = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
throw generateError("Missing postAction for universalSwap on Oraichain"); | |
throw generateError("Missing postAction for universalSwap on Osmosis"); |
contract: this.ENTRY_POINT_CONTRACT, | ||
msg: toUtf8( | ||
JSON.stringify({ | ||
msg | ||
}) | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
contract: this.ENTRY_POINT_CONTRACT, | |
msg: toUtf8( | |
JSON.stringify({ | |
msg | |
}) | |
), | |
contract: this.ENTRY_POINT_CONTRACT, | |
msg: toUtf8( | |
JSON.stringify({ | |
...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: { | ||
send: { | ||
contract: this.ENTRY_POINT_CONTRACT, | ||
amount: this.path.tokenInAmount, | ||
msg: toBinary(msg) | ||
} | ||
} | ||
}) | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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) | |
} | |
}) | |
), |
amount: bridgeInfo.fromToken, | ||
denom: this.path.tokenInAmount |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
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 | ||
} | ||
] | ||
}) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
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 | ||
} | ||
] | ||
}) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling and consistency
While error handling is generally consistent using the generateError
function, some improvements could be made:
- 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.
- Consider adding more detailed error messages in complex logic sections to aid in debugging.
- 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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, | |
remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (2)
packages/universal-swap/src/msg/oraichain.ts (2)
25-40
: Consider improving error handling in the constructorThe constructor checks if the
chainId
is "Oraichain" and throws an error if it's not. However, it might be helpful to include the actualchainId
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 robustnessWhen accessing the last element of the
swapOps
array, it's better to use array indexing instead of theslice
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
📒 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 implementationThe
genMemoForIbcWasm
method is well-implemented. It correctly generates the memo message for swap through IBC WASM after bridge, using thegetSwapAndBridgeInfo
method to get swap operations and bridge info.
47-47
:⚠️ Potential issueFix 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 issueFix 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 issueFix incorrect assignment of 'amount' and 'denom' in funds
The
amount
anddenom
are assigned incorrectly in thefunds
array.amount
should bethis.path.tokenInAmount
anddenom
should bebridgeInfo.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 issueFix 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 issueFix 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.
}) | ||
}; | ||
} | ||
// 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 | ||
} | ||
] | ||
}) | ||
}; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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:
- Error handling: Implement a consistent error handling strategy across all methods. Consider creating custom error types for different scenarios.
- Code duplication: Some methods like
getProtoForPostAction
andgetPostAction
have similar structures. Consider extracting common logic into helper methods. - Method length: Some methods, particularly
genExecuteMsg
, are quite long. Consider breaking them down into smaller, more focused methods for improved readability and maintainability. - 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 16
🧹 Outside diff range and nitpick comments (4)
packages/universal-swap/src/msg/chains/cosmos.ts (1)
43-44
: Avoid unnecessary type assertionsIn the
fromChain
andtoChain
properties, you are assertingthis.path.chainId
andthis.path.tokenOutChainId
asNetworkChainId
. If these properties are already of typeNetworkChainId
, 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.tokenOutChainIdThis 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 operatorThe unary plus operator is used before
calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
. IfcalculateTimeoutTimestamp
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 theOraichainMsg
classGiven 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
📒 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 issueCorrect 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
: EnsuretokenInAmount
andtokenIn
are defined inPath
In the
genExecuteMsg()
method, you accessthis.path.tokenInAmount
andthis.path.tokenIn
. Ensure that these properties are defined in thePath
type and are properly initialized before use.Run the following script to confirm that
tokenInAmount
andtokenIn
are properties ofPath
: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
andtokenIn
are defined in thePath
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 nameEnsure 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 timestampVerify 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 -A20Length of output: 857
export interface MiddleWareResponse { | ||
memo: String; | ||
receiver: String; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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)
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 | ||
} | ||
}) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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}`); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
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}`); | |
} | |
} |
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; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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; | |
} |
throw generateError("Only support swap + bride on Osmosis"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
if (isBridgeToEvm) { | ||
if (!this.destPrefix || !this.obridgeAddress) | ||
throw generateError("Missing prefix os Obridge address for bridge to EVM"); | ||
prefix = this.destPrefix; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix typo in method name: pasreConverterMsgToPoolId
→ parseConverterMsgToPoolId
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.
pasreConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => { | |
parseConverterMsgToPoolId = (tokenIn: string, tokenOut: string) => { |
userSwap, | ||
minimumReceive: this.minimumReceive, | ||
timeoutTimestamp: +calculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT), | ||
postSwapAction: this.getProtoForPostAction(bridgeInfo), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Outside diff range and nitpick comments (4)
packages/universal-swap/src/msg/chains/cosmos.ts (1)
20-20
: Confirm Necessity of Default Value formemo
ParameterThe constructor sets a default value of
memo
to an empty string. If an emptymemo
is acceptable in all contexts, this is fine. Otherwise, consider makingmemo
a required parameter or handling cases where it might be unintentionally omitted.packages/universal-swap/src/msg/msgs.ts (3)
27-27
: Reminder: Address theFIXME
commentAt 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 theFIXME
commentAt 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 readabilityConsider 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
📒 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 thegetSwapAndBridgeInfo
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:
- Consider using a constant for the Osmosis chain ID for better maintainability.
- Correct the typos and incorrect chain names in error messages as pointed out in previous comments.
- 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 suggestionConsider 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.
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"); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider implementing internationalization and using TypeScript's Error class.
While the function logic is correct, there are still opportunities for improvement:
- The error messages are hardcoded strings, which makes future internationalization difficult.
- The function uses a custom
generateError
function instead of TypeScript's built-inError
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.
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}`); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Implement internationalization and use TypeScript's Error class for consistency.
The validateReceiver
function could benefit from the same improvements suggested for validatePath
:
- Replace hardcoded error messages with error keys to facilitate future internationalization.
- 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.
// 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; | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve function naming, documentation, and error handling.
The current implementation of isCw20Token
has several areas for improvement:
- The function name is misleading. It's actually checking if the address is a valid bech32 address, not specifically a CW20 token.
- The comment above the function is slightly inaccurate.
- The function doesn't distinguish between different types of decoding errors.
Consider the following improvements:
- Rename the function to
isValidBech32Address
for clarity. - Update the function's documentation to accurately describe its behavior.
- 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.
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}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
if (bridgeInfo.sourcePort != "transfer") { | ||
throw generateError("Error on generate executeMsg on Oraichain: Only support ibc transfer"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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"); | |
} |
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; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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:
// ...
// ...
}
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; | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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:
- Adding validation for
minimumReceive
in the constructor and setter method.- Implementing getter methods for other properties if they need to be accessed outside the class.
- 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 handlingThe
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 contractThe 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 improvementThe
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:
- Fix typos in method names and error messages.
- Address potential issues with operator precedence in string concatenations.
- Improve error handling and input validation in some methods.
- Consider refactoring longer methods (e.g.,
genExecuteMsg
) for better maintainability.- 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
📒 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 forpackages/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:
- Error handling: Add checks for undefined values, especially when accessing properties from the
addresses
object.- Code duplication: Refactor common logic in
buildMemoSwap
andbuildExecuteMsg
into shared helper functions.- Compatibility: Replace
Array.prototype.at(-1)
with a more widely supported method to access the last element of an array.- Constants: Use constants for chain IDs to improve maintainability and reduce the risk of typos.
- TODO comments: Address or provide more specific information for the TODO comments about calculating minimum receive amounts.
- 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 logicThe
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 suggestedThe
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:
- Proper handling of different scenarios (swap, bridge, IBC transfer).
- Comprehensive memo generation for middleware operations.
- 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 issueCorrect 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 issueCorrect 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 issueCorrect 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 issueCorrect 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 nestedsend
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 implementationThe constructor correctly extends the base class and validates the
path
parameter to ensure it corresponds to "Oraichain".
282-350
: LGTM:genMemoAsMiddleware
implementationThe
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 issueReplace placeholder
ENTRY_POINT_CONTRACT
with the mainnet addressThe
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 issueFix typo in method name:
pasreConverterMsgToPoolId
→parseConverterMsgToPoolId
The method name
pasreConverterMsgToPoolId
appears to be misspelled. Renaming it toparseConverterMsgToPoolId
enhances code readability and maintains consistency.
201-228
:⚠️ Potential issueVerify that
bridgeInfo
is defined before using ingetProtoForPostAction
In the method
genMemoForIbcWasm
,bridgeInfo
is passed togetProtoForPostAction
without ensuring it's defined. IfbridgeInfo
isundefined
, this could lead to runtime errors.Add a check to ensure
bridgeInfo
is notundefined
before using it:- postSwapAction: this.getProtoForPostAction(bridgeInfo), + postSwapAction: this.getProtoForPostAction(bridgeInfo || {}),Alternatively, handle the undefined case within
getProtoForPostAction
.
setMinimumReceive(minimumReceive: string) { | ||
this.minimumReceive = minimumReceive; | ||
} | ||
|
||
getMinimumReceive() { | ||
return this.minimumReceive; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
.
//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; | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve buildMemoSwap
function implementation
- The TODO comment about calculating the minimum receive amount should be addressed.
- Consider using constants for chain IDs to improve maintainability, as suggested in a previous review.
- The error message for unsupported EVM chains uses a contraction. Consider rephrasing it for clarity and professionalism.
- Add error handling for potentially undefined
addresses[currentChain]
.
Apply these changes to address the issues:
- Implement the minimum receive calculation or add a more specific TODO with acceptance criteria.
- Define constants for chain IDs:
const CHAIN_ID_ORAICHAIN = "Oraichain"; const CHAIN_ID_OSMOSIS = "osmosis-1";
- Rephrase the error message:
- throw generateError("Don't support universal swap in EVM"); + throw generateError("Universal swap is not supported on EVM chains");
- 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.
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 | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve generateMsgSwap
function for better compatibility and error handling
- 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. - Add error handling for potentially undefined
addresses[route.paths.at(-1)?.tokenOutChainId]
.
Apply these changes to address the issues:
-
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];
-
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.
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"); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
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"); | |
} | |
} |
if (!this.destPrefix || !this.obridgeAddress) | ||
throw generateError("Missing prefix os Obridge address for bridge to EVM"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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"); |
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]; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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]; | |
} |
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"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix operator precedence and typo in error message (similar to getProtoForPostAction
)
The getPostAction
method has similar issues to those found in getProtoForPostAction
:
- There's an operator precedence issue in the IBC wasm transfer case.
- The error message for missing prefix or OBridge address contains a typo.
Apply these diffs to fix the issues:
- Fix operator precedence:
- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
- 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.
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"); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Outside diff range and nitpick comments (1)
packages/universal-swap/src/msg/msgs.ts (1)
1-155
: Overall improvements for enhanced robustness and maintainabilityThis file implements crucial functionality for message generation in universal swap operations. While the overall structure is sound, consider the following improvements:
- Address TODO comments, particularly regarding the calculation of minimum receive amounts.
- Implement consistent error handling across all functions, especially for potentially undefined accesses.
- Reduce code duplication between
buildMemoSwap
andbuildExecuteMsg
by extracting common logic.- Use constants for chain IDs to improve maintainability.
- Consider breaking down larger functions into smaller, more focused functions.
- Add input validation for function parameters, especially in exported functions.
- Ensure consistency in error handling and structure between similar functions like
generateMsgSwap
andgenerateMemoSwap
.Implementing these suggestions will significantly enhance the robustness, maintainability, and consistency of the code.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 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 implementationThe
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 issueFix typo in method name:
pasreConverterMsgToPoolId
→parseConverterMsgToPoolId
The method name
pasreConverterMsgToPoolId
appears to be misspelled. Renaming it toparseConverterMsgToPoolId
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 issueUpdate 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 issueVerify that
bridgeInfo
is defined before using ingetProtoForPostAction
In the method
genMemoForIbcWasm
,bridgeInfo
is passed togetProtoForPostAction
without ensuring it's defined. IfbridgeInfo
isundefined
, this could lead to runtime errors.Add a check to ensure
bridgeInfo
is notundefined
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 issueFix 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 issueFix operator precedence and typo in error message (similar to
getProtoForPostAction
)The
getPostAction
method has similar issues to those found ingetProtoForPostAction
:
- There's an operator precedence issue in the IBC wasm transfer case.
- The error message for missing prefix or OBridge address contains a typo.
Apply these diffs to fix the issues:
- Fix operator precedence:
- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, + remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
- 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.
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; | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor buildMemoSwap
for improved maintainability and error handling
- Address the TODO comment about calculating the minimum receive amount.
- Add error handling for potentially undefined
addresses[currentChain]
. - Use constants for chain IDs to improve maintainability.
- 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.
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(); | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor buildExecuteMsg
to reduce duplication and improve maintainability
- Address the TODO comment about calculating the minimum receive amount.
- Add error handling for potentially undefined
addresses[currentChain]
. - Reduce duplication with
buildMemoSwap
by extracting common logic. - 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.
} | ||
}) | ||
), | ||
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 | ||
} | ||
] | ||
}) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
throw generateError("Only support swap + convert + bride on Oraichain"); | |
throw generateError("Only support swap + convert + bridge on Oraichain"); |
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"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix operator precedence and typo in error message
-
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. -
The error message for missing prefix or OBridge address contains a typo: "os" should be "or".
Apply these diffs to fix the issues:
- Fix operator precedence:
- remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
- 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.
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"); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🧹 Outside diff range and nitpick comments (10)
packages/oraidex-common/src/celestia-network.ts (3)
1-3
: Remove unused importdefaultBech32Config
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 suggestionThe 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 suggestionThe 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 hardcodedfromAmount
.The
fromAmount
is currently set to10
. 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-outrecipientAddress
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 variableapprove
holds a mapping of coin types to addresses. Renaming it toaddresses
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 becalculateMinimumReceive()
.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
📒 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 moduleThe addition of
export * from "./celestia-network";
is consistent with the existing export statements in this file. This change expands the public API of theoraidex-common
package by including all exports from thecelestia-network
module.However, there are a few points to consider:
- Ensure that exporting all contents from the
celestia-network
module is intentional, as it may expose more functionality than necessary.- Verify that the
celestia-network
module exists and is correctly implemented.- 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 inpackages/oraidex-common/src/index.ts
. The exports fromcelestia-network
are utilized within the codebase as shown innetwork.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 typeThe 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 pricesThe 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 additionThe 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 UniversalSwapDataThe addition of the optional
affiliates
property to theUniversalSwapData
interface enhances the system's capability to handle affiliate-related data. This change maintains backward compatibility and uses the importedAffiliate
type for type safety.
99-99
: LGTM: Addition of isAlphaIbcWasm to SwapOptionsThe new
isAlphaIbcWasm
property in theSwapOptions
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 interfaceThe
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 RouterConfigSmartRouteThe new
maxSplits
property in theRouterConfigSmartRoute
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 typesThe changes in this file improve the universal swap system by:
- Introducing a new
ActionType
enum for categorizing main actions.- Adding support for affiliates in the
UniversalSwapData
interface.- Introducing a new configuration option for alpha IBC WASM functionality.
- Exporting the
Path
interface for better reusability.- Adding a
maxSplits
property to theRouterConfigSmartRoute
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 maintainabilityThe 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 addedThe changes in this file are minimal but impactful:
- Added a trailing comma to the
NOBLE_CHAIN_ID
entry, improving maintainability.- Introduced support for the Celestia network by adding a new
CELESTIA_CHAIN_ID
entry to theCOSMOS_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 IDThe addition of
CELESTIA_CHAIN_ID = "celestia"
to theCOSMOS_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 ofCOSMOS_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 jsLength of output: 4257
packages/oraidex-common/src/ibc-info.ts (1)
183-186
: Confirm consistency in omitting "celestia" fromibcInfosOld
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 tocelestia
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
: ImportcelestiaNetwork
is correctly addedThe import statement for
celestiaNetwork
ensures that the Celestia network configuration is included.
80-81
: UpdateNetworkName
type to include "Celestia"Adding
"Celestia"
to theNetworkName
type allows the application to recognize the new network.
547-547
: AddcelestiaNetwork
tochainInfos
arrayIncluding
celestiaNetwork
in thechainInfos
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, usinginjAddress
forCOSMOSHUB_CHAIN_ID
may not be appropriate unless intended.Please confirm that
addresses.injAddress
andaddresses.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 foruseIbcWasm
anduseAlphaIbcWasm
.Ensure that the condition correctly reflects the intended behavior when
useIbcWasm
is false or whenuseAlphaIbcWasm
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 thealphaRoutes
object and thepaths
array. Incorrect parameters might lead to unexpected behavior.Double-check the structure of
alphaRoutes
and ensure thatpaths
contains the necessary data starting from the second element (as you are filtering withindex > 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 parameterswapOption?.isSourceReceiverTest
aligns with the method's expected parameters.Review the
getSourceReceiver
method signature to confirm thatisSourceReceiverTest
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
andfromBech32
,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
toundefined
and usingCOSMOS_CHAIN_IDS
forORAICHAIN
andORAIBRIDGE
aligns with the new constants.
1192-1202
: Ensure proper parameters are passed to 'addOraiBridgeRoute'.With the addition of
injAddress
andalphaSmartRoutes
, verify thataddOraiBridgeRoute
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
andswapOptions.isAlphaIbcWasm
, and then callsthis.transferAndSwap(swapRoute)
. EnsureswapRoute
is defined and the method behaves as expected.Confirm that
swapRoute
is available in this scope and thattransferAndSwap
can handle the provided arguments.
1185-1185
:⚠️ Potential issueFix the typo in method name 'caculateMinimumReceive'.
There's a typo in the method name
caculateMinimumReceive()
. It should becalculateMinimumReceive()
.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.
currencies: [ | ||
{ | ||
coinDecimals: 6, | ||
coinDenom: "TIA", | ||
coinGeckoId: "celestia", | ||
coinMinimalDenom: "utia", | ||
coinImageUrl: "https://raw.githubusercontent.com/chainapsis/keplr-chain-registry/main/images/celestia/chain.png" | ||
} | ||
], | ||
features: [], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 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"> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 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" }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
UniversalSwapHelper.addOraiBridgeRoute( | ||
{ sourceReceiver: "" }, | ||
undefined as any, | ||
undefined as any, | ||
"0", | ||
undefined, | ||
{ | ||
isSourceReceiverTest: false | ||
} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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
.
| "Neutaro-1" // neutaro network; | ||
| "celestia"; // Celestia |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 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, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
/** | ||
* useAlphaIbcWasm case: (evm -> oraichain -> osmosis -> inj not using wasm) | ||
* useIbcWasm case: (evm -> cosmos) | ||
*/ | ||
if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
/** | |
* 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) { |
const hasInjectiveAddress = routesFlatten.some((route) => route.chainId === COSMOS_CHAIN_IDS.INJECTVE); | ||
if (hasInjectiveAddress) injAddress = this.config.cosmosWallet.getKeplrAddr(COSMOS_CHAIN_IDS.INJECTVE); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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); |
{ obridgeAddress, sourceReceiver: oraiAddress }, | ||
originalFromToken, | ||
originalToToken, | ||
minimumReceive, | ||
destinationReceiver, | ||
this.config.swapOptions, | ||
alphaSmartRoutes, | ||
obridgeAddress | ||
alphaSmartRoutes |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Outside diff range and nitpick comments (1)
packages/universal-swap/src/msg/msgs.ts (1)
1-155
: Summary of review forpackages/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:
- Error handling: Enhance error handling throughout the file, especially for potentially undefined values.
- Code duplication: Reduce duplication between
buildMemoSwap
andbuildExecuteMsg
by extracting common logic.- Compatibility: Replace
Array.prototype.at(-1)
with a more widely supported method to access the last element of an array.- Maintainability: Use constants for chain IDs instead of hard-coded strings.
- TODOs: Address the TODO comments, particularly regarding the calculation of minimum receive amounts.
- 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
📒 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 issueImprove error handling and return value in
getDestPrefixForBridgeToEvmOnOrai
- Rephrase the error message to avoid using contractions and improve clarity.
- Consider throwing an error for non-EVM chains instead of returning an empty string to prevent silent failures.
- 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.
//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; | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Address TODO and improve buildMemoSwap
function
- The TODO comment about calculating the minimum receive amount should be addressed. Implement this calculation to ensure proper slippage handling.
- Consider using constants for chain IDs to improve maintainability.
- 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.
//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(); | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor buildExecuteMsg
to reduce duplication and improve error handling
- Address the TODO comment about calculating the minimum receive amount, similar to
buildMemoSwap
. - There's significant code duplication between
buildMemoSwap
andbuildExecuteMsg
. Consider extracting common logic into a shared helper function. - Add error handling for potentially undefined
addresses[currentChain]
.
Apply these changes to address the issues:
- Implement the minimum receive calculation or add a more specific TODO with acceptance criteria.
- 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 }
- 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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 forreceiver
andcurrentAddress
promotes consistency across tests.Consider using a more descriptive name for the
validPath
constant, such asvalidBridgeActionPath
, to better reflect its content and purpose.
34-102
: LGTM: Comprehensive test cases forgetPostAction
.The use of
it.each
is an efficient way to test multiple scenarios for thegetPostAction
method. The test cases cover important scenarios, including error handling for undefinedbridgeInfo
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
, andgenExecuteMsg
) 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:
- Consider using more descriptive names for test data constants.
- Break down larger tests into smaller, more focused test cases.
- 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 thegetPostAction
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 contractThere'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 improvementsThe
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:
- Fix recurring typos in method names and error messages.
- Address operator precedence issues in ternary operations.
- Consider refactoring larger methods (e.g.,
genExecuteMsg
) into smaller, more focused helper methods.- Implement more precise decimal handling in the
setMinimumReceiveForSwap
method.- 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 withcalculateTimeoutTimestamp
The unary
+
operator is used to coerce the result ofcalculateTimeoutTimestamp(IBC_TRANSFER_TIMEOUT)
to a number in multiple places. IfcalculateTimeoutTimestamp
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 TestsThe
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
📒 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 ofChainMsg
. 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 ifgetBridgeInfo()
now throws an error for undefined cases, as suggested in a previous review. The current implementation assumesgetBridgeInfo()
always returns a validBridgeMsgInfo
object or throws an error, which is the correct approach.
1-143
: Overall, theCosmosMsg
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:
- Optimizing the loop in
getBridgeInfo
.- Adding clarifying comments in
getPostAction
.- 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 theOraichainMsg
class. The use ofit.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 thatbridgeInfo
is defined before using ingetProtoForPostAction
In the method
genMemoForIbcWasm
,bridgeInfo
is passed togetProtoForPostAction
without ensuring it's defined. IfbridgeInfo
isundefined
, this could lead to runtime errors.Add a check to ensure
bridgeInfo
is notundefined
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 generationThe
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 issueFix 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 issueFix typo in method name:
pasreConverterMsgToPoolId
→parseConverterMsgToPoolId
The method name
pasreConverterMsgToPoolId
appears to be misspelled. Renaming it toparseConverterMsgToPoolId
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 issueFix 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.
export const cosmosTestData = [ | ||
{ | ||
type: "Valid Path", | ||
path: "", | ||
nextMemo: "{}", | ||
expectedMemo: "" | ||
// expected | ||
} | ||
]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Enhance test data for comprehensive coverage
The current test data seems incomplete and potentially inconsistent. Consider the following improvements:
- Add more test cases to cover different scenarios (e.g., invalid paths, various memo contents).
- Ensure consistency between the
type
andpath
fields. For a "Valid Path", thepath
should not be empty. - Align the
expectedMemo
with thenextMemo
or clarify why they differ. - 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?
|
||
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) | ||
} | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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) | |
} | |
}; | |
} |
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; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
let currentChain = path.chainId; | ||
let currentAddress = addresses[currentChain]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
if (!this.destPrefix || !this.obridgeAddress) | ||
throw generateError("Missing prefix os Obridge address for bridge to EVM"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix operator precedence and typo in error message (similar to getProtoForPostAction
)
The getPostAction
method has similar issues to those found in getProtoForPostAction
:
- There's an operator precedence issue in the IBC wasm transfer case.
- The error message for missing prefix or OBridge address contains a typo.
Apply these diffs to fix the issues:
- Fix operator precedence:
- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
- 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
} | ||
}) | ||
), | ||
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 | ||
} | ||
] | ||
}) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
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) | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
try { | ||
osmosis.genMemoAsMiddleware(); | ||
} catch (err) { | ||
expect(err).toEqual(generateError(`Only support IBC bridge on ${invalidPathNotIbcTransfer.chainId}`)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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}`)); | |
} |
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}`)); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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); | |
} | |
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 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:
- If these tests are no longer relevant, remove them entirely.
- If they are still needed, move them to a separate test file for
OsmosisMsg
.- 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:
- Consider enhancing the error handling test for better clarity.
- The large test case for "Valid path with swap + ibc bridge" could be split into smaller, more focused tests for better maintainability.
- 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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:
- If the test is no longer relevant, remove it entirely.
- If it's still needed, implement it in a separate file for OsmosisMsg tests.
- 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 testsKeeping the codebase clean of unused code improves maintainability and readability.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 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:
- Use parameterized tests to reduce redundancy across similar test cases.
- Extract common assertion logic into helper functions for better maintainability.
- Improve error handling tests by using more specific assertions.
- Consider splitting large test cases into smaller, more focused tests.
- Remove or implement the commented-out test case at the end of the file.
- 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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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:
- If this test is no longer relevant, remove it entirely.
- If it is still needed, move it to a separate test file for
OsmosisMsg
.- 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.tsAddressing this will improve the clarity and maintainability of the test suite.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 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:
- Test structure: Consider using parameterized tests to reduce redundancy and improve maintainability.
- Helper functions: Extract common assertion logic into reusable helper functions.
- Setup: Use
beforeEach
for common setup tasks.- Constants: Replace hardcoded addresses and tokens with constants or configuration files.
- Error handling: Improve specificity in error message assertions.
- 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)
…ain/oraidex-sdk into feat/support-swap-oraidex-osmosis
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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 improvementThe
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:
Recurring issues:
- Fix operator precedence issues in ternary operations.
- Correct typos in error messages.
Code organization:
- Consider refactoring long methods (e.g.,
genMemoAsMiddleware
,genExecuteMsg
) into smaller, more focused helper methods.- Group related functionality together for better readability.
Error handling:
- Implement more robust error checking, especially when dealing with potentially undefined values.
Naming conventions:
- Fix the typo in the method name
pasreConverterMsgToPoolId
.Comments and documentation:
- Add more inline comments to explain complex logic.
- Consider adding JSDoc comments for better code documentation.
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 theUniversalSwapHelper
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 ofalphaSmartRoute
improve the method's capability to handle various routing scenarios.Consider the following improvements:
- Extract the
isAlphaIbcWasm
logic into a separate private method for better readability and maintainability.- Add comments explaining the purpose of the
alphaSmartRoute
parameter and theisAlphaIbcWasm
case.- 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 therouterOption
increases the flexibility of thehandleSimulateSwap
method. However, the condition for settingfromInfo
andtoInfo
could be clearer.
- Clarify the condition for setting
fromInfo
andtoInfo
:- if (!query?.routerOption?.useIbcWasm || query?.routerOption?.useAlphaIbcWasm) { + if (query?.routerOption?.useAlphaIbcWasm || (!query?.routerOption?.useIbcWasm && !query?.routerOption?.useAlphaIbcWasm)) {This change makes it explicit that
fromInfo
andtoInfo
are set to the original values whenuseAlphaIbcWasm
is true, or when bothuseIbcWasm
anduseAlphaIbcWasm
are false.
- 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
📒 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 issueFix operator precedence and typo in error message (similar to
getProtoForPostAction
)The
getPostAction
method has similar issues to those found ingetProtoForPostAction
:
- There's an operator precedence issue in the IBC wasm transfer case.
- The error message for missing prefix or OBridge address contains a typo.
Apply these diffs to fix the issues:
- Fix operator precedence:
- remote_address: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver, + remote_address: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
- 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 suggestionConsider refactoring
genExecuteMsg
for improved maintainabilityThe
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 issueFix 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 issueVerify that
bridgeInfo
is defined before using ingetProtoForPostAction
In the method
genMemoForIbcWasm
,bridgeInfo
is passed togetProtoForPostAction
without ensuring it's defined. IfbridgeInfo
isundefined
, this could lead to runtime errors.Add a check to ensure
bridgeInfo
is notundefined
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:
- The flagged lines contain blockchain addresses (e.g., contract addresses, token addresses) and other blockchain-related identifiers.
- These are not actual API keys, but rather public identifiers used in blockchain transactions and smart contracts.
- 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)
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; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
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 | ||
} | ||
}) | ||
}; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
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"); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix operator precedence and typo in error message
-
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. -
The error message for missing prefix or OBridge address contains a typo: "os" should be "or".
Apply these diffs to fix the issues:
- Fix operator precedence:
- remoteAddress: prefix + isBridgeToEvm ? this.obridgeAddress : this.receiver,
+ remoteAddress: prefix + (isBridgeToEvm ? this.obridgeAddress : this.receiver),
- 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.
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: {} | ||
}) | ||
}); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix typo in method name and consider implementing TODO
-
The method name
pasreConverterMsgToPoolId
contains a typo. It should beparseConverterMsgToPoolId
. -
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.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codebase verification
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
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") | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider using constants for hardcoded addresses and improving test structure.
- 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
};
-
Consider parameterizing this test case along with the others, as suggested in previous comments.
-
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)
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`)); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling test.
The error handling test is good, but it can be improved:
- Use a more specific error message:
const errorMessage = `Error on generate memo as middleware: Only support ibc bridge for chain ${invalidPath.chainId}`;
- 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)
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") | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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:
-
Use constants for hardcoded addresses and tokens, as suggested earlier.
-
Consider including this in the parameterized tests:
const testCases = [
// ... previous cases
{ name: "ibc wasm bridge only", path: createValidPathWithIbcWasmBridgeOnly() },
];
- 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)
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`)); | ||
} | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve error handling tests with parameterization.
The error handling tests are good, but they can be improved:
- 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'));
});
-
Use
expect().toThrow()
for cleaner error testing. -
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)
|
||
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`)); | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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:
-
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.
-
Helper Functions: Extract common assertion logic into helper functions to improve readability and reduce repetition.
-
Constants: Replace hardcoded values (especially addresses and tokens) with constants or configuration objects to improve maintainability and reduce the risk of exposing sensitive information.
-
Error Handling: Improve error handling tests by using more specific error message assertions and consistent testing patterns.
-
Test Structure: Consider breaking down large test cases into smaller, more focused tests. This will make the tests easier to understand and maintain.
-
Setup and Teardown: Use
beforeEach
andafterEach
hooks to set up common objects and clean up after tests. -
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)
…ain/oraidex-sdk into feat/support-swap-oraidex-osmosis
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (9)
packages/universal-swap/tests/helper.spec.ts (3)
589-593
: Update toaddOraiBridgeRoute
test looks good, but consider adding more assertions.The test case for
addOraiBridgeRoute
has been updated to use an object for thesourceReceiver
anddestReceiver
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 forgenerateSmartRouteForSwap
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:
- Invalid asset information
- Unsupported chain IDs
- 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:
- Add a test for the
generateConvertCw20Erc20Message
function, which is currently not covered.- 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 theUniversalSwapHelper
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.
- Consider extracting the alpha IBC WASM handling logic (lines 374-411) into a separate private method for better code organization and readability.
- Improve error handling by providing more specific error messages, especially for the error thrown on line 379.
- Consider using optional chaining (
?.
) for safer property access, particularly when checkingswapOption
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
todontAlowSwapAfter
to match the usage in line 612. Alternatively, fix the typo in line 612 to usedontAllowSwapAfter
. 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 theUniversalSwapHelper
class.
- Consider breaking down the large function into smaller, more focused helper functions to improve readability and maintainability.
- Add comments explaining the purpose of key logic blocks, especially the conditions for handling different types of actions and paths.
- Consider using more descriptive variable names to make the code self-documenting. For example,
isSwapType
could beisSwapAction
for clarity.- 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 documentationThe changes to the
UniversalSwapHandler
class, particularly in theswapCosmosToOtherNetwork
andprocessUniversalSwap
methods, have introduced new parameters and more complex logic. To ensure the reliability of these changes:
- Consider adding more comprehensive unit tests to cover the new scenarios, especially for different combinations of swap types and chain IDs.
- Update the documentation for the
UniversalSwapHandler
class to reflect the new parameters and functionality, particularly for theswapCosmosToOtherNetwork
andprocessUniversalSwap
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
andsimulateAmount
reduces flexibility and maintainability. Consider externalizing these values to a configuration file or environment variables.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 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 toaddOraiBridgeRoute
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 toamountSimulate
in thesimulateSwapUsingSmartRoute
function call maintains consistency with the changes made in thehandleSimulateSwap
method.packages/universal-swap/src/handler.ts (6)
1066-1075
: Update method signature to include new parametersThe
swapCosmosToOtherNetwork
method now includes additional parameters such asalphaSmartRoutes
anduserSlippage
. This change allows for more flexibility in the swap process.
1200-1211
: Confirm correct parameter passing toaddOraiBridgeRoute
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 IDsNew variables have been added to the method, including
alphaSmartRoutes
anduserSlippage
. Additionally, there are changes in how chain IDs are accessed, now usingCOSMOS_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 3Also applies to: 1184-1187
Line range hint
1214-1221
: Review the conditional logic for different swap typesThe 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 addressesA 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 inaddOraiBridgeRoute
The
addOraiBridgeRoute
function now includesuserSlippage
andalphaSmartRoutes
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)
} catch (error) { | ||
console.trace("error: ", error); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
} catch (error) { | |
console.trace("error: ", error); | |
} | |
} catch (error) { | |
console.error("An error occurred during the universal swap:", error.message); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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:
- Implement the structural improvements suggested in previous comments (separate
it
blocks, refactor large objects, use constants).- Consider adding more test cases to cover edge cases or different combinations of input parameters.
- If not already present elsewhere, add tests for invalid input data (e.g., malformed route object, invalid addresses).
- 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
📒 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.
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" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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:
- Move this object to a separate file (e.g.,
testData.ts
) and import it. - 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.
// 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`)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve test structure and error assertion.
While the error scenarios are well-chosen, the test structure can be improved:
- Use separate
it
blocks for each test case. This improves test isolation and makes it easier to identify which specific case failed. - 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.
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) | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance test readability and maintainability.
The successful case test is comprehensive, but it can be improved:
-
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.
-
Use constants for repeated values (like addresses and channel IDs) to improve maintainability.
-
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 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 improvementsThis 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:
- Reduce code duplication by extracting common setup logic and using parameterized tests.
- Improve readability by breaking down large test cases into smaller, more focused ones.
- Enhance maintainability by using helper functions or fixtures for complex data structures.
- Increase consistency in error case testing across all scenarios.
- 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
📒 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.
{ | ||
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) | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve test structure and readability
While this test case is comprehensive, consider the following improvements:
- Move the large
route
object to a separate fixture or helper function to reduce clutter in the test body. - Separate error cases and the successful case into different
it
blocks for better isolation and clarity. - 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.
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) | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Enhance test coverage and reduce duplication
This test case has similar structure issues to the previous one. Consider the following improvements:
- Extract the
route
object creation to a shared helper function that can be parameterized for different scenarios. - Add error case testing to ensure robust coverage.
- 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)
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) | ||
} | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Improve test specificity and structure
This test case continues the pattern of the previous ones. Consider these improvements:
- Make the test description more specific, e.g., "should correctly generate swap message from Noble to Oraichain".
- Extract the
route
object to a helper function or fixture. - Add error case testing.
- 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)
{ | ||
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) | ||
} | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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:
- Create a shared setup function for common elements across tests.
- Use parameterized tests to cover multiple scenarios with less code duplication.
- 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)
…ain/oraidex-sdk into feat/support-swap-oraidex-osmosis
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 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 suggestionRefactor 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:
- Extract the
route
object to a separate file or create a factory function for it.- Refactor error cases to use
expect().toThrow()
instead of try-catch blocks.- 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 suggestionApply 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:
This test covers a swap from cosmos to EVM, which might require additional assertions or checks specific to EVM interactions.
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
- 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 suggestionRefactor test and address potential security concerns
Apply the refactoring suggestions from previous test cases to improve the overall structure and readability of this test.
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
- 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 stringBy 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 suggestionRefactor test and enhance Noble-to-EVM swap verification
Apply the refactoring suggestions from previous test cases to improve the overall structure and readability of this test.
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
- 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
- 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)
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 ""; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve error handling and return value in getDestPrefixForBridgeToEvmOnOrai
The function can be improved for better error handling and clarity:
- Instead of returning an empty string for non-EVM chains, consider throwing an error to prevent silent failures.
- 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.
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; | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor buildMemoSwap
for improved maintainability and error handling
- Address the TODO comment about calculating the minimum receive amount.
- Add error handling for potentially undefined
addresses[currentChain]
. - Use constants for chain IDs to improve maintainability.
- 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.
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(); | ||
} | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Refactor buildExecuteMsg
to reduce duplication and improve maintainability
- Address the TODO comment about calculating the minimum receive amount.
- There's significant code duplication between
buildMemoSwap
andbuildExecuteMsg
. Consider extracting common logic into shared helper functions. - Add error handling for potentially undefined
addresses[currentChain]
.
Apply these changes to address the issues:
- Implement the minimum receive calculation or add a more specific TODO with acceptance criteria.
- 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 }
- 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.
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); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve generateMsgSwap
for better compatibility and error handling
-
Replace
Array.prototype.at(-1)
with a more compatible approach:let lastPath = route.paths[route.paths.length - 1]; let receiver = addresses[lastPath?.tokenOutChainId];
-
Add error handling for undefined addresses:
if (!receiver) { throw generateError(`Address not found for the last path's tokenOutChainId`); }
-
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.
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); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improve generateMemoSwap
for better compatibility, error handling, and input validation
-
Replace
Array.prototype.at(-1)
with a more compatible approach:let lastPath = route.paths[route.paths.length - 1]; let receiver = addresses[lastPath?.tokenOutChainId];
-
Add error handling for undefined addresses:
if (!receiver) { throw generateError(`Address not found for the last path's tokenOutChainId`); }
-
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.
{ | ||
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) | ||
} | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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:
-
Test structure refactoring: Extract complex route objects to separate files or create factory functions. This will significantly reduce the complexity within each test case.
-
Error case testing: Refactor error cases to use
expect().toThrow()
instead of try-catch blocks for cleaner and more idiomatic test code. -
Assertion improvements: Break down large expect statements into smaller, more specific checks. Consider using snapshot testing for complex objects.
-
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.
-
Consistency: Apply the suggested improvements consistently across all test cases.
-
Helper functions: Implement helper functions for common operations like route generation and memo decoding to reduce duplication and improve maintainability.
Next steps:
- Implement the suggested refactorings for one test case.
- Review the changes and adjust as necessary.
- Apply similar refactoring to the remaining test cases.
- Add any missing test scenarios or edge cases.
- Run the refactored tests and ensure all pass.
- 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)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 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 theaddresses
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
andmaxSplits
ingenerateSmartRouteForSwap
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_SPLITSThis approach increases flexibility and maintainability of the code.
maxSplits: routerConfig.maxSplits, | ||
swapConfig: { | ||
dontAlowSwapAfter: routerConfig.dontAllowSwapAfter | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
maxSplits: routerConfig.maxSplits, | |
swapConfig: { | |
dontAlowSwapAfter: routerConfig.dontAllowSwapAfter | |
} | |
maxSplits: routerConfig.maxSplits, | |
swapConfig: { | |
dontAllowSwapAfter: routerConfig.dontAllowSwapAfter | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Outside diff range and nitpick comments (8)
packages/universal-swap/src/helper.ts (7)
314-340
: New address handling methods addedThe new static methods
getAddress
andgenerateAddress
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
: EnhancedaddOraiBridgeRoute
method with improved flexibilityThe 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:
- Consider adding type definitions for the
addresses
object and theswapOption
object to improve type safety and code clarity.- 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 addedThe 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 thegenerateMemoSwap
function.Suggestions for improvement:
- Consider extracting the logic for generating receiver addresses into a separate helper method to improve code readability and maintainability.
- 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
: EnhancedhandleSimulateSwap
method with new routing optionsThe updates to the
handleSimulateSwap
method improve its flexibility by adding support for new routing scenarios, particularly the alpha IBC WASM case. The changes torouterConfigDefault
ensure consistency with the new routing options.Suggestions for improvement:
- Consider using TypeScript's optional chaining operator (
?.
) to simplify the null checks in therouterConfigDefault
object initialization.- 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
: UpdatedgenerateMsgsSmartRouterV2withV3
to support V3 poolsThe 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:
- Consider adding type annotations for the
routes
andofferInfo
parameters to improve type safety and code clarity.- 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
: NewflattenSmartRouters
method for simplifying route structuresThe 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:
- Consider adding type annotations for the
routers
parameter and the return type to improve type safety and code clarity.- The method is quite complex. Consider breaking it down into smaller, more focused helper methods to improve readability and maintainability.
- 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 classThe changes made to the
UniversalSwapHelper
class inpackages/universal-swap/src/helper.ts
significantly enhance its capabilities for handling complex swap operations across various blockchain networks. Key improvements include:
- Support for alpha IBC WASM routing
- Enhanced address handling for Cosmos-based chains
- Improved flexibility in the
addOraiBridgeRoute
method- Support for V3 pools in swap message generation
- 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:
- Consider breaking down this large class into smaller, more focused classes or modules to improve maintainability and adherence to the Single Responsibility Principle.
- Implement comprehensive unit tests for the new methods and updated functionality to ensure reliability and ease of future modifications.
- 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 classThe UniversalSwapHandler class has undergone several improvements:
- Standardization of chain IDs using
COSMOS_CHAIN_IDS
constants.- Integration of alpha smart router functionality for potentially more efficient swaps.
- Enhanced flexibility in swap operations with new parameters like
userSlippage
andalphaSmartRoutes
.- 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:
- Ensure comprehensive testing of all modified methods, especially with edge cases.
- Update documentation to reflect new parameters and functionality.
- 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
📒 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 handlingThe 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 functionalityThe addition of
COSMOS_CHAIN_ID_COMMON
,generateMemoSwap
, andgenerateMsgSwap
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 addedThe addition of
COSMOS_CHAIN_IDS
from@oraichain/common
andgenerateMsgSwap
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 methodalphaSmartRouterSwapNewMsg
addedThis new method introduces specialized handling for alpha smart router swaps:
- It supports multiple swap types: cosmos-to-others, oraichain-to-oraichain, and oraichain-to-cosmos.
- It uses the new
generateMsgSwap
function for efficient message generation.- 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:
- The
generateMsgSwap
function is properly implemented and tested.- Error handling is adequate for unsupported swap types.
- 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 inprocessUniversalSwap
The
processUniversalSwap
method has been updated to incorporate alpha smart router functionality:
- New parameters like
alphaSmartRoutes
anduserSlippage
are now used.- A new condition checks for
alphaSmartRoutes
andisAlphaIbcWasm
to determine the swap method.- 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:
- Ensure that the integration doesn't break existing swap flows.
- Verify that error handling is comprehensive, especially for edge cases in the new routing logic.
- 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 typescriptAlso applies to: 1231-1238
1058-1067
: Enhanced flexibility inswapCosmosToOtherNetwork
methodThe method has been updated to include more parameters and handle new scenarios:
- New parameters like
alphaSmartRoutes
anduserSlippage
have been added.- Support for Injective chain has been added with the
injAddress
variable.- 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
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" | ||
} | ||
} | ||
] | ||
} | ||
] | ||
} | ||
] | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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);
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"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
(() => { | ||
alphaSwapToOraichain(); | ||
})(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ 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.
Summary by CodeRabbit
New Features
CosmosMsg
,OsmosisMsg
, andOraichainMsg
classes for managing token swap and bridge messages on their respective platforms.types.ts
with new action types and optional properties for better data management.ChainMsg
class to encapsulate chain message properties and validation.UniversalSwapHelper
andUniversalSwapHandler
classes to enhance token swap functionalities and improve error handling.Bug Fixes
Documentation
.gitignore
entries for better project management.generateMsgSwap
to cover additional scenarios and improve robustness.