Conversation
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughWalkthroughThe changes introduce two new dependencies to the project, enhancing its capabilities with the Squid SDK and associated types. The Changes
Tip OpenAI O1 model for chat
Recent review detailsConfiguration used: CodeRabbit UI Files ignored due to path filters (1)
Files selected for processing (1)
Files skipped from review as they are similar to previous changes (1)
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 (
|
There was a problem hiding this comment.
Actionable comments posted: 0
Outside diff range, codebase verification and nitpick comments (1)
src/components/Request/Pay/Views/Initial.view.tsx (1)
48-133: LGTM, but address the commented-out code blocks.The code changes are approved.
However, please address the following:
- There are commented-out code blocks from lines 77-90 that seem to be related to estimating the
fromAmountusing the Squid SDK. If this code is no longer needed, please remove it. If it is still relevant, uncomment it and ensure it is properly integrated with the rest of the code.
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
Files selected for processing (2)
- package.json (1 hunks)
- src/components/Request/Pay/Views/Initial.view.tsx (6 hunks)
Additional comments not posted (1)
package.json (1)
12-13: LGTM!The code changes are approved.
There was a problem hiding this comment.
Actionable comments posted: 0
Outside diff range and nitpick comments (1)
src/context/tokenSelector.context.tsx (1)
35-35: Consider a different default value forselectedTokenDecimals.The code changes are approved.
However, the initial value of
18forselectedTokenDecimalsmay not be appropriate for all tokens. Consider using a more generic default value, such as0orundefined, and updating it based on the token's actual decimal places.
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
Files selected for processing (7)
- package.json (2 hunks)
- src/components/Create/Link/Confirm.view.tsx (2 hunks)
- src/components/Request/Pay/Views/Initial.view.tsx (6 hunks)
- src/components/Request/Pay/utils.ts (1 hunks)
- src/context/tokenSelector.context.tsx (8 hunks)
- src/utils/fetch.utils.ts (1 hunks)
- src/utils/general.utils.ts (3 hunks)
Files skipped from review as they are similar to previous changes (1)
- package.json
Additional context used
Biome
src/context/tokenSelector.context.tsx
[error] 107-107: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
Additional comments not posted (17)
src/utils/fetch.utils.ts (1)
29-29: LGTM!The code changes are approved for the following reasons:
- The
decimalsproperty is correctly retrieved from thejson.data.contractsarray based on the providedchainId.- The
findmethod is used efficiently to locate the desired contract object.- The optional chaining operator
?.is used to safely access thedecimalsproperty, preventing potential errors.The addition of the
decimalsproperty enhances the functionality of thefetchTokenPricefunction by providing useful information about the token's decimal precision.src/components/Request/Pay/utils.ts (2)
3-9: LGTM!The code changes are approved.
25-83: LGTM!The code changes are approved. The function correctly calculates the
fromAmountconsidering the token prices and slippage, while handling various edge cases and potential issues.Some key points:
- Handles zero address tokens by replacing them with the
NATIVE_TOKEN_ADDRESS.- Normalizes token prices to account for different decimal counts, ensuring consistent calculations.
- Applies slippage percentage to the
fromAmountusing BigNumber calculations to avoid floating-point issues.- Handles errors by logging them and returning
null.src/context/tokenSelector.context.tsx (5)
13-13: LGTM!The code changes are approved.
53-53: LGTM!The code changes are approved.
63-63: LGTM!The code changes are approved.
Also applies to: 73-73, 81-81, 92-92, 96-96
107-107: LGTM!The code changes are approved.
Also applies to: 110-110
Tools
Biome
[error] 107-107: Change to an optional chain.
Unsafe fix: Change to an optional chain.
(lint/complexity/useOptionalChain)
119-119: LGTM!The code changes are approved.
src/components/Create/Link/Confirm.view.tsx (2)
44-46: LGTM!The code changes are approved.
167-167: LGTM!The code changes are approved.
src/components/Request/Pay/Views/Initial.view.tsx (5)
3-5: LGTM!The code changes are approved.
14-17: LGTM!The code changes are approved.
Line range hint
27-53: LGTM!The code changes are approved.
56-163: LGTM!The code changes in the
handleOnNextfunction are approved. The updates introduce significant enhancements to the transaction handling logic, allowing for cross-chain transactions and flexibility in fulfilling payment requests with different tokens and chains.
Line range hint
209-257: LGTM!The code changes are approved. The addition of the
TokenSelectorcomponent and the informative label enhances the user experience by providing flexibility in choosing the token and chain for fulfilling the payment request.src/utils/general.utils.ts (2)
659-667: LGTM!The changes to the
updatePeanutPreferencesfunction are approved. The addition of the optionaldecimalsparameter and its inclusion in thedataobject enhance the function's capability to store additional data related to peanut preferences in local storage.Also applies to: 676-676
696-696: LGTM!The change to the
dataobject in thegetPeanutPreferencesfunction is approved. Initializing thedecimalsproperty toundefinedensures consistency with the changes made to theupdatePeanutPreferencesfunction.
| open() | ||
| } | ||
|
|
||
| const switchNetwork = async (chainId: string) => { |
There was a problem hiding this comment.
why do we need a new function to switch networks?
If we need it, lets use it consistently on all pages
There was a problem hiding this comment.
Actionable comments posted: 2
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- src/components/Request/Pay/Views/Initial.view.tsx (6 hunks)
- src/components/Request/Pay/utils.ts (1 hunks)
Files skipped from review due to trivial changes (1)
- src/components/Request/Pay/utils.ts
Additional comments not posted (2)
src/components/Request/Pay/Views/Initial.view.tsx (2)
30-30: LGTM! The addition of theselectedTokenDecimalsproperty and the usage of theTokenSelectorcomponent enhance the user experience.The changes are approved.
Also applies to: 232-232
227-230: LGTM! The informative comment enhances the user experience by providing flexibility in fulfilling the payment request.The changes are approved.
| const switchNetwork = async (chainId: string) => { | ||
| try { | ||
| await switchNetworkUtil({ | ||
| chainId, | ||
| currentChainId: currentChain?.id, | ||
| setLoadingState, | ||
| switchChainAsync, | ||
| }) | ||
| console.log(`Switched to chain ${chainId}`) | ||
| } catch (error) { | ||
| console.error('Failed to switch network:', error) | ||
| } | ||
| } |
There was a problem hiding this comment.
Move the switchNetwork function to a separate utility file for reusability and consistency across the codebase.
The switchNetwork function is a utility function that can be reused in other parts of the codebase. Moving it to a separate utility file will promote reusability and consistency.
Apply this diff to move the function to a separate utility file (e.g., src/utils/network.utils.ts):
-const switchNetwork = async (chainId: string) => {
- try {
- await switchNetworkUtil({
- chainId,
- currentChainId: currentChain?.id,
- setLoadingState,
- switchChainAsync,
- })
- console.log(`Switched to chain ${chainId}`)
- } catch (error) {
- console.error('Failed to switch network:', error)
- }
-}Then, import and use the function from the utility file:
import { switchNetwork } from '@/utils/network.utils'| if (selectedChainID !== currentChain) { | ||
| await switchNetwork(selectedChainID) | ||
| } | ||
| try { | ||
| setErrorState({ showError: false, errorMessage: '' }) | ||
| if (!unsignedTx) return | ||
| await assertValues({ tokenValue: requestLinkData.tokenAmount }) | ||
| if (selectedChainID === requestLinkData.chainId && selectedTokenAddress === requestLinkData.tokenAddress) { | ||
| await assertValues({ tokenValue: requestLinkData.tokenAmount }) | ||
| setLoadingState('Sign in wallet') | ||
| const hash = await sendTransactions({ | ||
| preparedDepositTxs: { unsignedTxs: [unsignedTx] }, | ||
| feeOptions: undefined, | ||
| }) | ||
|
|
||
| setLoadingState('Sign in wallet') | ||
| setLoadingState('Executing transaction') | ||
|
|
||
| const hash = await sendTransactions({ | ||
| preparedDepositTxs: { unsignedTxs: [unsignedTx] }, | ||
| feeOptions: undefined, | ||
| }) | ||
| await peanut.submitRequestLinkFulfillment({ | ||
| chainId: requestLinkData.chainId, | ||
| hash: hash ?? '', | ||
| payerAddress: address ?? '', | ||
| link: requestLinkData.link, | ||
| apiUrl: '/api/proxy/patch/', | ||
| }) | ||
|
|
||
| setLoadingState('Executing transaction') | ||
| const currentDate = new Date().toISOString() | ||
| utils.saveRequestLinkFulfillmentToLocalStorage({ | ||
| details: { | ||
| ...requestLinkData, | ||
| destinationChainFulfillmentHash: hash ?? '', | ||
| createdAt: currentDate, | ||
| }, | ||
| link: requestLinkData.link, | ||
| }) | ||
|
|
||
| await peanut.submitRequestLinkFulfillment({ | ||
| chainId: requestLinkData.chainId, | ||
| hash: hash ?? '', | ||
| payerAddress: address ?? '', | ||
| link: requestLinkData.link, | ||
| apiUrl: '/api/proxy/patch/', | ||
| }) | ||
| setTransactionHash(hash ?? '') | ||
| onNext() | ||
| } else { | ||
| setLoadingState('Sign in wallet') | ||
|
|
||
| const currentDate = new Date().toISOString() | ||
| utils.saveRequestLinkFulfillmentToLocalStorage({ | ||
| details: { | ||
| ...requestLinkData, | ||
| destinationChainFulfillmentHash: hash ?? '', | ||
| createdAt: currentDate, | ||
| }, | ||
| link: requestLinkData.link, | ||
| }) | ||
| const xchainUnsignedTxs = await peanut.prepareXchainRequestFulfillmentTransaction({ | ||
| fromToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', | ||
| fromChainId: selectedChainID, | ||
| senderAddress: address ?? '', | ||
| recipientAddress: requestLinkData.recipientAddress as string, | ||
| destinationChainId: requestLinkData.chainId, | ||
| destinationToken: requestLinkData.tokenAddress, | ||
| link: requestLinkData.link, | ||
| squidRouterUrl: 'https://apiplus.squidrouter.com/v2/route', | ||
| apiUrl: '/api/proxy/get', | ||
| provider: await peanut.getDefaultProvider(selectedChainID), | ||
| tokenType: selectedTokenAddress === ADDRESS_ZERO ? EPeanutLinkType.native : EPeanutLinkType.erc20, | ||
| fromTokenDecimals: selectedTokenDecimals as number, | ||
| }) | ||
|
|
||
| const { unsignedTxs } = xchainUnsignedTxs | ||
| const hash = await sendTransactions({ | ||
| preparedDepositTxs: { unsignedTxs }, | ||
| feeOptions: undefined, | ||
| }) | ||
| setLoadingState('Executing transaction') | ||
|
|
||
| await peanut.submitRequestLinkFulfillment({ | ||
| chainId: requestLinkData.chainId, | ||
| hash: hash ?? '', | ||
| payerAddress: address ?? '', | ||
| link: requestLinkData.link, | ||
| apiUrl: '/api/proxy/patch/', | ||
| }) | ||
|
|
||
| setTransactionHash(hash ?? '') | ||
| onNext() | ||
| const currentDate = new Date().toISOString() | ||
| utils.saveRequestLinkFulfillmentToLocalStorage({ | ||
| details: { | ||
| ...requestLinkData, | ||
| destinationChainFulfillmentHash: hash ?? '', | ||
| createdAt: currentDate, | ||
| }, | ||
| link: requestLinkData.link, | ||
| }) | ||
|
|
||
| setTransactionHash(hash ?? '') | ||
| onNext() | ||
| } |
There was a problem hiding this comment.
Consider refactoring the handleOnNext function for better readability and maintainability.
The handleOnNext function is quite large and can be refactored for better readability and maintainability. Here are a few suggestions:
- Extract the logic for handling the case when the selected chain and token match the request data into a separate function (e.g.,
handleMatchingChainAndToken). - Extract the logic for handling the case when the selected chain and token do not match the request data into a separate function (e.g.,
handleCrossChainFulfillment). - Move the common logic for submitting the request link fulfillment and saving it to local storage into a separate function (e.g.,
submitAndSaveFulfillment).
Apply this diff to refactor the function:
-const handleOnNext = async () => {
- if (selectedChainID !== currentChain) {
- await switchNetwork(selectedChainID)
- }
- try {
- setErrorState({ showError: false, errorMessage: '' })
- if (!unsignedTx) return
- if (selectedChainID === requestLinkData.chainId && selectedTokenAddress === requestLinkData.tokenAddress) {
- await assertValues({ tokenValue: requestLinkData.tokenAmount })
- setLoadingState('Sign in wallet')
- const hash = await sendTransactions({
- preparedDepositTxs: { unsignedTxs: [unsignedTx] },
- feeOptions: undefined,
- })
-
- setLoadingState('Executing transaction')
-
- await peanut.submitRequestLinkFulfillment({
- chainId: requestLinkData.chainId,
- hash: hash ?? '',
- payerAddress: address ?? '',
- link: requestLinkData.link,
- apiUrl: '/api/proxy/patch/',
- })
-
- const currentDate = new Date().toISOString()
- utils.saveRequestLinkFulfillmentToLocalStorage({
- details: {
- ...requestLinkData,
- destinationChainFulfillmentHash: hash ?? '',
- createdAt: currentDate,
- },
- link: requestLinkData.link,
- })
-
- setTransactionHash(hash ?? '')
- onNext()
- } else {
- setLoadingState('Sign in wallet')
-
- const xchainUnsignedTxs = await peanut.prepareXchainRequestFulfillmentTransaction({
- fromToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
- fromChainId: selectedChainID,
- senderAddress: address ?? '',
- recipientAddress: requestLinkData.recipientAddress as string,
- destinationChainId: requestLinkData.chainId,
- destinationToken: requestLinkData.tokenAddress,
- link: requestLinkData.link,
- squidRouterUrl: 'https://apiplus.squidrouter.com/v2/route',
- apiUrl: '/api/proxy/get',
- provider: await peanut.getDefaultProvider(selectedChainID),
- tokenType: selectedTokenAddress === ADDRESS_ZERO ? EPeanutLinkType.native : EPeanutLinkType.erc20,
- fromTokenDecimals: selectedTokenDecimals as number,
- })
-
- const { unsignedTxs } = xchainUnsignedTxs
- const hash = await sendTransactions({
- preparedDepositTxs: { unsignedTxs },
- feeOptions: undefined,
- })
- setLoadingState('Executing transaction')
-
- await peanut.submitRequestLinkFulfillment({
- chainId: requestLinkData.chainId,
- hash: hash ?? '',
- payerAddress: address ?? '',
- link: requestLinkData.link,
- apiUrl: '/api/proxy/patch/',
- })
-
- const currentDate = new Date().toISOString()
- utils.saveRequestLinkFulfillmentToLocalStorage({
- details: {
- ...requestLinkData,
- destinationChainFulfillmentHash: hash ?? '',
- createdAt: currentDate,
- },
- link: requestLinkData.link,
- })
-
- setTransactionHash(hash ?? '')
- onNext()
- }
- } catch (error) {
- const errorString = utils.ErrorHandler(error)
- setErrorState({
- showError: true,
- errorMessage: errorString,
- })
- console.error('Error while submitting request link fulfillment:', error)
- } finally {
- setLoadingState('Idle')
- }
-}
+const handleMatchingChainAndToken = async () => {
+ await assertValues({ tokenValue: requestLinkData.tokenAmount })
+ setLoadingState('Sign in wallet')
+ const hash = await sendTransactions({
+ preparedDepositTxs: { unsignedTxs: [unsignedTx] },
+ feeOptions: undefined,
+ })
+ await submitAndSaveFulfillment(hash)
+}
+
+const handleCrossChainFulfillment = async () => {
+ setLoadingState('Sign in wallet')
+ const xchainUnsignedTxs = await peanut.prepareXchainRequestFulfillmentTransaction({
+ fromToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE',
+ fromChainId: selectedChainID,
+ senderAddress: address ?? '',
+ recipientAddress: requestLinkData.recipientAddress as string,
+ destinationChainId: requestLinkData.chainId,
+ destinationToken: requestLinkData.tokenAddress,
+ link: requestLinkData.link,
+ squidRouterUrl: 'https://apiplus.squidrouter.com/v2/route',
+ apiUrl: '/api/proxy/get',
+ provider: await peanut.getDefaultProvider(selectedChainID),
+ tokenType: selectedTokenAddress === ADDRESS_ZERO ? EPeanutLinkType.native : EPeanutLinkType.erc20,
+ fromTokenDecimals: selectedTokenDecimals as number,
+ })
+ const { unsignedTxs } = xchainUnsignedTxs
+ const hash = await sendTransactions({
+ preparedDepositTxs: { unsignedTxs },
+ feeOptions: undefined,
+ })
+ await submitAndSaveFulfillment(hash)
+}
+
+const submitAndSaveFulfillment = async (hash: string) => {
+ setLoadingState('Executing transaction')
+ await peanut.submitRequestLinkFulfillment({
+ chainId: requestLinkData.chainId,
+ hash: hash ?? '',
+ payerAddress: address ?? '',
+ link: requestLinkData.link,
+ apiUrl: '/api/proxy/patch/',
+ })
+ const currentDate = new Date().toISOString()
+ utils.saveRequestLinkFulfillmentToLocalStorage({
+ details: {
+ ...requestLinkData,
+ destinationChainFulfillmentHash: hash ?? '',
+ createdAt: currentDate,
+ },
+ link: requestLinkData.link,
+ })
+ setTransactionHash(hash ?? '')
+ onNext()
+}
+
+const handleOnNext = async () => {
+ if (selectedChainID !== currentChain) {
+ await switchNetwork(selectedChainID)
+ }
+ try {
+ setErrorState({ showError: false, errorMessage: '' })
+ if (!unsignedTx) return
+ if (selectedChainID === requestLinkData.chainId && selectedTokenAddress === requestLinkData.tokenAddress) {
+ await handleMatchingChainAndToken()
+ } else {
+ await handleCrossChainFulfillment()
+ }
+ } catch (error) {
+ const errorString = utils.ErrorHandler(error)
+ setErrorState({
+ showError: true,
+ errorMessage: errorString,
+ })
+ console.error('Error while submitting request link fulfillment:', error)
+ } finally {
+ setLoadingState('Idle')
+ }
+}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 (selectedChainID !== currentChain) { | |
| await switchNetwork(selectedChainID) | |
| } | |
| try { | |
| setErrorState({ showError: false, errorMessage: '' }) | |
| if (!unsignedTx) return | |
| await assertValues({ tokenValue: requestLinkData.tokenAmount }) | |
| if (selectedChainID === requestLinkData.chainId && selectedTokenAddress === requestLinkData.tokenAddress) { | |
| await assertValues({ tokenValue: requestLinkData.tokenAmount }) | |
| setLoadingState('Sign in wallet') | |
| const hash = await sendTransactions({ | |
| preparedDepositTxs: { unsignedTxs: [unsignedTx] }, | |
| feeOptions: undefined, | |
| }) | |
| setLoadingState('Sign in wallet') | |
| setLoadingState('Executing transaction') | |
| const hash = await sendTransactions({ | |
| preparedDepositTxs: { unsignedTxs: [unsignedTx] }, | |
| feeOptions: undefined, | |
| }) | |
| await peanut.submitRequestLinkFulfillment({ | |
| chainId: requestLinkData.chainId, | |
| hash: hash ?? '', | |
| payerAddress: address ?? '', | |
| link: requestLinkData.link, | |
| apiUrl: '/api/proxy/patch/', | |
| }) | |
| setLoadingState('Executing transaction') | |
| const currentDate = new Date().toISOString() | |
| utils.saveRequestLinkFulfillmentToLocalStorage({ | |
| details: { | |
| ...requestLinkData, | |
| destinationChainFulfillmentHash: hash ?? '', | |
| createdAt: currentDate, | |
| }, | |
| link: requestLinkData.link, | |
| }) | |
| await peanut.submitRequestLinkFulfillment({ | |
| chainId: requestLinkData.chainId, | |
| hash: hash ?? '', | |
| payerAddress: address ?? '', | |
| link: requestLinkData.link, | |
| apiUrl: '/api/proxy/patch/', | |
| }) | |
| setTransactionHash(hash ?? '') | |
| onNext() | |
| } else { | |
| setLoadingState('Sign in wallet') | |
| const currentDate = new Date().toISOString() | |
| utils.saveRequestLinkFulfillmentToLocalStorage({ | |
| details: { | |
| ...requestLinkData, | |
| destinationChainFulfillmentHash: hash ?? '', | |
| createdAt: currentDate, | |
| }, | |
| link: requestLinkData.link, | |
| }) | |
| const xchainUnsignedTxs = await peanut.prepareXchainRequestFulfillmentTransaction({ | |
| fromToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', | |
| fromChainId: selectedChainID, | |
| senderAddress: address ?? '', | |
| recipientAddress: requestLinkData.recipientAddress as string, | |
| destinationChainId: requestLinkData.chainId, | |
| destinationToken: requestLinkData.tokenAddress, | |
| link: requestLinkData.link, | |
| squidRouterUrl: 'https://apiplus.squidrouter.com/v2/route', | |
| apiUrl: '/api/proxy/get', | |
| provider: await peanut.getDefaultProvider(selectedChainID), | |
| tokenType: selectedTokenAddress === ADDRESS_ZERO ? EPeanutLinkType.native : EPeanutLinkType.erc20, | |
| fromTokenDecimals: selectedTokenDecimals as number, | |
| }) | |
| const { unsignedTxs } = xchainUnsignedTxs | |
| const hash = await sendTransactions({ | |
| preparedDepositTxs: { unsignedTxs }, | |
| feeOptions: undefined, | |
| }) | |
| setLoadingState('Executing transaction') | |
| await peanut.submitRequestLinkFulfillment({ | |
| chainId: requestLinkData.chainId, | |
| hash: hash ?? '', | |
| payerAddress: address ?? '', | |
| link: requestLinkData.link, | |
| apiUrl: '/api/proxy/patch/', | |
| }) | |
| setTransactionHash(hash ?? '') | |
| onNext() | |
| const currentDate = new Date().toISOString() | |
| utils.saveRequestLinkFulfillmentToLocalStorage({ | |
| details: { | |
| ...requestLinkData, | |
| destinationChainFulfillmentHash: hash ?? '', | |
| createdAt: currentDate, | |
| }, | |
| link: requestLinkData.link, | |
| }) | |
| setTransactionHash(hash ?? '') | |
| onNext() | |
| } | |
| const handleMatchingChainAndToken = async () => { | |
| await assertValues({ tokenValue: requestLinkData.tokenAmount }) | |
| setLoadingState('Sign in wallet') | |
| const hash = await sendTransactions({ | |
| preparedDepositTxs: { unsignedTxs: [unsignedTx] }, | |
| feeOptions: undefined, | |
| }) | |
| await submitAndSaveFulfillment(hash) | |
| } | |
| const handleCrossChainFulfillment = async () => { | |
| setLoadingState('Sign in wallet') | |
| const xchainUnsignedTxs = await peanut.prepareXchainRequestFulfillmentTransaction({ | |
| fromToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', | |
| fromChainId: selectedChainID, | |
| senderAddress: address ?? '', | |
| recipientAddress: requestLinkData.recipientAddress as string, | |
| destinationChainId: requestLinkData.chainId, | |
| destinationToken: requestLinkData.tokenAddress, | |
| link: requestLinkData.link, | |
| squidRouterUrl: 'https://apiplus.squidrouter.com/v2/route', | |
| apiUrl: '/api/proxy/get', | |
| provider: await peanut.getDefaultProvider(selectedChainID), | |
| tokenType: selectedTokenAddress === ADDRESS_ZERO ? EPeanutLinkType.native : EPeanutLinkType.erc20, | |
| fromTokenDecimals: selectedTokenDecimals as number, | |
| }) | |
| const { unsignedTxs } = xchainUnsignedTxs | |
| const hash = await sendTransactions({ | |
| preparedDepositTxs: { unsignedTxs }, | |
| feeOptions: undefined, | |
| }) | |
| await submitAndSaveFulfillment(hash) | |
| } | |
| const submitAndSaveFulfillment = async (hash: string) => { | |
| setLoadingState('Executing transaction') | |
| await peanut.submitRequestLinkFulfillment({ | |
| chainId: requestLinkData.chainId, | |
| hash: hash ?? '', | |
| payerAddress: address ?? '', | |
| link: requestLinkData.link, | |
| apiUrl: '/api/proxy/patch/', | |
| }) | |
| const currentDate = new Date().toISOString() | |
| utils.saveRequestLinkFulfillmentToLocalStorage({ | |
| details: { | |
| ...requestLinkData, | |
| destinationChainFulfillmentHash: hash ?? '', | |
| createdAt: currentDate, | |
| }, | |
| link: requestLinkData.link, | |
| }) | |
| setTransactionHash(hash ?? '') | |
| onNext() | |
| } | |
| const handleOnNext = async () => { | |
| if (selectedChainID !== currentChain) { | |
| await switchNetwork(selectedChainID) | |
| } | |
| try { | |
| setErrorState({ showError: false, errorMessage: '' }) | |
| if (!unsignedTx) return | |
| if (selectedChainID === requestLinkData.chainId && selectedTokenAddress === requestLinkData.tokenAddress) { | |
| await handleMatchingChainAndToken() | |
| } else { | |
| await handleCrossChainFulfillment() | |
| } | |
| } catch (error) { | |
| const errorString = utils.ErrorHandler(error) | |
| setErrorState({ | |
| showError: true, | |
| errorMessage: errorString, | |
| }) | |
| console.error('Error while submitting request link fulfillment:', error) | |
| } finally { | |
| setLoadingState('Idle') | |
| } | |
| } |
| try { | ||
| await switchNetworkUtil({ | ||
| chainId, | ||
| currentChainId: currentChain?.id, | ||
| setLoadingState, | ||
| switchChainAsync, | ||
| }) | ||
| console.log(`Switched to chain ${chainId}`) | ||
| } catch (error) { | ||
| console.error('Failed to switch network:', error) |
There was a problem hiding this comment.
this is great, it seems like you standardized the behavior. Thats nice! x
| try { | ||
| await switchNetworkUtil({ | ||
| chainId, | ||
| currentChainId: currentChain?.id, | ||
| setLoadingState, | ||
| switchChainAsync, | ||
| }) | ||
| console.log(`Switched to chain ${chainId}`) | ||
| } catch (error) { | ||
| console.error('Failed to switch network:', error) |
There was a problem hiding this comment.
idg this - why are you wrapping switchNetworkUtil in switchNetwork and adding an error log? whats the point?
| .trim() | ||
| } | ||
|
|
||
| export const switchNetwork = async ({ |
There was a problem hiding this comment.
Actionable comments posted: 1
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- src/components/Request/Pay/Views/Initial.view.tsx (6 hunks)
Additional comments not posted (2)
src/components/Request/Pay/Views/Initial.view.tsx (2)
26-30: LGTM!The destructuring of values from
useAccountandtokenSelectorContextlooks good. These values are likely used for handling the connection status, user address, current chain, and selected chain/token details for cross-chain transactions.
224-229: LGTM!The label provides clear information to the user about the flexibility of fulfilling the payment request with any token on any chain. The inclusion of the
TokenSelectorcomponent allows the user to easily select the desired token and chain for fulfillment.
| if (selectedChainID !== currentChain) { | ||
| await switchNetwork(selectedChainID) | ||
| } | ||
| try { | ||
| setErrorState({ showError: false, errorMessage: '' }) | ||
| if (!unsignedTx) return | ||
| await assertValues({ tokenValue: requestLinkData.tokenAmount }) | ||
| if (selectedChainID === requestLinkData.chainId && selectedTokenAddress === requestLinkData.tokenAddress) { | ||
| await assertValues({ tokenValue: requestLinkData.tokenAmount }) | ||
| setLoadingState('Sign in wallet') | ||
| const hash = await sendTransactions({ | ||
| preparedDepositTxs: { unsignedTxs: [unsignedTx] }, | ||
| feeOptions: undefined, | ||
| }) | ||
|
|
||
| setLoadingState('Sign in wallet') | ||
| setLoadingState('Executing transaction') | ||
|
|
||
| const hash = await sendTransactions({ | ||
| preparedDepositTxs: { unsignedTxs: [unsignedTx] }, | ||
| feeOptions: undefined, | ||
| }) | ||
| await peanut.submitRequestLinkFulfillment({ | ||
| chainId: requestLinkData.chainId, | ||
| hash: hash ?? '', | ||
| payerAddress: address ?? '', | ||
| link: requestLinkData.link, | ||
| apiUrl: '/api/proxy/patch/', | ||
| }) | ||
|
|
||
| setLoadingState('Executing transaction') | ||
| const currentDate = new Date().toISOString() | ||
| utils.saveRequestLinkFulfillmentToLocalStorage({ | ||
| details: { | ||
| ...requestLinkData, | ||
| destinationChainFulfillmentHash: hash ?? '', | ||
| createdAt: currentDate, | ||
| }, | ||
| link: requestLinkData.link, | ||
| }) | ||
|
|
||
| await peanut.submitRequestLinkFulfillment({ | ||
| chainId: requestLinkData.chainId, | ||
| hash: hash ?? '', | ||
| payerAddress: address ?? '', | ||
| link: requestLinkData.link, | ||
| apiUrl: '/api/proxy/patch/', | ||
| }) | ||
| setTransactionHash(hash ?? '') | ||
| onNext() | ||
| } else { | ||
| setLoadingState('Sign in wallet') | ||
|
|
||
| const currentDate = new Date().toISOString() | ||
| utils.saveRequestLinkFulfillmentToLocalStorage({ | ||
| details: { | ||
| ...requestLinkData, | ||
| destinationChainFulfillmentHash: hash ?? '', | ||
| createdAt: currentDate, | ||
| }, | ||
| link: requestLinkData.link, | ||
| }) | ||
| const xchainUnsignedTxs = await peanut.prepareXchainRequestFulfillmentTransaction({ | ||
| fromToken: selectedTokenAddress, | ||
| fromChainId: selectedChainID, | ||
| senderAddress: address ?? '', | ||
| link: requestLinkData.link, | ||
| squidRouterUrl: 'https://apiplus.squidrouter.com/v2/route', | ||
| apiUrl: '/api/proxy/get', | ||
| provider: await peanut.getDefaultProvider(selectedChainID), | ||
| tokenType: selectedTokenAddress === ADDRESS_ZERO ? EPeanutLinkType.native : EPeanutLinkType.erc20, | ||
| fromTokenDecimals: selectedTokenDecimals as number, | ||
| }) | ||
|
|
||
| const { unsignedTxs } = xchainUnsignedTxs | ||
| const hash = await sendTransactions({ | ||
| preparedDepositTxs: { unsignedTxs }, | ||
| feeOptions: undefined, | ||
| }) | ||
| setLoadingState('Executing transaction') | ||
|
|
||
| await peanut.submitRequestLinkFulfillment({ | ||
| chainId: requestLinkData.chainId, | ||
| hash: hash ?? '', | ||
| payerAddress: address ?? '', | ||
| link: requestLinkData.link, | ||
| apiUrl: '/api/proxy/patch/', | ||
| }) | ||
|
|
||
| setTransactionHash(hash ?? '') | ||
| onNext() | ||
| const currentDate = new Date().toISOString() | ||
| utils.saveRequestLinkFulfillmentToLocalStorage({ | ||
| details: { | ||
| ...requestLinkData, | ||
| destinationChainFulfillmentHash: hash ?? '', | ||
| createdAt: currentDate, | ||
| }, | ||
| link: requestLinkData.link, | ||
| }) | ||
|
|
||
| setTransactionHash(hash ?? '') | ||
| onNext() | ||
| } |
There was a problem hiding this comment.
Consider refactoring the handleOnNext function for better readability and maintainability.
The handleOnNext function is quite large and can be refactored for better readability and maintainability. Here are a few suggestions:
- Extract the logic for handling the case when the selected chain and token match the request data into a separate function (e.g.,
handleMatchingChainAndToken). - Extract the logic for handling the case when the selected chain and token do not match the request data into a separate function (e.g.,
handleCrossChainFulfillment). - Move the common logic for submitting the request link fulfillment and saving it to local storage into a separate function (e.g.,
submitAndSaveFulfillment).
There was a problem hiding this comment.
Actionable comments posted: 1
Outside diff range and nitpick comments (1)
src/components/utils/utils.ts (1)
3-14: LGTM with a suggestion!The
checkTransactionStatusfunction is implemented correctly and follows best practices for making API requests. The error handling ensures that any errors are caught and logged to the console.Consider adding type annotations for the response data to improve type safety and code readability. For example:
-export async function checkTransactionStatus(txHash: string): Promise<void> { +export async function checkTransactionStatus(txHash: string): Promise<TransactionStatusResponse> { try { const response = await axios.get('https://apiplus.squidrouter.com/v2/status', { params: { transactionId: txHash }, headers: { 'x-integrator-id': '11CBA45B-5EE9-4331-B146-48CCD7ED4C7C' }, }) - return response.data + return response.data as TransactionStatusResponse } catch (error) { console.error('Error fetching transaction status:', error) throw error } } +interface TransactionStatusResponse { + // Define the structure of the response data + // Example: + // squidTransactionStatus: string + // toChain: { + // chainData: { + // chainId: string + // } + // transactionId: string + // transactionUrl: string + // } +}
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (4)
- src/components/Claim/Link/Onchain/Success.view.tsx (2 hunks)
- src/components/Request/Pay/Views/Initial.view.tsx (5 hunks)
- src/components/Request/Pay/Views/Success.view.tsx (2 hunks)
- src/components/utils/utils.ts (1 hunks)
Additional context used
Biome
src/components/Request/Pay/Views/Initial.view.tsx
[error] 58-58: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with(lint/complexity/noUselessTernary)
Additional comments not posted (14)
src/components/Request/Pay/Views/Success.view.tsx (6)
5-7: LGTM!The imports are necessary for the component to function correctly and access shared state.
14-16: LGTM!The state variable is correctly typed and initialized to hold the transaction details for the destination chain.
17-17: LGTM!The constant variable is correctly constructed to provide a direct link to the Axelar transaction page.
19-19: LGTM!The
useContexthook is correctly used to access theselectedChainIDfrom thetokenSelectorContext.
21-25: LGTM!The
useEffecthook is correctly used to start the loop for checking the transaction status on component mount.
36-66: LGTM!The conditional rendering and display of transaction details with explorer links enhance the user experience and provide valuable information to the users.
src/components/Claim/Link/Onchain/Success.view.tsx (2)
9-9: LGTM!The import statement for the
loopUntilSuccessfunction is correct.
49-49: Verify theloopUntilSuccessfunction correctly sets the explorer URL.The changes to streamline the handling of transaction status updates by invoking the
loopUntilSuccessfunction with thesetExplorerUrlDestChainWithTxHashparameter look good. This refactoring improves modularity and maintainability of the codebase, aligning with the PR objective of integrating the UI with request links.Please ensure that the
loopUntilSuccessfunction correctly sets the explorer URL for the destination chain transaction hash. You can verify this by running the following script:Expect the
loopUntilSuccessfunction to contain logic for setting the explorer URL using thesetExplorerUrlDestChainWithTxHashfunction.Verification successful
Verification successful:
loopUntilSuccessfunction correctly sets the explorer URL.The
loopUntilSuccessfunction insrc/components/utils/utils.tscorrectly sets the explorer URL for the destination chain transaction hash using thesetExplorerUrlDestChainWithTxHashfunction. The logic ensures that the URL is set only when the transaction status is successful, aligning with the intended functionality.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify the `loopUntilSuccess` function sets the explorer URL for the destination chain transaction hash. # Test: Search for the `loopUntilSuccess` function implementation. ast-grep --lang typescript --pattern $'function loopUntilSuccess($_, $_) { $$$ }'Length of output: 2379
src/components/Request/Pay/Views/Initial.view.tsx (6)
31-31: LGTM!The integration with the
tokenSelectorContextallows the component to access the selected token and chain information, which is crucial for handling cross-chain transaction fulfillment and improving the user experience.
38-51: LGTM!The
createXChainUnsignedTxfunction encapsulates the logic for preparing cross-chain transactions, making the code more modular and reusable. The usage of the selected token and chain information ensures that the transactions are prepared based on the user's selection, enhancing the flexibility of the payment request fulfillment process.
53-67: LGTM!The
useEffecthook ensures that the transaction fee is re-estimated whenever the selected token changes, providing an up-to-date estimate to the user. The usage of thecreateXChainUnsignedTxfunction and thepeanut.calculateCrossChainTxFeemethod ensures that the transaction fee is accurately estimated based on the selected token and chain, improving the user experience and transparency.Tools
Biome
[error] 58-58: Unnecessary use of boolean literals in conditional expression.
Simplify your code by directly assigning the result without using a ternary operator.
If your goal is negation, you may use the logical NOT (!) or double NOT (!!) operator for clearer and concise code.
Check for more details about NOT operator.
Unsafe fix: Remove the conditional expression with(lint/complexity/noUselessTernary)
88-155: LGTM!The changes to the
handleOnNextfunction introduce a more flexible payment request fulfillment process, allowing users to fulfill the request with any token on any chain. The function handles both direct and cross-chain fulfillment scenarios, ensuring a seamless user experience. The usage of thecreateXChainUnsignedTxfunction and thepeanut.submitRequestLinkFulfillmentmethod ensures that the cross-chain fulfillment process is handled correctly, while the saving of the fulfillment details to local storage allows users to track their past fulfillments.
251-251: LGTM!The addition of the
TokenSelectorcomponent allows users to select the token and chain for fulfilling the payment request, enhancing the user experience by providing flexibility in the payment request fulfillment process.
253-279: LGTM!The changes to the transaction fee display logic ensure that the transaction fee is accurately displayed based on the selected token and chain. The usage of the
estimatedGasCostfor direct fulfillment and thetxFeefor cross-chain fulfillment provides transparency to the user. TheMoreInfocomponent is used effectively to provide additional information about the transaction fee, enhancing the user experience.
| export async function loopUntilSuccess( | ||
| txHash: string, | ||
| setExplorerUrlDestChainWithTxHash: (value: { transactionId: string; transactionUrl: string }) => void | ||
| ) { | ||
| let intervalId = setInterval(async () => { | ||
| const result = await checkTransactionStatus(txHash) | ||
|
|
||
| //@ts-ignore | ||
| if (result.squidTransactionStatus === 'success') { | ||
| //@ts-ignore | ||
| const explorerUrl = utils.getExplorerUrl(result.toChain.chainData.chainId.toString()) | ||
| if (explorerUrl) { | ||
| setExplorerUrlDestChainWithTxHash({ | ||
| //@ts-ignore | ||
| transactionUrl: explorerUrl + '/tx/' + result.toChain.transactionId, | ||
| //@ts-ignore | ||
| transactionId: result.toChain.transactionId, | ||
| }) | ||
| } else { | ||
| setExplorerUrlDestChainWithTxHash({ | ||
| //@ts-ignore | ||
| transactionUrl: result.toChain.transactionUrl, | ||
| //@ts-ignore | ||
| transactionId: result.toChain.transactionId, | ||
| }) | ||
| } | ||
| clearInterval(intervalId) | ||
| } else { | ||
| console.log('Checking status again...') | ||
| } | ||
| }, 5000) | ||
| } |
There was a problem hiding this comment.
Approved with suggestions for improvement!
The loopUntilSuccess function implements a polling mechanism to check the transaction status until it is successful. The use of setInterval is appropriate for periodically checking the status, and the function correctly updates the explorer URL and transaction ID using the callback function when the status is successful.
Consider the following suggestions to improve the code:
-
Properly type the
resultobject and its properties to avoid using@ts-ignorecomments. Define an interface for theresultobject based on the expected structure of the API response. -
Add a maximum number of retries or a timeout to prevent infinite polling in case of issues. For example, you can introduce a
maxRetriesparameter and keep track of the number of retries. If the maximum retries are exceeded, clear the interval and handle the failure scenario appropriately.
Here's an example of how you can refactor the code:
-export async function loopUntilSuccess(
+export async function loopUntilSuccess(
txHash: string,
- setExplorerUrlDestChainWithTxHash: (value: { transactionId: string; transactionUrl: string }) => void
+ setExplorerUrlDestChainWithTxHash: (value: { transactionId: string; transactionUrl: string }) => void,
+ maxRetries: number = 10
) {
+ let retryCount = 0
let intervalId = setInterval(async () => {
const result = await checkTransactionStatus(txHash)
- //@ts-ignore
- if (result.squidTransactionStatus === 'success') {
- //@ts-ignore
- const explorerUrl = utils.getExplorerUrl(result.toChain.chainData.chainId.toString())
+ if (result.squidTransactionStatus === 'success') {
+ const explorerUrl = utils.getExplorerUrl(result.toChain.chainData.chainId.toString())
if (explorerUrl) {
setExplorerUrlDestChainWithTxHash({
- //@ts-ignore
- transactionUrl: explorerUrl + '/tx/' + result.toChain.transactionId,
- //@ts-ignore
- transactionId: result.toChain.transactionId,
+ transactionUrl: explorerUrl + '/tx/' + result.toChain.transactionId,
+ transactionId: result.toChain.transactionId,
})
} else {
setExplorerUrlDestChainWithTxHash({
- //@ts-ignore
- transactionUrl: result.toChain.transactionUrl,
- //@ts-ignore
- transactionId: result.toChain.transactionId,
+ transactionUrl: result.toChain.transactionUrl,
+ transactionId: result.toChain.transactionId,
})
}
clearInterval(intervalId)
+ } else if (retryCount >= maxRetries) {
+ console.error('Maximum retries exceeded. Transaction status check failed.')
+ clearInterval(intervalId)
+ // Handle the failure scenario, e.g., show an error message to the user
} else {
console.log('Checking status again...')
+ retryCount++
}
}, 5000)
}
+interface TransactionStatusResult {
+ squidTransactionStatus: string
+ toChain: {
+ chainData: {
+ chainId: string
+ }
+ transactionId: string
+ transactionUrl: 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.
| export async function loopUntilSuccess( | |
| txHash: string, | |
| setExplorerUrlDestChainWithTxHash: (value: { transactionId: string; transactionUrl: string }) => void | |
| ) { | |
| let intervalId = setInterval(async () => { | |
| const result = await checkTransactionStatus(txHash) | |
| //@ts-ignore | |
| if (result.squidTransactionStatus === 'success') { | |
| //@ts-ignore | |
| const explorerUrl = utils.getExplorerUrl(result.toChain.chainData.chainId.toString()) | |
| if (explorerUrl) { | |
| setExplorerUrlDestChainWithTxHash({ | |
| //@ts-ignore | |
| transactionUrl: explorerUrl + '/tx/' + result.toChain.transactionId, | |
| //@ts-ignore | |
| transactionId: result.toChain.transactionId, | |
| }) | |
| } else { | |
| setExplorerUrlDestChainWithTxHash({ | |
| //@ts-ignore | |
| transactionUrl: result.toChain.transactionUrl, | |
| //@ts-ignore | |
| transactionId: result.toChain.transactionId, | |
| }) | |
| } | |
| clearInterval(intervalId) | |
| } else { | |
| console.log('Checking status again...') | |
| } | |
| }, 5000) | |
| } | |
| export async function loopUntilSuccess( | |
| txHash: string, | |
| setExplorerUrlDestChainWithTxHash: (value: { transactionId: string; transactionUrl: string }) => void, | |
| maxRetries: number = 10 | |
| ) { | |
| let retryCount = 0 | |
| let intervalId = setInterval(async () => { | |
| const result = await checkTransactionStatus(txHash) | |
| if (result.squidTransactionStatus === 'success') { | |
| const explorerUrl = utils.getExplorerUrl(result.toChain.chainData.chainId.toString()) | |
| if (explorerUrl) { | |
| setExplorerUrlDestChainWithTxHash({ | |
| transactionUrl: explorerUrl + '/tx/' + result.toChain.transactionId, | |
| transactionId: result.toChain.transactionId, | |
| }) | |
| } else { | |
| setExplorerUrlDestChainWithTxHash({ | |
| transactionUrl: result.toChain.transactionUrl, | |
| transactionId: result.toChain.transactionId, | |
| }) | |
| } | |
| clearInterval(intervalId) | |
| } else if (retryCount >= maxRetries) { | |
| console.error('Maximum retries exceeded. Transaction status check failed.') | |
| clearInterval(intervalId) | |
| // Handle the failure scenario, e.g., show an error message to the user | |
| } else { | |
| console.log('Checking status again...') | |
| retryCount++ | |
| } | |
| }, 5000) | |
| } | |
| interface TransactionStatusResult { | |
| squidTransactionStatus: string | |
| toChain: { | |
| chainData: { | |
| chainId: string | |
| } | |
| transactionId: string | |
| transactionUrl: string | |
| } | |
| } |
Summary by CodeRabbit
New Features
TokenSelectorcomponent for enhanced token selection during payment requests.Chores