Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/app/(spaces)/PublicSpace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ export default function PublicSpace({
const [currentUserFid, setCurrentUserFid] = useState<number | null>(null);
const [isSignedIntoFarcaster, setIsSignedIntoFarcaster] = useState(false);
const { wallets } = useWallets();
const currentIdentity = useAppStore((state) => state.account.getCurrentIdentity());
const associatedFids = currentIdentity?.associatedFids || [];

const {
lastUpdatedAt: authManagerLastUpdatedAt,
Expand All @@ -122,9 +124,20 @@ export default function PublicSpace({
});
}, [authManagerLastUpdatedAt]);

// Loads the current user's FID if they're signed into Farcaster
// Loads the current user's FID from associatedFids first, then from authenticator if signed in
useEffect(() => {
if (!isSignedIntoFarcaster) return;
// First, try to get FID from associatedFids (works even without signer)
if (associatedFids.length > 0) {
setCurrentUserFid(associatedFids[0]);
return;
}

// If no associated FIDs and user is signed into Farcaster, try to get from authenticator
if (!isSignedIntoFarcaster) {
setCurrentUserFid(null);
return;
}

authManagerCallMethod({
requestingFidgetId: "root",
authenticatorId: FARCASTER_NOUNSPACE_AUTHENTICATOR_NAME,
Expand All @@ -133,9 +146,11 @@ export default function PublicSpace({
}).then((authManagerResp) => {
if (authManagerResp.result === "success") {
setCurrentUserFid(authManagerResp.value as number);
} else {
setCurrentUserFid(null);
}
});
}, [isSignedIntoFarcaster, authManagerLastUpdatedAt]);
}, [isSignedIntoFarcaster, authManagerLastUpdatedAt, associatedFids]);

// Load editable spaces when user signs in
useEffect(() => {
Expand Down
52 changes: 37 additions & 15 deletions src/common/data/stores/app/accounts/farcasterStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ type FarcasterActions = {
getFidsForCurrentIdentity: () => Promise<void>;
registerFidForCurrentIdentity: (
fid: number,
signingKey: string,
signingKey?: string,
// Takes in signMessage as it is a method
// of the Authenticator and client doesn't
// have direct access to the keys
signMessage: (messageHash: Uint8Array) => Promise<Uint8Array>,
signMessage?: (messageHash: Uint8Array) => Promise<Uint8Array>,
) => Promise<void>;
setFidsForCurrentIdentity: (fids: number[]) => void;
addFidToCurrentIdentity: (fid: number) => void;
Expand Down Expand Up @@ -59,23 +59,45 @@ export const farcasterStore = (
}
},
registerFidForCurrentIdentity: async (fid, signingKey, signMessage) => {
const request: Omit<FidLinkToIdentityRequest, "signature"> = {
console.log("[registerFidForCurrentIdentity] Starting registration:", { fid, hasSigningKey: !!signingKey });
if (signingKey && !signMessage) {
throw new Error("signMessage is required when signingKey is provided");
}
const baseRequest: FidLinkToIdentityRequest = {
fid,
identityPublicKey: get().account.currentSpaceIdentityPublicKey!,
timestamp: moment().toISOString(),
signingPublicKey: signingKey,
};
const signedRequest: FidLinkToIdentityRequest = {
...request,
signature: bytesToHex(await signMessage(hashObject(request))),
signingPublicKey: signingKey ?? null,
signature: null,
};
Comment on lines +66 to 72
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Consider handling undefined currentSpaceIdentityPublicKey.

Line 68 uses a non-null assertion (!) on currentSpaceIdentityPublicKey. If this value is undefined (e.g., no identity is loaded), the request will contain undefined as the identity key, which could cause a server-side error or unexpected behavior.

Consider adding a guard:

 registerFidForCurrentIdentity: async (fid, signingKey, signMessage) => {
   console.log("[registerFidForCurrentIdentity] Starting registration:", { fid, hasSigningKey: !!signingKey });
   if (signingKey && !signMessage) {
     throw new Error("signMessage is required when signingKey is provided");
   }
+  const identityPublicKey = get().account.currentSpaceIdentityPublicKey;
+  if (!identityPublicKey) {
+    throw new Error("No identity public key available");
+  }
   const baseRequest: FidLinkToIdentityRequest = {
     fid,
-    identityPublicKey: get().account.currentSpaceIdentityPublicKey!,
+    identityPublicKey,
     timestamp: moment().toISOString(),
📝 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.

Suggested change
const baseRequest: FidLinkToIdentityRequest = {
fid,
identityPublicKey: get().account.currentSpaceIdentityPublicKey!,
timestamp: moment().toISOString(),
signingPublicKey: signingKey,
};
const signedRequest: FidLinkToIdentityRequest = {
...request,
signature: bytesToHex(await signMessage(hashObject(request))),
signingPublicKey: signingKey ?? null,
signature: null,
};
const identityPublicKey = get().account.currentSpaceIdentityPublicKey;
if (!identityPublicKey) {
throw new Error("No identity public key available");
}
const baseRequest: FidLinkToIdentityRequest = {
fid,
identityPublicKey,
timestamp: moment().toISOString(),
signingPublicKey: signingKey ?? null,
signature: null,
};
🤖 Prompt for AI Agents
In src/common/data/stores/app/accounts/farcasterStore.ts around lines 66 to 72,
the code uses a non-null assertion on
get().account.currentSpaceIdentityPublicKey which can be undefined; change this
to guard against a missing identity by checking if currentSpaceIdentityPublicKey
is present before building the request and either (a) throw or return a rejected
Promise with a clear error/error code indicating "no current space identity
loaded", or (b) attempt to load/resolve the identity first and then proceed;
ensure you do not insert undefined into the request object and update callers to
handle the thrown/rejected result accordingly.

const { data } = await axiosBackend.post<FidLinkToIdentityResponse>(
"/api/fid-link",
signedRequest,
);
if (!isUndefined(data.value)) {
get().account.addFidToCurrentIdentity(data.value!.fid);
analytics.track(AnalyticsEvent.LINK_FID, { fid });
const signedRequest: FidLinkToIdentityRequest = signingKey
? {
...baseRequest,
signature: bytesToHex(await signMessage!(hashObject(baseRequest))),
}
: baseRequest;
console.log("[registerFidForCurrentIdentity] Request payload:", {
fid: signedRequest.fid,
hasSigningPublicKey: !!signedRequest.signingPublicKey,
hasSignature: !!signedRequest.signature,
});
try {
const { data } = await axiosBackend.post<FidLinkToIdentityResponse>(
"/api/fid-link",
signedRequest,
);
console.log("[registerFidForCurrentIdentity] API response:", data);
if (!isUndefined(data.value)) {
get().account.addFidToCurrentIdentity(data.value!.fid);
analytics.track(AnalyticsEvent.LINK_FID, { fid });
console.log("[registerFidForCurrentIdentity] Successfully registered FID:", data.value.fid);
} else {
console.warn("[registerFidForCurrentIdentity] API response has no value:", data);
}
} catch (error: any) {
console.error("[registerFidForCurrentIdentity] Error:", error);
console.error("[registerFidForCurrentIdentity] Error response:", error.response?.data);
throw error;
}
},
});
171 changes: 140 additions & 31 deletions src/common/providers/LoggedInStateProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { SetupStep } from "@/common/data/stores/app/setup";
import useValueHistory from "@/common/lib/hooks/useValueHistory";

import requiredAuthenticators from "@/constants/requiredAuthenticators";
import { FARCASTER_AUTHENTICATOR_NAME } from "@/fidgets/farcaster";
import { bytesToHex } from "@noble/ciphers/utils";
import { usePrivy } from "@privy-io/react-auth";
import { isEqual, isUndefined } from "lodash";
Expand Down Expand Up @@ -132,6 +133,73 @@ const LoggedInStateProvider: React.FC<LoggedInLayoutProps> = ({ children }) => {
}
}

const inferFidFromWallet = async (): Promise<number | undefined> => {
if (!user?.wallet?.address) {
console.log("[inferFidFromWallet] No wallet address available");
return undefined;
}
try {
console.log("[inferFidFromWallet] Fetching FID for wallet:", user.wallet.address);
const response = await fetch(
`/api/farcaster/neynar/users?addresses=${user.wallet.address}`,
);
if (!response.ok) {
console.log("[inferFidFromWallet] API response not OK:", response.status, response.statusText);
return undefined;
}
const data = await response.json();
console.log("[inferFidFromWallet] API response data:", data);
const users = data?.users ?? [];
if (users.length === 0) {
console.log("[inferFidFromWallet] No users found in response");
return undefined;
}

const walletLower = user.wallet.address.toLowerCase();
const matchingUser = users.find((u: any) => {
// Check verified_addresses.primary.eth_address
if (u.verified_addresses?.primary?.eth_address?.toLowerCase() === walletLower) {
return true;
}
// Check verified_addresses.eth_addresses array
if (u.verified_addresses?.eth_addresses?.some(
(addr: string) => addr.toLowerCase() === walletLower
)) {
return true;
}
// Check verifications array (fallback)
if (u.verifications?.some(
(addr: string) => addr.toLowerCase() === walletLower
)) {
return true;
}
return false;
});

const fid = matchingUser?.fid ?? users[0]?.fid;
console.log("[inferFidFromWallet] Found FID:", fid, "from matching user:", !!matchingUser);
return fid;
} catch (e) {
console.error("[inferFidFromWallet] Error inferring FID from wallet:", e);
return undefined;
}
};
Comment on lines +136 to +186
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Potential incorrect FID association with fallback to users[0].

Line 179 falls back to users[0]?.fid when no matching user is found. If the API returns users that don't have the wallet address verified, this could incorrectly associate an unrelated FID with the current identity.

Consider returning undefined when no explicit match is found:

-      const fid = matchingUser?.fid ?? users[0]?.fid;
-      console.log("[inferFidFromWallet] Found FID:", fid, "from matching user:", !!matchingUser);
-      return fid;
+      if (!matchingUser) {
+        console.log("[inferFidFromWallet] No matching user found for wallet");
+        return undefined;
+      }
+      console.log("[inferFidFromWallet] Found FID:", matchingUser.fid);
+      return matchingUser.fid;
📝 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.

Suggested change
const inferFidFromWallet = async (): Promise<number | undefined> => {
if (!user?.wallet?.address) {
console.log("[inferFidFromWallet] No wallet address available");
return undefined;
}
try {
console.log("[inferFidFromWallet] Fetching FID for wallet:", user.wallet.address);
const response = await fetch(
`/api/farcaster/neynar/users?addresses=${user.wallet.address}`,
);
if (!response.ok) {
console.log("[inferFidFromWallet] API response not OK:", response.status, response.statusText);
return undefined;
}
const data = await response.json();
console.log("[inferFidFromWallet] API response data:", data);
const users = data?.users ?? [];
if (users.length === 0) {
console.log("[inferFidFromWallet] No users found in response");
return undefined;
}
const walletLower = user.wallet.address.toLowerCase();
const matchingUser = users.find((u: any) => {
// Check verified_addresses.primary.eth_address
if (u.verified_addresses?.primary?.eth_address?.toLowerCase() === walletLower) {
return true;
}
// Check verified_addresses.eth_addresses array
if (u.verified_addresses?.eth_addresses?.some(
(addr: string) => addr.toLowerCase() === walletLower
)) {
return true;
}
// Check verifications array (fallback)
if (u.verifications?.some(
(addr: string) => addr.toLowerCase() === walletLower
)) {
return true;
}
return false;
});
const fid = matchingUser?.fid ?? users[0]?.fid;
console.log("[inferFidFromWallet] Found FID:", fid, "from matching user:", !!matchingUser);
return fid;
} catch (e) {
console.error("[inferFidFromWallet] Error inferring FID from wallet:", e);
return undefined;
}
};
const inferFidFromWallet = async (): Promise<number | undefined> => {
if (!user?.wallet?.address) {
console.log("[inferFidFromWallet] No wallet address available");
return undefined;
}
try {
console.log("[inferFidFromWallet] Fetching FID for wallet:", user.wallet.address);
const response = await fetch(
`/api/farcaster/neynar/users?addresses=${user.wallet.address}`,
);
if (!response.ok) {
console.log("[inferFidFromWallet] API response not OK:", response.status, response.statusText);
return undefined;
}
const data = await response.json();
console.log("[inferFidFromWallet] API response data:", data);
const users = data?.users ?? [];
if (users.length === 0) {
console.log("[inferFidFromWallet] No users found in response");
return undefined;
}
const walletLower = user.wallet.address.toLowerCase();
const matchingUser = users.find((u: any) => {
// Check verified_addresses.primary.eth_address
if (u.verified_addresses?.primary?.eth_address?.toLowerCase() === walletLower) {
return true;
}
// Check verified_addresses.eth_addresses array
if (u.verified_addresses?.eth_addresses?.some(
(addr: string) => addr.toLowerCase() === walletLower
)) {
return true;
}
// Check verifications array (fallback)
if (u.verifications?.some(
(addr: string) => addr.toLowerCase() === walletLower
)) {
return true;
}
return false;
});
if (!matchingUser) {
console.log("[inferFidFromWallet] No matching user found for wallet");
return undefined;
}
console.log("[inferFidFromWallet] Found FID:", matchingUser.fid);
return matchingUser.fid;
} catch (e) {
console.error("[inferFidFromWallet] Error inferring FID from wallet:", e);
return undefined;
}
};
🤖 Prompt for AI Agents
In src/common/providers/LoggedInStateProvider.tsx around lines 136 to 186, the
current logic falls back to users[0]?.fid when no matching user is found which
can incorrectly associate an unrelated FID; change the behavior to return
undefined when no explicit wallet match exists by removing the users[0]
fallback, set fid to matchingUser?.fid (or undefined), and update the log to
clearly state when no matching FID was found before returning undefined.


const waitForAuthenticator = async (
authenticatorName: string,
attempts = 10,
delay = 1000,
) => {
for (let i = 0; i < attempts; i++) {
const initialized = await authenticatorManager.getInitializedAuthenticators();
if (initialized.includes(authenticatorName)) {
return true;
}
await new Promise((resolve) => setTimeout(resolve, delay));
}
return false;
};

async function loadAuthenticators() {
try {
await loadPreKeys();
Expand All @@ -144,48 +212,89 @@ const LoggedInStateProvider: React.FC<LoggedInLayoutProps> = ({ children }) => {
}

const installRequiredAuthenticators = async () => {
await authenticatorManager.installAuthenticators(requiredAuthenticators);
authenticatorManager.initializeAuthenticators(requiredAuthenticators);
setCurrentStep(SetupStep.REQUIRED_AUTHENTICATORS_INSTALLED);
if (requiredAuthenticators.length > 0) {
await authenticatorManager.installAuthenticators(requiredAuthenticators);
authenticatorManager.initializeAuthenticators(requiredAuthenticators);
setCurrentStep(SetupStep.REQUIRED_AUTHENTICATORS_INSTALLED);
} else {
// If no required authenticators, skip directly to initialized
setCurrentStep(SetupStep.AUTHENTICATORS_INITIALIZED);
}
};

const registerAccounts = async () => {
let currentIdentity = getCurrentIdentity()!;
console.log("[registerAccounts] Starting, current FIDs:", currentIdentity.associatedFids);
if (currentIdentity.associatedFids.length > 0) {
console.log("[registerAccounts] FIDs already exist, skipping registration");
setCurrentStep(SetupStep.ACCOUNTS_REGISTERED);
} else {
console.log("[registerAccounts] No FIDs found, loading from server...");
await loadFidsForCurrentIdentity();
currentIdentity = getCurrentIdentity()!;
console.log("[registerAccounts] After loadFidsForCurrentIdentity, FIDs:", currentIdentity.associatedFids);
if (currentIdentity.associatedFids.length === 0) {
const fidResult = (await authenticatorManager.callMethod({
requestingFidgetId: "root",
authenticatorId: "farcaster:nounspace",
methodName: "getAccountFid",
isLookup: true,
})) as { value: number };
const publicKeyResult = (await authenticatorManager.callMethod({
requestingFidgetId: "root",
authenticatorId: "farcaster:nounspace",
methodName: "getSignerPublicKey",
isLookup: true,
})) as { value: Uint8Array };
const signForFid = async (messageHash) => {
const signResult = (await authenticatorManager.callMethod(
{
console.log("[registerAccounts] No FIDs from server, attempting to infer from wallet...");
const fidFromWallet = await inferFidFromWallet();
console.log("[registerAccounts] Inferred FID from wallet:", fidFromWallet);
if (!isUndefined(fidFromWallet)) {
try {
console.log("[registerAccounts] Registering FID without signer:", fidFromWallet);
await registerFidForCurrentIdentity(fidFromWallet);
console.log("[registerAccounts] FID registered, reloading...");
await loadFidsForCurrentIdentity();
currentIdentity = getCurrentIdentity()!;
console.log("[registerAccounts] After registration, FIDs:", currentIdentity.associatedFids);
} catch (e) {
console.error("[registerAccounts] Error registering FID from wallet:", e);
// Continue to fallback flow if registration fails
}
}
if (currentIdentity.associatedFids.length === 0) {
console.log("[registerAccounts] No FID found/inferred, falling back to signer flow...");
await authenticatorManager.installAuthenticators([
FARCASTER_AUTHENTICATOR_NAME,
]);
authenticatorManager.initializeAuthenticators([
FARCASTER_AUTHENTICATOR_NAME,
]);
const signerReady = await waitForAuthenticator(FARCASTER_AUTHENTICATOR_NAME);
if (signerReady) {
try {
const fidResult = (await authenticatorManager.callMethod({
requestingFidgetId: "root",
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "getAccountFid",
isLookup: true,
})) as { value: number };
const publicKeyResult = (await authenticatorManager.callMethod({
requestingFidgetId: "root",
authenticatorId: "farcaster:nounspace",
methodName: "signMessage",
isLookup: false,
},
messageHash,
)) as { value: Uint8Array };
return signResult.value;
};
await registerFidForCurrentIdentity(
fidResult.value,
bytesToHex(publicKeyResult.value),
signForFid,
);
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "getSignerPublicKey",
isLookup: true,
})) as { value: Uint8Array };
const signForFid = async (messageHash) => {
const signResult = (await authenticatorManager.callMethod(
{
requestingFidgetId: "root",
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "signMessage",
isLookup: false,
},
messageHash,
)) as { value: Uint8Array };
return signResult.value;
};
await registerFidForCurrentIdentity(
fidResult.value,
bytesToHex(publicKeyResult.value),
signForFid,
);
} catch (e) {
console.error("Error registering FID with signer:", e);
}
}
}
}
}
setCurrentStep(SetupStep.ACCOUNTS_REGISTERED);
Comment on lines +253 to 300
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Silent failure when authenticator initialization times out.

If waitForAuthenticator returns false (line 261), the code silently proceeds to setCurrentStep(SetupStep.ACCOUNTS_REGISTERED) at line 300 without registering any FID. This could leave the user in an inconsistent state where they appear registered but have no associated FID.

Consider logging a warning or handling this case explicitly:

          const signerReady = await waitForAuthenticator(FARCASTER_AUTHENTICATOR_NAME);
          if (signerReady) {
            try {
              // ... existing signer registration code
            } catch (e) {
              console.error("Error registering FID with signer:", e);
            }
+          } else {
+            console.warn("[registerAccounts] Authenticator initialization timed out, proceeding without FID registration");
          }
📝 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.

Suggested change
if (currentIdentity.associatedFids.length === 0) {
console.log("[registerAccounts] No FID found/inferred, falling back to signer flow...");
await authenticatorManager.installAuthenticators([
FARCASTER_AUTHENTICATOR_NAME,
]);
authenticatorManager.initializeAuthenticators([
FARCASTER_AUTHENTICATOR_NAME,
]);
const signerReady = await waitForAuthenticator(FARCASTER_AUTHENTICATOR_NAME);
if (signerReady) {
try {
const fidResult = (await authenticatorManager.callMethod({
requestingFidgetId: "root",
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "getAccountFid",
isLookup: true,
})) as { value: number };
const publicKeyResult = (await authenticatorManager.callMethod({
requestingFidgetId: "root",
authenticatorId: "farcaster:nounspace",
methodName: "signMessage",
isLookup: false,
},
messageHash,
)) as { value: Uint8Array };
return signResult.value;
};
await registerFidForCurrentIdentity(
fidResult.value,
bytesToHex(publicKeyResult.value),
signForFid,
);
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "getSignerPublicKey",
isLookup: true,
})) as { value: Uint8Array };
const signForFid = async (messageHash) => {
const signResult = (await authenticatorManager.callMethod(
{
requestingFidgetId: "root",
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "signMessage",
isLookup: false,
},
messageHash,
)) as { value: Uint8Array };
return signResult.value;
};
await registerFidForCurrentIdentity(
fidResult.value,
bytesToHex(publicKeyResult.value),
signForFid,
);
} catch (e) {
console.error("Error registering FID with signer:", e);
}
}
}
}
}
setCurrentStep(SetupStep.ACCOUNTS_REGISTERED);
if (currentIdentity.associatedFids.length === 0) {
console.log("[registerAccounts] No FID found/inferred, falling back to signer flow...");
await authenticatorManager.installAuthenticators([
FARCASTER_AUTHENTICATOR_NAME,
]);
authenticatorManager.initializeAuthenticators([
FARCASTER_AUTHENTICATOR_NAME,
]);
const signerReady = await waitForAuthenticator(FARCASTER_AUTHENTICATOR_NAME);
if (signerReady) {
try {
const fidResult = (await authenticatorManager.callMethod({
requestingFidgetId: "root",
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "getAccountFid",
isLookup: true,
})) as { value: number };
const publicKeyResult = (await authenticatorManager.callMethod({
requestingFidgetId: "root",
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "getSignerPublicKey",
isLookup: true,
})) as { value: Uint8Array };
const signForFid = async (messageHash) => {
const signResult = (await authenticatorManager.callMethod(
{
requestingFidgetId: "root",
authenticatorId: FARCASTER_AUTHENTICATOR_NAME,
methodName: "signMessage",
isLookup: false,
},
messageHash,
)) as { value: Uint8Array };
return signResult.value;
};
await registerFidForCurrentIdentity(
fidResult.value,
bytesToHex(publicKeyResult.value),
signForFid,
);
} catch (e) {
console.error("Error registering FID with signer:", e);
}
} else {
console.warn("[registerAccounts] Authenticator initialization timed out, proceeding without FID registration");
}
}
}
}
setCurrentStep(SetupStep.ACCOUNTS_REGISTERED);
🤖 Prompt for AI Agents
In src/common/providers/LoggedInStateProvider.tsx around lines 253 to 300, when
waitForAuthenticator returns false the flow silently continues and later calls
setCurrentStep(SetupStep.ACCOUNTS_REGISTERED) even though no FID was registered;
update the control flow to handle the timeout case explicitly: if signerReady is
false, log a warning/error, avoid advancing to ACCOUNTS_REGISTERED (either set
an error/failure step or keep the current step), and surface the failure to the
user/telemetry so the UI does not show the account as registered without an FID.

Expand Down
2 changes: 1 addition & 1 deletion src/constants/requiredAuthenticators.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export default ["farcaster:nounspace"];
export default [];
7 changes: 4 additions & 3 deletions src/fidgets/farcaster/components/CastRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,8 @@ const CastAttributionSecondary = ({ cast }) => {
const CastReactions = ({ cast }: { cast: CastWithInteractions }) => {
const [didLike, setDidLike] = useState(cast.viewer_context?.liked ?? false);
const [didRecast, setDidRecast] = useState(cast.viewer_context?.recasted ?? false);
const { signer, fid: userFid } = useFarcasterSigner("render-cast");
const { signer, fid: userFid, requestSignerAuthorization } =
useFarcasterSigner("render-cast");
const { showToast } = useToastStore();
const { setModalOpen, getIsAccountReady } = useAppStore((state) => ({
setModalOpen: state.setup.setModalOpen,
Expand Down Expand Up @@ -367,8 +368,8 @@ const CastReactions = ({ cast }: { cast: CastWithInteractions }) => {
}

// We check if we have the signer before proceeding
if (isUndefined(signer)) {
console.error("NO SIGNER");
if (isUndefined(signer) || userFid < 0) {
await requestSignerAuthorization();
return;
}

Expand Down
11 changes: 9 additions & 2 deletions src/fidgets/farcaster/components/CreateCast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,8 @@ const CreateCast: React.FC<CreateCastProps> = ({

const hasEmbeds = draft?.embeds && !!draft.embeds.length;
const isReply = draft?.parentCastId !== undefined;
const { signer, isLoadingSigner, fid } = useFarcasterSigner("create-cast");
const { signer, isLoadingSigner, fid, requestSignerAuthorization } =
useFarcasterSigner("create-cast");
const [initialChannels, setInitialChannels] = useState() as any;
const [isEmojiPickerOpen, setIsEmojiPickerOpen] = useState(false);
const [isEnhancing, setIsEnhancing] = useState(false);
Expand Down Expand Up @@ -415,7 +416,12 @@ const CreateCast: React.FC<CreateCastProps> = ({
);

const onSubmitPost = async (): Promise<boolean> => {
if ((!draft?.text && !draft?.embeds?.length) || isUndefined(signer)) {
if (isUndefined(signer)) {
await requestSignerAuthorization();
return false;
}

if (!draft?.text && !draft?.embeds?.length) {
console.error(
"Submission failed: Missing text or embeds, or signer is undefined.",
{
Expand Down Expand Up @@ -724,6 +730,7 @@ const CreateCast: React.FC<CreateCastProps> = ({


const getButtonText = () => {
if (!signer) return "Connect Farcaster";
if (isLoadingSigner) return "Not signed into Farcaster";
if (isPublishing) return "Publishing...";
if (submissionError) return "Retry";
Expand Down
Loading