Conversation
…nd destination in createIntent
…details and improve token handling in payment flow
… adjust payment amount formatting
…n up unused imports in IntentQuoteDisplayStep
|
|
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
|
@shunkakinoki is attempting to deploy a commit to the Foundry development Team on Vercel. A member of the Team first needs to authorize it. |
Reviewer's GuideRefactors demo anypay configuration into a shared config module, updates the pay flow to use configurable PAY* constants (now targeting Arbitrum USDC), adds same-chain/same-token validation and handling in the SDK, ensures the wallet client is on the correct chain before sending origin transactions, and extends the send widget to pass token metadata and fee updates through to prepareSend. Sequence diagram for intent creation with same-chain/same-token validationsequenceDiagram
actor User
participant SendForm
participant AnyPayHook_useAnyPay as AnyPayHook_useAnyPay
participant SequenceAPI as SequenceAPI
User->>SendForm: Fill send form and submit
SendForm->>AnyPayHook_useAnyPay: createIntentMutation.mutate(args)
AnyPayHook_useAnyPay->>AnyPayHook_useAnyPay: Validate account.address exists
AnyPayHook_useAnyPay->>AnyPayHook_useAnyPay: Compare args.originChainId and args.destinationChainId
AnyPayHook_useAnyPay->>AnyPayHook_useAnyPay: Compare args.originTokenAddress and args.destinationTokenAddress
alt Same chain and same token
AnyPayHook_useAnyPay-->>SendForm: Throw Error("The same token cannot be used as both the source and destination token.")
SendForm-->>User: Show validation error
else Different chain or different token
AnyPayHook_useAnyPay->>SequenceAPI: getIntentCallsPayloads(args)
SequenceAPI-->>AnyPayHook_useAnyPay: intentCallsPayloads
AnyPayHook_useAnyPay-->>SendForm: Success with intentCallsPayloads
SendForm-->>User: Show quote and continue flow
end
Sequence diagram for prepareSend and origin transaction chain switchingsequenceDiagram
actor User
participant SendForm
participant AnyPaySDK as AnyPaySDK_prepareSend
participant Intents as Intents_sendOriginTransaction
participant WalletClient
participant Blockchain
User->>SendForm: Confirm send
SendForm->>AnyPaySDK: prepareSend(options)
AnyPaySDK->>AnyPaySDK: Read originTokenAddress, destinationTokenAddress, destinationTokenAmount, originTokenAmount
alt Same origin and destination token
AnyPaySDK->>AnyPaySDK: Set originTokenAmount = destinationTokenAmount
else Different tokens
AnyPaySDK->>AnyPaySDK: Keep originTokenAmount unchanged
end
AnyPaySDK->>Intents: sendOriginTransaction(wallet, client, originParams)
Intents->>WalletClient: getChainId()
WalletClient-->>Intents: currentChainId
alt currentChainId != originParams.chain.id
Intents->>WalletClient: switchChain(id = originParams.chain.id)
WalletClient-->>Intents: Switched to origin chain
else Already on correct chain
Intents->>Intents: Skip chain switch
end
Intents->>WalletClient: sendTransaction(originParams)
WalletClient->>Blockchain: Broadcast transaction
Blockchain-->>WalletClient: txHash
WalletClient-->>Intents: txHash
Intents-->>AnyPaySDK: txHash
AnyPaySDK-->>SendForm: txHash
SendForm-->>User: Show transaction submitted
Updated class diagram for AnyPay SDK send flow and shared configclassDiagram
class Config {
<<module>>
+Hex MOCK_TRANSFER_DATA
+number MOCK_CHAIN_ID
+string MOCK_CONTRACT_ADDRESS
+string MOCK_TOKEN_ADDRESS
+string MOCK_TOKEN_AMOUNT
+number PAY_CHAIN_ID
+string BASE_USDC_PAY_TOKEN_ADDRESS
+string ARB_USDC_PAY_TOKEN_ADDRESS
+string PAY_TOKEN_ADDRESS
+string PAY_TOKEN_SYMBOL
+number PAY_TOKEN_DECIMALS
+string PAY_RECIPIENT_ADDRESS
+bigint PAY_AMOUNT
+string PAY_AMOUNT_FORMATTED
+string PAY_DISPLAY_TEXT
}
class UseAnyPayHook {
<<hook>>
+createIntentMutation(args)
}
class GetIntentCallsPayloadsArgs {
+number originChainId
+number destinationChainId
+string originTokenAddress
+string destinationTokenAddress
}
class SendOptions {
+number originChainId
+string originTokenAddress
+string originTokenAmount
+number destinationChainId
+string recipient
+string destinationTokenAddress
+string destinationTokenAmount
+string destinationTokenSymbol
+string sequenceApiKey
+string fee
+WalletClient client
+any apiClient
+any originRelayer
}
class AnyPaySDK {
<<module>>
+prepareSend(options) Promise~void~
}
class IntentsModule {
<<module>>
+sendOriginTransaction(wallet, client, originParams) Promise~string~
}
class WalletClient {
+getChainId() Promise~number~
+switchChain(params) Promise~void~
+sendTransaction(tx) Promise~string~
}
class SendFormComponent {
<<ReactComponent>>
+onSubmit()
-buildSendOptions() SendOptions
}
class IntentQuoteDisplayStepComponent {
<<ReactComponent>>
+renderPayIntentSummary(intentCallsPayloads, selectedToken)
}
class ChooseActionStepComponent {
<<ReactComponent>>
+renderPayButton()
}
UseAnyPayHook --> GetIntentCallsPayloadsArgs : uses
AnyPaySDK --> SendOptions : uses
IntentsModule --> WalletClient : uses
SendFormComponent --> AnyPaySDK : calls_prepareSend
SendFormComponent --> UseAnyPayHook : calls_createIntentMutation
SendFormComponent --> Config : reads_PAY_constants
IntentQuoteDisplayStepComponent --> Config : reads_PAY_and_MOCK_constants
ChooseActionStepComponent --> Config : reads_PAY_CHAIN_ID_and_PAY_DISPLAY_TEXT
UseAnyPayHook --> Config : uses_MOCK_and_PAY_defaults
AnyPaySDK --> IntentsModule : calls_sendOriginTransaction
UseAnyPayHook --> "1" WalletClient : indirectly via caller
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly refactors the demo application by centralizing configuration constants, making the codebase more organized and easier to manage. It also enhances the robustness of the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- In
useAnyPay'screateIntentMutation, the new guard only blocks whenoriginChainId === destinationChainIdand tokens match, but the error message claims the same token cannot be used as both source and destination in general; either relax the condition or update the message to reflect the same-chain-only restriction. - In
sendOriginTransactionyou callclient.switchChain, butWalletClienttypically exposes chain switching via theswitchChainaction helper rather than as a method; consider using the action (switchChain(client, { id })) or a typed client that actually supports this method to avoid runtime/type issues. - You added
destinationTokenSymboltoSendOptionsand pass it intoprepareSend, but it isn’t used in the shown logic; if it’s not consumed downstream, remove it to keep the send API minimal, or wire it through where it’s intended to be used.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `useAnyPay`'s `createIntentMutation`, the new guard only blocks when `originChainId === destinationChainId` and tokens match, but the error message claims the same token cannot be used as both source and destination in general; either relax the condition or update the message to reflect the same-chain-only restriction.
- In `sendOriginTransaction` you call `client.switchChain`, but `WalletClient` typically exposes chain switching via the `switchChain` action helper rather than as a method; consider using the action (`switchChain(client, { id })`) or a typed client that actually supports this method to avoid runtime/type issues.
- You added `destinationTokenSymbol` to `SendOptions` and pass it into `prepareSend`, but it isn’t used in the shown logic; if it’s not consumed downstream, remove it to keep the send API minimal, or wire it through where it’s intended to be used.
## Individual Comments
### Comment 1
<location path="packages/wallet/anypay-sdk/src/anypay.ts" line_range="332-336" />
<code_context>
// TODO: Add type for args
const createIntentMutation = useMutation<GetIntentCallsPayloadsReturn, Error, GetIntentCallsPayloadsArgs>({
mutationFn: async (args: GetIntentCallsPayloadsArgs) => {
+ if (
+ args.originChainId === args.destinationChainId &&
+ isAddressEqual(Address.from(args.originTokenAddress), Address.from(args.destinationTokenAddress))
+ ) {
+ throw new Error('The same token cannot be used as both the source and destination token.')
+ }
if (!account.address) {
</code_context>
<issue_to_address>
**issue:** Align the validation condition with the error message and consider whether same-chain/same-token flows should be allowed.
The guard currently rejects only the case where both chain ID and token address match. Please clarify the intent:
- If the rule is “no same token anywhere”, the chain ID check is too restrictive.
- If the rule is “no exact same chain+token no-op”, the error message should mention the chain or “no-op” nature.
Also note `Address.from(...)` will throw on malformed input. If invalid addresses are possible here, either validate them first or compare raw strings before normalizing. Consider extracting this predicate into a shared helper so other call sites can reuse the same logic and stay consistent.
</issue_to_address>
### Comment 2
<location path="packages/wallet/anypay-sdk/src/widget/components/SendForm.tsx" line_range="187" />
<code_context>
account,
chain: getChainConfig(selectedToken.chainId),
- transport: custom(window.ethereum),
+ transport: custom((window as any).ethereum),
})
</code_context>
<issue_to_address>
**suggestion:** Add a defensive check around `(window as any).ethereum` before constructing the wallet client.
The TS cast removes the type error but still assumes `ethereum` always exists at runtime. In contexts without an injected provider (or before it’s ready), `custom((window as any).ethereum)` will throw. Consider guarding and surfacing a clear error/fallback:
```ts
const provider = (window as any).ethereum
if (!provider) {
// handle missing provider (e.g., user-facing error or fallback)
}
transport: custom(provider),
```
Suggested implementation:
```typescript
const provider = (window as any)?.ethereum
if (!provider) {
// TODO: handle missing provider (e.g. show a user-facing error, disable the form, or fallback)
throw new Error('Ethereum provider is not available in this context.')
}
const client = createWalletClient({
account,
chain: getChainConfig(selectedToken.chainId),
transport: custom(provider),
})
```
You may want to replace the `throw new Error(...)` with UI-specific handling consistent with the rest of `SendForm.tsx` (for example: setting an error state, showing a toast/snackbar, or disabling submission when no provider is present).
</issue_to_address>
### Comment 3
<location path="packages/wallet/anypay-sdk/src/intents.ts" line_range="154-156" />
<code_context>
client: WalletClient,
originParams: SendOriginCallTxArgs,
): Promise<`0x${string}`> {
+ const chainId = await client.getChainId()
+ if (chainId !== originParams.chain.id) {
+ await client.switchChain({ id: originParams.chain.id })
+ }
+
</code_context>
<issue_to_address>
**suggestion:** Consider handling `switchChain` failures explicitly for clearer user feedback.
If `switchChain` is rejected by the user or unsupported by the wallet, this will currently surface as a generic viem error. Consider wrapping the call in a try/catch and rethrowing clearer errors (e.g., "User rejected network switch" vs "Wallet does not support network switching") so callers can distinguish switch failures from transaction failures.
Suggested implementation:
```typescript
const chainId = await client.getChainId()
if (chainId !== originParams.chain.id) {
try {
await client.switchChain({ id: originParams.chain.id })
} catch (error) {
// viem-specific error handling for clearer UX
if (error instanceof UserRejectedRequestError) {
throw new Error('User rejected network switch')
}
if (error instanceof SwitchChainError) {
throw new Error('Wallet does not support network switching')
}
// Fallback: rethrow the original error so callers can still inspect it
throw error
}
}
const hash = await client.sendTransaction({
```
You’ll also need to ensure the viem error types are imported at the top of `packages/wallet/anypay-sdk/src/intents.ts`, e.g.:
- Add `UserRejectedRequestError` and `SwitchChainError` to your existing `viem` imports, or create a new import:
`import { SwitchChainError, UserRejectedRequestError } from 'viem'`
If the project uses a custom error abstraction, you may want to throw those custom error classes instead of `new Error(...)` to keep error handling consistent with the rest of the codebase.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Code Review
This pull request refactors the demo app to use a centralized configuration for constants and adds several enhancements, including intent validation and automatic chain switching. My review focuses on improving maintainability by dynamically generating display values instead of hardcoding them in the configuration, and enhancing TypeScript type safety when interacting with browser-specific objects like window.ethereum. These suggestions aim to make the code more robust and easier to maintain.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@Mergifyio update |
|
@Mergifyio refresh |
☑️ Nothing to do, the required conditions are not metDetails
|
✅ Pull request refreshed |
Summary by Sourcery
Configure the demo anypay app and SDK to support a configurable same-chain USDC pay intent on Arbitrum, centralizing mock and pay constants, tightening intent validation, and improving wallet/send UX.
New Features:
Bug Fixes:
Enhancements: