From 4ebf183d708634921ca134ac57ba461234774262 Mon Sep 17 00:00:00 2001 From: Mayank Mittal Date: Sat, 26 Sep 2026 17:17:09 +0530 Subject: [PATCH] fix(card-spend): skip the module read on a Safe not yet deployed on Fuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 65138daf made card set-up read the Safe's module list on every v2 set-up, to disable any retired v2 core. A new cardholder's Safe is often still counterfactual on Fuse — the set-up batch is what deploys it — so `getModulesPaginated` returns `0x`, viem throws, and activation fails with "Card not activated" before anything is signed. A Safe with no code has no modules, so there is nothing to disable: return an empty cleanup and let the batch deploy and register the Safe as before. Co-Authored-By: Claude Opus 5.5 (1M context) --- hooks/useCardSpendRegistration.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hooks/useCardSpendRegistration.ts b/hooks/useCardSpendRegistration.ts index 4fada2614..aeaadcd5b 100644 --- a/hooks/useCardSpendRegistration.ts +++ b/hooks/useCardSpendRegistration.ts @@ -301,9 +301,16 @@ const findModulePredecessor = async ( * * The list is read fresh for the same reason {@link findModulePredecessor} reads it: enabling * any module rewrites the pointers, and a stale predecessor reverts the whole batch. + * + * A new cardholder's Safe is often still counterfactual on Fuse — the set-up batch is what + * deploys it. It has no code yet, so `getModulesPaginated` returns `0x` and viem throws; with + * no code there are no modules either, so there is nothing to disable. */ const encodeModuleDisables = async (safeAddress: Address, targets: readonly Address[]) => { const client = publicClient(fuse.id); + const code = await client.getCode({ address: safeAddress }); + if (!code || code === '0x') return { disabled: [] as Address[], transactions: [] }; + const [modules] = await client.readContract({ address: safeAddress, abi: Safe_ABI,