@@ -115,7 +115,7 @@ export default function RootLayout({
115115 const router = useRouter ( ) ;
116116 const { appWallet } = useAppWallet ( ) ;
117117 const { multisigWallet } = useMultisigWallet ( ) ;
118- const { isEnabled : isUtxosEnabled } = useUTXOS ( ) ;
118+ const { isEnabled : isUtxosEnabled , wallet : utxosWallet } = useUTXOS ( ) ;
119119
120120 const userAddress = useUserStore ( ( state ) => state . userAddress ) ;
121121 const setUserAddress = useUserStore ( ( state ) => state . setUserAddress ) ;
@@ -436,13 +436,69 @@ export default function RootLayout({
436436 setHasCheckedSession ( true ) ; // Mark as checked so we don't check again
437437 // Show loading skeleton for smooth transition
438438 setShowPostAuthLoading ( true ) ;
439-
439+
440440 // Wait a moment for the cookie to be set by the browser, then refetch session
441441 await new Promise ( resolve => setTimeout ( resolve , 200 ) ) ;
442-
442+
443443 // Refetch session to update state
444444 await refetchWalletSession ( ) ;
445-
445+
446+ // Create/ensure the User row now that the wallet session cookie exists.
447+ //
448+ // createUser is a protectedProcedure (it needs a session). The onboarding
449+ // effects fire createUser on *connect* (regular wallets in initializeWallet
450+ // below; UTXOS in connect-wallet) — which happens seconds before the user
451+ // approves the signing prompt — so that first attempt always runs
452+ // unauthenticated and is rejected UNAUTHORIZED, and it never retries once the
453+ // cookie lands. The result: useUser() stays null and the connect button is
454+ // stuck on "Authorize" even though signing succeeded. Re-run it here, after
455+ // authorization, when the cookie is actually present — for both regular
456+ // browser wallets and the UTXOS smart wallet.
457+ try {
458+ const authedAddress = userAddress || address ;
459+ if ( authedAddress ) {
460+ let stakeAddress : string | undefined ;
461+ let drepKeyHash = "" ;
462+
463+ if ( activeWallet ) {
464+ // Regular browser wallet: stake from the wallet, DRep from the 1.9 IWallet.
465+ stakeAddress = ( await activeWallet . getRewardAddresses ( ) ) [ 0 ] ;
466+ try {
467+ const dRepKey = await meshWallet ?. getDRep ( ) ;
468+ if ( dRepKey ?. publicKeyHash ) {
469+ drepKeyHash = dRepKey . publicKeyHash ;
470+ }
471+ } catch {
472+ // DRep key is optional
473+ }
474+ } else if ( isUtxosEnabled && utxosWallet ?. cardano ) {
475+ // UTXOS smart wallet: stake + DRep come from its CIP-30 interface.
476+ stakeAddress = ( await utxosWallet . cardano . getRewardAddresses ( ) ) [ 0 ] ;
477+ try {
478+ if ( typeof utxosWallet . cardano . getDRep === "function" ) {
479+ const dRepKey = await utxosWallet . cardano . getDRep ( ) ;
480+ if ( dRepKey && typeof dRepKey === "object" && "publicKeyHash" in dRepKey ) {
481+ drepKeyHash = ( dRepKey as { publicKeyHash : string } ) . publicKeyHash ;
482+ }
483+ }
484+ } catch {
485+ // DRep key is optional
486+ }
487+ }
488+
489+ if ( stakeAddress ) {
490+ createUser ( {
491+ address : authedAddress ,
492+ stakeAddress : normalizeAddressToBech32 ( stakeAddress ) ,
493+ drepKeyHash,
494+ } ) ;
495+ }
496+ }
497+ } catch {
498+ // Non-fatal: the onboarding effects will retry on a later render now that
499+ // the session cookie is present.
500+ }
501+
446502 // Invalidate wallet queries so they refetch with the new session
447503 // Use a small delay to ensure cookie is available on subsequent requests
448504 setTimeout ( ( ) => {
@@ -458,7 +514,7 @@ export default function RootLayout({
458514 setTimeout ( ( ) => {
459515 setShowPostAuthLoading ( false ) ;
460516 } , 1500 ) ;
461- } , [ refetchWalletSession , ctx . wallet , userAddress , address ] ) ;
517+ } , [ refetchWalletSession , ctx . wallet , userAddress , address , activeWallet , meshWallet , isUtxosEnabled , utxosWallet , createUser ] ) ;
462518
463519 // Memoize computed route values
464520 const isWalletPath = useMemo ( ( ) => router . pathname . includes ( "/wallets/[wallet]" ) , [ router . pathname ] ) ;
0 commit comments