Skip to content

feat: allow EIP7702 wallets to batch transactions#7844

Open
kernelwhisperer wants to merge 1 commit into
developfrom
feat/7702-batch-support
Open

feat: allow EIP7702 wallets to batch transactions#7844
kernelwhisperer wants to merge 1 commit into
developfrom
feat/7702-batch-support

Conversation

@kernelwhisperer

@kernelwhisperer kernelwhisperer commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements https://linear.app/cowswap/issue/FE-265/batch-support-for-eoas-and-eip7702-delegated-eoas
Fixes #7661

Adds EIP-7702 batching capability detection while keeping TWAP restricted to Safe wallets.

  • Recognizes EIP-5792 atomic.status values supported and ready
  • Preserves legacy atomicBatch.supported detection
  • Removes the mobile WalletConnect capability-check bypass
  • For TWAP, it keeps Safe App support implicit; Safe via WalletConnect must report batching capabilities

To Test

  1. Connect a MetaMask account without Smart account set up
  • Enable in Settings -> Transactions -> Smart account requests from dapps (should be on by default)
  • TWAP remains disabled & the unsupported-wallet warning appears
  • Swapping can batch approve & swap
Image
Screencast.From.2026-07-10.10-58-16.webm
  1. Connect a MetaMask account with Smart account set up
  • Enable in Settings -> Transactions -> Smart account requests from dapps (should be on by default)
  • Enable in Accounts -> Smart Accounts -> Enable for a network
  • TWAP remains disabled & the unsupported-wallet warning appears
  • Swapping can batch approve & swap
Screencast.From.2026-07-10.10-59-24.webm
  1. Connect a SafeWallet through the Safe App
  • TWAP is available
  • No unsupported-wallet warning appears
  • Batching works in SWAP
  1. Connect a SafeWallet through WalletConnect
  • TWAP is available
  • No unsupported-wallet warning appears
  • Batching works in SWAP
  1. Connect an inactive (not yet deployed) SafeWallet through WalletConnect
  • TWAP is available
  • No unsupported-wallet warning appears
  • Batching works in SWAP
  1. Connect an Ambire wallet
  • TWAP remains disabled
  • The unsupported-wallet warning appears
  • Batching works in SWAP
  1. Connect a Rabby EOA wallet
  • Batching does not work in SWAP

Background

Capability detection is independent from TWAP wallet eligibility: this change recognizes EIP-7702 batching, but TWAP remains restricted to Safe wallets.

Summary by CodeRabbit

  • Bug Fixes

    • Improved TWAP wallet compatibility detection, including clearer handling for unsupported wallets.
    • Added a disabled “Unsupported wallet” action when the connected wallet cannot use TWAP.
    • Improved Safe wallet and WalletConnect capability detection, including atomic batch support.
    • Prevented unsupported fallback capabilities from being used when chain-specific data is unavailable.
  • Tests

    • Added coverage for unsupported wallets and transaction bundling scenarios.

@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cowfi Ready Ready Preview Jul 14, 2026 11:13am
explorer-dev Ready Ready Preview Jul 14, 2026 11:13am
storybook Ready Ready Preview Jul 14, 2026 11:13am
swap-dev Ready Ready Preview Jul 14, 2026 11:13am
widget-configurator Ready Ready Preview Jul 14, 2026 11:13am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
cosmos Ignored Ignored Jul 14, 2026 11:13am
sdk-tools Ignored Ignored Preview Jul 14, 2026 11:13am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

TWAP now derives wallet support from Safe metadata and wallet capabilities. Unsupported wallets receive a dedicated form state, disabled action button, warning, and analytics event. Capability resolution no longer falls back to arbitrary chains, and atomic-batch support uses capability status fields.

Changes

TWAP wallet support

Layer / File(s) Summary
Wallet capability resolution
libs/wallet/src/api/state/walletCapabilitiesAtom.ts, libs/wallet/src/api/state/walletCapabilitiesAtom.test.ts
Capability lookup requires a matching chain and atomic-batch support is derived from Safe metadata and capability responses.
TWAP form state derivation
apps/cowswap-frontend/src/modules/twap/hooks/useTwapFormState.ts, apps/cowswap-frontend/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.*
TWAP accepts tri-state wallet support, returns WALLET_NOT_SUPPORTED, and tests the new state alongside transaction-bundling support.
Warnings and analytics wiring
apps/cowswap-frontend/src/modules/twap/containers/TwapFormWarnings/*, apps/cowswap-frontend/src/modules/twap/containers/TwapFormWidget/index.tsx, apps/cowswap-frontend/src/modules/twap/pure/PrimaryActionButton/index.tsx, apps/cowswap-frontend/src/modules/twap/index.ts, apps/cowswap-frontend/src/locales/en-US.po
Unsupported wallets render the warning and disabled action, update analytics handling, and expose the related translation reference.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WalletMetadata
  participant useTwapFormState
  participant getTwapFormState
  participant TwapFormWarnings
  participant PrimaryActionButton
  WalletMetadata->>useTwapFormState: provide Safe wallet metadata
  useTwapFormState->>getTwapFormState: pass wallet and bundling support
  getTwapFormState-->>TwapFormWarnings: return WALLET_NOT_SUPPORTED
  getTwapFormState-->>PrimaryActionButton: return WALLET_NOT_SUPPORTED
  TwapFormWarnings-->>PrimaryActionButton: render unsupported wallet warning and disabled action
Loading

Possibly related PRs

Suggested reviewers: danziger, limitofzero, shoom3301, fairlighteth

Poem

I’m a rabbit with a wallet in tow,
Now unsupported states clearly show.
No more loading in the night,
The button says what isn’t right.
Capabilities guide the way—
Hop, review, and ship today!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: enabling EIP-7702 wallets to batch transactions while adjusting TWAP wallet support logic.
Description check ✅ Passed The PR description follows the template with Summary, To Test, and Background sections, and it includes the linked issue and test notes.
Linked Issues check ✅ Passed The changes add a TWAP unsupported-wallet state and update wallet capability detection, which addresses the loading-state bug in #7661.
Out of Scope Changes check ✅ Passed The touched files and tests all support wallet capability detection or TWAP wallet eligibility, with no clear unrelated changes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/7702-batch-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 10, 2026

Copy link
Copy Markdown

Deploying explorer-dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0eb7628
Status: ✅  Deploy successful!
Preview URL: https://14cdcc4c.explorer-dev-dxz.pages.dev
Branch Preview URL: https://feat-7702-batch-support.explorer-dev-dxz.pages.dev

View logs

* WalletConnect in mobile browsers initiates a request with confirmation to the wallet
* to get the capabilities. It breaks the flow with perpetual requests.
*/
export async function getShouldSkipCapabilitiesCheck(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This function was removed in #7742 and replaced with a 5s timeout. It was reintroduced during conflict resolution.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Assuming all the test scenarios are ok, I'm fine with it. For me, when resolving some of the conflicts I found around this, it was not working as expected in some scenarios. But if we are going to check for batching support for EOAs then this is probably not relevant anymore anyway.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 10, 2026

Copy link
Copy Markdown

Deploying swap-dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0eb7628
Status: ✅  Deploy successful!
Preview URL: https://cb4b7b83.swap-dev-5u6.pages.dev
Branch Preview URL: https://feat-7702-batch-support.swap-dev-5u6.pages.dev

View logs

@linear-code

linear-code Bot commented Jul 10, 2026

Copy link
Copy Markdown

FE-265


logWallet.debug('Fetching wallet capabilities', { account, chainId })

// TODO remove this completely: it doesn't resolve capabilities for no wallet: MM, Safe, Ambire.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

From my testing wagmi is useless, it never resolves capabilities...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in #7847, and removed the fallback

@kernelwhisperer
kernelwhisperer marked this pull request as ready for review July 10, 2026 12:16
@kernelwhisperer
kernelwhisperer requested review from a team July 10, 2026 12:18
@kernelwhisperer kernelwhisperer self-assigned this Jul 10, 2026
@kernelwhisperer kernelwhisperer changed the title feat: allow 7702 wallet to batch support feat: allow EIP7702 wallets to batch transactions Jul 10, 2026

@elena-zh elena-zh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey @kernelwhisperer , thank you.

Base smart account is working perfectly fine now.

What is not working/not working as expected:

  1. Safe+WC: bundling stopped working
Image
  1. MM smart account: now it does not use gasless transactions for placing orders with permittable tokens
Image
  1. MM+Smart account: impossible to place a ETH-flow order without wrapping
Image

4 MM+regular account (not smart one): impossible to sign a permit

Image
  1. MM+ regular account on Avalanche, Plasma, Ink (chains that do not support smart accounts): impossible to place an order with a gasless permit inside
Image

Could you please review this issues and fix them?

@fairlighteth
fairlighteth self-requested a review July 10, 2026 15:02

@fairlighteth fairlighteth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ AI Review (Codex GPT-5, worked 25m): atomic batching can partially execute or use wrong-chain capabilities

Finding: [BLOCKING] Require atomic execution for ready wallets

  • Location: libs/wallet/src/api/state/walletCapabilitiesAtom.ts:177 and libs/wallet/src/api/hooks/useSendBatchTransactions.ts:36
  • This one is important: the PR accepts atomic.status === 'ready', but the sender omits forceAtomic.
  • viem defaults forceAtomic to false, producing atomicRequired: false. EIP-5792 then permits sequential execution and does not require a ready wallet to upgrade.
  • Approve/presign and wrap/approve/presign flows can partially execute, leaving allowances or wrapped funds behind, or activating a posted order after token preparation failed.
  • Security classification: verified-vulnerability, P2 (Medium). A targeted viem reproduction captured atomicRequired as [false, true] without and with forceAtomic.

Suggested fix

  • Pass forceAtomic: true from useSendBatchTransactions.
  • Add a boundary test asserting the resulting wallet_sendCalls.atomicRequired, including upgrade rejection and unsupported atomicity.

Finding: [BLOCKING] Do not reuse another chain's batching capability

  • Location: libs/wallet/src/api/state/walletCapabilitiesAtom.ts:35
  • resolveCapabilitiesForChain returns the first response entry when the active chain is absent. EIP-5792 states that atomic support is chain-specific and an absent chain means unsupported.
  • With ready now accepted, a Mainnet entry can incorrectly enable batching on Avalanche, Plasma, or Ink.
  • Commit 171de9b93 previously restricted this fallback to confirmed Safe-via-WalletConnect connections specifically to prevent non-Safe cross-chain false positives.

Suggested fix

  • Require the exact numeric/hex active-chain key for non-Safe wallets. Preserve a Safe-WC fallback only if it remains necessary.
  • Replace the current mismatched-chain test with assertions that non-Safe batching stays disabled.

Finding: [NON-BLOCKING] Keep batching-incompatible Safes out of compatible analytics

  • Location: apps/cowswap-frontend/src/modules/twap/containers/TwapFormWidget/index.tsx:132
  • The warning handles both unsupported states, but analytics now checks only WALLET_NOT_SUPPORTED.
  • A Safe-WC connection with failed capability detection remains TX_BUNDLING_NOT_SUPPORTED, then falls through to compatible or safe-that-could-be-converted even though TWAP is disabled.

Suggested fix

  • Handle both unsupported states before the fallback-handler branches, or add a distinct batching-unavailable event.
  • Add effect-level coverage for a Safe with batching disabled.
Review scope and related context
  • DeepSec PR mode ran against the exact PR file list: run 20260710151419-a335c60ae97dff7f, exit 1, artifact /tmp/deepsec-comment-pr-7844-review-2055455.md.
  • DeepSec confirmed the wrong-chain fallback. Its other two findings were pre-existing behavior outside this PR's changes.
  • The existing changes-requested review already covers Safe-WC, permit, gasless, ETH-flow, and unsupported-chain symptoms; those are not repeated here.
  • Targeted verification passed: 29 wallet tests, 4 TWAP tests, and changed-file ESLint.
  • The failing Cypress cases reproduce identically on the base branch and develop, so they are not attributable to this PR.
🤖 Prompt for AI agents
Verify these findings against PR 7844's current head.

1. Require atomic execution when `ready` enables batching and test the final wallet_sendCalls payload.
2. Reject non-Safe capability responses that omit the active chain.
3. Keep TX_BUNDLING_NOT_SUPPORTED out of compatible TWAP analytics.

Keep changes focused and rerun the wallet capability and TWAP tests.

Generated using the pr-review skill from the CoW Protocol skills repo.
Generated using the security-review skill from the CoW Protocol skills repo.

@kernelwhisperer

kernelwhisperer commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author
  1. Safe+WC: bundling stopped working

This one is supposed to be intermittent, that's why we have the "Use Safe web app" message.
For me, it Safe+WC always works. Can you confirm that it usually works, for you?

  1. MM smart account: now it does not use gasless transactions for placing orders with permittable tokens
  2. MM+Smart account: impossible to place a ETH-flow order without wrapping

I understand, but this is the flow for smart contracts, like Safe. I wonder, why this special behavior? Would it also make sense for Safes to support this behavior? Would like to clarify the use cases before implementing.

  1. MM+regular account (not smart one): impossible to sign a permit
  2. MM+ regular account on Avalanche, Plasma, Ink (chains that do not support smart accounts): impossible to place an order with a gasless permit inside

There was a bug, can you try again? @elena-zh

@shoom3301

Copy link
Copy Markdown
Collaborator

@kernelwhisperer tests are red

* Safe WC returns EIP-5792 capabilities keyed by hex chain id (e.g. "0xaa36a7")
* while walletInfoAtom.chainId is numeric (e.g. 11155111). Numeric lookup alone misses them.
*/
export function resolveCapabilitiesForChain(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note: This whole function is gone in #7847 (review) anyway.

// or error. So, if we cannot check, then we must be false,
// not null (as some components/functions like `validateTradeForm` treat `null` as loading):
if (!walletCapabilities) return false
if (walletCapabilities === null) return false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Then maybe walletCapabilities could return false or a hardcoded { atomic: { status: false }} or similar? I think it would be better to be able to distinguish loading from not supported (either because it's actually not supported or because we got some kind of empty response / error). Otherwise, this will probably trigger those annoying UI flickers we have all over the app, as it will always briefly show the UI branches for unsupported batching, and then switch to the supported ones once/if wallet capabilities resolve positively.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I also dislike that null is used for both error state and loading state. I will not address it in this PR though.

@kernelwhisperer
kernelwhisperer force-pushed the refactor/wallet-capabilities branch from 5a1c3f9 to bd984ee Compare July 14, 2026 10:23
@elena-zh

Copy link
Copy Markdown
Contributor

Hey @kernelwhisperer , thanks for the reply.

This one is supposed to be intermittent, that's why we have the "Use Safe web app" message.

Not exactly: we show "Use Safe web app" message in case the app is connected to Safe+Rabby (it does not support bundling), and when connected to Safe's deprecated mobile app in iOS (also does not support bundling). In all the rest cases this message should not appear.

I've retested the PR, I still see 'Unsupported wallet' when I'm connected to Safe app (web app)using WC
image

and this is what I see on Develop for the same conditions
image

MM smart account: now it does not use gasless transactions for placing orders with permittable tokens
MM+Smart account: impossible to place a ETH-flow order without wrapping

I understand, but this is the flow for smart contracts, like Safe. I wonder, why this special behavior? Would it also make sense for Safes to support this behavior? Would like to clarify the use cases before implementing.

Not sure if I get you correctly here.

But I will try to answer.

  1. if you're asking why Safe does not support ETH-flow, I think, @shoom3301 or @alfetopito are the best persons to answer you. I also had this question here
  2. If you're asking why MM should support gasless TX even being converted to smart accounts, then the answer is the next one: MM started their campaign by converting users' account to smart ones without a proper explanation, so after users did this, they did not get why their orders placement and all transactions became not-gasless. That's why there was a decision taken to treat these accounts as EOAs. Here is the thread for this

MM+regular account (not smart one): impossible to sign a permit
MM+ regular account on Avalanche, Plasma, Ink (chains that do not support smart accounts): impossible to place an order with a gasless permit inside

There was a bug, can you try again?

Permits stopped working at all when connected to MM now. :(
image

Another issue that I've noticed:
When I connected to a MM smart account now, it is impossible to change a network in a sell token selector to specify a cross-chain swap:
image

kernelwhisperer added a commit that referenced this pull request Jul 14, 2026
# Summary

1. Improve logging
2. Delete PromiseWithTimeout
3. Refactor withTimeout which is used by the explorer

# To Test

This PR doesn't really change any logic, just a refactor.

1. Connect with a safe wallet

- [ ] Batching works in swap
- [ ] TWAP support is detected

# Background

In the next PR (#7844) I will implement
https://linear.app/cowswap/issue/FE-265/batch-support-for-eoas-and-eip7702-delegated-eoas

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Summary by CodeRabbit

- **New Features**
- Improved wallet capability detection with clearer timeout handling and
legacy fallback.
- **Bug Fixes**
- Wallet capability fetch now returns `null` when providers fail or time
out.
  - Improved timeout handling to avoid lingering timers.
- **Refactor**
- Standardized timeout handling to use a typed, options-based
`withTimeout` API and `TimeoutError`.
  - Expanded environment-specific timeout messages for requests.
- **Chores**
- Removed the old `PromiseWithTimeout` utility export and updated
related tests.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Base automatically changed from refactor/wallet-capabilities to develop July 14, 2026 10:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/cowswap-frontend/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx (1)

51-56: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Ensure hierarchical resolution of loading states to avoid UI flicker.

Currently, if isTxBundlingSupported resolves to false while isWalletSupported is still loading (null), the function will prematurely return TX_BUNDLING_NOT_SUPPORTED. Once isWalletSupported resolves to false, the state will flip to WALLET_NOT_SUPPORTED, causing a flicker in the UI.

To prevent this, handle the loading (null) check for a higher-priority condition before evaluating the lower-priority failure state.

♻️ Proposed refactor
   if (isWalletSupported === false) return TwapFormState.WALLET_NOT_SUPPORTED
+  if (isWalletSupported === null) return TwapFormState.LOADING_SAFE_INFO
+
   if (isTxBundlingSupported === false) return TwapFormState.TX_BUNDLING_NOT_SUPPORTED
 
-  if (verification === null || isTxBundlingSupported === null || isWalletSupported === null) {
+  if (verification === null || isTxBundlingSupported === null) {
     return TwapFormState.LOADING_SAFE_INFO
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/cowswap-frontend/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx`
around lines 51 - 56, Update the state-resolution ordering in getTwapFormState
so any null loading value for the higher-priority wallet support check is
handled before returning TX_BUNDLING_NOT_SUPPORTED. Preserve the final priority
where WALLET_NOT_SUPPORTED takes precedence once isWalletSupported resolves
false, while retaining LOADING_SAFE_INFO for unresolved verification or bundling
support.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/wallet/src/api/state/walletCapabilitiesAtom.ts`:
- Around line 155-180: Update isAtomicBatchSupportedAsyncAtom so a null
walletCapabilities result returns null rather than false, preserving the
distinction between unresolved capabilities and definitively unsupported
wallets. Keep the existing capability-status checks unchanged for non-null
results.

---

Nitpick comments:
In
`@apps/cowswap-frontend/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx`:
- Around line 51-56: Update the state-resolution ordering in getTwapFormState so
any null loading value for the higher-priority wallet support check is handled
before returning TX_BUNDLING_NOT_SUPPORTED. Preserve the final priority where
WALLET_NOT_SUPPORTED takes precedence once isWalletSupported resolves false,
while retaining LOADING_SAFE_INFO for unresolved verification or bundling
support.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cec4035a-2799-4818-97df-d3df53d9a48c

📥 Commits

Reviewing files that changed from the base of the PR and between dc339ca and 0eb7628.

📒 Files selected for processing (11)
  • apps/cowswap-frontend/src/locales/en-US.po
  • apps/cowswap-frontend/src/modules/twap/containers/TwapFormWarnings/index.tsx
  • apps/cowswap-frontend/src/modules/twap/containers/TwapFormWarnings/warnings/UnsupportedWalletWarning.tsx
  • apps/cowswap-frontend/src/modules/twap/containers/TwapFormWidget/index.tsx
  • apps/cowswap-frontend/src/modules/twap/hooks/useTwapFormState.ts
  • apps/cowswap-frontend/src/modules/twap/index.ts
  • apps/cowswap-frontend/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.test.ts
  • apps/cowswap-frontend/src/modules/twap/pure/PrimaryActionButton/getTwapFormState.tsx
  • apps/cowswap-frontend/src/modules/twap/pure/PrimaryActionButton/index.tsx
  • libs/wallet/src/api/state/walletCapabilitiesAtom.test.ts
  • libs/wallet/src/api/state/walletCapabilitiesAtom.ts

Comment on lines 155 to 180
export const isAtomicBatchSupportedAsyncAtom = atom(async (get): Promise<boolean | null> => {
const isSafeApp = get(isSafeAppAtom)

if (isSafeApp === null) return null

if (isSafeApp) return true

const accountType = get(accountTypeAtom)
const isSmartContractWallet = get(isSmartContractWalletAtom)
const isSafeWallet = get(isSafeWalletAtom)
const isSafeViaWc = get(isSafeViaWcAtom)

if (accountType === null || isSmartContractWallet === null || isSafeViaWc === null) return null

// Smart accounts (ERC-4337, Coinbase Smart Wallet, EIP-7702, etc.) that are not a Safe lack the
// fallback handler mechanism TWAP requires, so we treat them as unsupported.
// Note: useIsSmartContractWallet() only detects AccountType.SMART_CONTRACT, not EIP-7702 accounts
// (which keep the same EOA address but have delegation bytecode). We check both explicitly.
if ((isSmartContractWallet || accountType === AccountType.EIP7702EOA) && !isSafeWallet && !isSafeViaWc) return false
/**
* A SafeWallet connected through SafeApp is assumed to have support.
* A SafeWallet connected through WC or any other provider needs to pass the capabilities check.
*/
if (isSafeApp) return true
if (isSafeApp === null || isSafeViaWc === null) return null

const walletCapabilities = await get(walletCapabilitiesAtom)

// If `walletCapabilitiesAtom` returns `null` it's because `shouldSkipCapabilitiesCheck === true`,
// or because some kind of API empty response or error. So, if we cannot check, then we must be false,
// If `walletCapabilitiesAtom` returns `null` it's because some kind of API empty response
// or error. So, if we cannot check, then we must be false,
// not null (as some components/functions like `validateTradeForm` treat `null` as loading):
if (!walletCapabilities) return false
if (walletCapabilities === null) return false

const status = walletCapabilities.atomic?.status || ''
const supported = walletCapabilities?.atomicBatch?.supported
const status = walletCapabilities.atomic?.status
const isLegacyAtomicBatchSupported = Boolean(walletCapabilities?.atomicBatch?.supported)

// See https://www.eip5792.xyz/getting-started:
// - supported: The wallet will execute all calls atomically and contiguously
// - ready: The wallet is able to upgrade to supported pending user approval (e.g. via EIP-7702)
return status === 'supported' || !!supported
// return status === 'supported' || status === 'ready'
return status === 'supported' || status === 'ready' || isLegacyAtomicBatchSupported
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Collapsing "capabilities unresolved" into false likely explains the reported Safe-via-WC "Unsupported wallet" regression.

if (walletCapabilities === null) return false treats an unresolved/errored capabilities check identically to a definitively-unsupported wallet. This is the same design point Danziger raised previously: it prevents callers from distinguishing "loading/unknown" from "not supported," which both risks UI flicker and — more importantly per the PR conversation — matches the exact symptom Elena reported: Safe web app connections via WalletConnect still show "Unsupported wallet" (rather than resolving to true/null) when the capabilities call for that chain doesn't resolve cleanly for that connection type.

Given resolveCapabilitiesForChain was just changed to return null more often (no more fallback), this path is now reached more frequently, which could make the regression worse rather than better for wallets whose capability responses don't clearly key by chain.

Based on learnings from the past review comment on this exact code path: "Then maybe walletCapabilities could return false or a hardcoded { atomic: { status: false }}... I think it would be better to be able to distinguish loading from not supported... Otherwise, this will probably trigger those annoying UI flickers."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/wallet/src/api/state/walletCapabilitiesAtom.ts` around lines 155 - 180,
Update isAtomicBatchSupportedAsyncAtom so a null walletCapabilities result
returns null rather than false, preserving the distinction between unresolved
capabilities and definitively unsupported wallets. Keep the existing
capability-status checks unchanged for non-null results.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Base smart account: TWAP form shows a 'loading' state

5 participants