Skip to content
Open
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
25ee077
Feat: Installed farcaster package
vortex-hue Aug 13, 2025
42fc37e
fix: build errors
vortex-hue Aug 14, 2025
9557dce
feat: Add Farcaster miniapp support for identity flow with build fixes
vortex-hue Aug 14, 2025
42e08a9
rm eslintrc
vortex-hue Aug 14, 2025
64d56a8
refactor: Address code review feedback with improved architecture acr…
vortex-hue Aug 14, 2025
3323d82
fix: Address code review complaints
vortex-hue Aug 16, 2025
8bff2fe
fix: working on resolving the reviews made
vortex-hue Aug 18, 2025
f3940b2
fix: removed dynamic import, and reverted some configs to the default…
vortex-hue Aug 19, 2025
53564da
pulled update
vortex-hue Aug 28, 2025
60eb1cb
fix: Addressed all outstanding code review feedback.
vortex-hue Sep 2, 2025
c105e1b
fix: resolved all code changes requested
vortex-hue Sep 2, 2025
e10b584
feat: implement shared build config utility for Node.js built-ins and…
vortex-hue Sep 2, 2025
8edaca1
revert: removed the shared build config changes and other changes
vortex-hue Sep 3, 2025
c34de25
fc:frame meta tag
vortex-hue Sep 5, 2025
f6889fd
fix: refined the codebase
vortex-hue Sep 5, 2025
8616d89
fix:patching up the codebase
vortex-hue Sep 8, 2025
36dfa32
Revert "fix:patching up the codebase"
vortex-hue Sep 8, 2025
5ed0e06
revert: reverted all changes requested
vortex-hue Sep 8, 2025
5bdab21
revert: reverted all changes requested
vortex-hue Sep 8, 2025
cd6d40f
revert: reverted all changes requested
vortex-hue Sep 8, 2025
f1e9b8d
fix: revert changes requested
vortex-hue Sep 13, 2025
b7de6cb
fix: revert changes requested
vortex-hue Sep 13, 2025
304c15a
fix: revert changes affecting mobile
vortex-hue Sep 18, 2025
9841794
feat: implemented the universal link using farcaster docs
vortex-hue Sep 22, 2025
d19d211
fix: resolving previous issues
vortex-hue Oct 4, 2025
158465a
fix: resolved redirection concerns and fixed bugs
vortex-hue Oct 4, 2025
4060809
Merge remote-tracking branch 'origin/main' into add-farcaster-support…
vortex-hue Oct 9, 2025
e6ba4d0
fix: build id working well now
vortex-hue Oct 25, 2025
8cf55a7
fix: remove the unused interface of VerifyButtonProps
vortex-hue Oct 25, 2025
c1486dc
fix: removed the used import { createUniversalLinkCallback } from ../…
vortex-hue Oct 25, 2025
04ff59c
fix: revert apps/demo-identity-app/src/App.tsx and verifybutton.tsx t…
vortex-hue Oct 25, 2025
3dd1832
Merge upstream/main into add-farcaster-support-for-the-identity-flow
vortex-hue Nov 10, 2025
59484bc
fix: update auth.ts to use chainConfigs instead of contractAddresses
vortex-hue Nov 10, 2025
f620a1c
Merge remote-tracking branch 'upstream/main' into add-farcaster-suppo…
vortex-hue Dec 16, 2025
c38476f
refactor: apply engineering principles to Farcaster implementation
vortex-hue Dec 16, 2025
20d0905
refactor: clean up demo app following engineering principles
vortex-hue Dec 16, 2025
9a9dbbc
fix: address code review feedback
vortex-hue Dec 28, 2025
7a7a921
fix: address round 3 code review comments (restore comments, fix fvRe…
vortex-hue Dec 28, 2025
994b4f3
fix: address PR review feedback from L03TJ3
vortex-hue Feb 10, 2026
75a9b45
fix: address round 2 PR review feedback from sirpy
vortex-hue Feb 16, 2026
6377bf0
chore: merge upstream/main and regenerate yarn.lock
vortex-hue Feb 16, 2026
359e2c2
feat: add navigateToFaceVerification with Farcaster-aware redirect
vortex-hue Feb 16, 2026
4ad2886
refactor: remove Farcaster config coupling from SDK internals
vortex-hue Feb 28, 2026
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
2 changes: 1 addition & 1 deletion apps/demo-identity-app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
</html>
9 changes: 2 additions & 7 deletions apps/demo-identity-app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const App: React.FC = () => {
setIsVerified(isWhitelisted ?? false)
} catch (error) {
console.error("Error checking whitelist:", error)
setIsWhitelisted(false)
} finally {
setLoadingWhitelist(false)
}
Expand All @@ -87,10 +88,6 @@ const App: React.FC = () => {
}
}, [address, identitySDK, isWhitelisted, connectedAccount])

const handleVerificationSuccess = () => {
setIsVerified(true)
}

return (
<TamaguiProvider config={tamaguiConfig}>
<ScrollView
Expand Down Expand Up @@ -180,9 +177,7 @@ const App: React.FC = () => {
!isWhitelisted &&
!error ? (
<YStack alignItems="center" gap={12}>
<VerifyButton
onVerificationSuccess={handleVerificationSuccess}
/>
<VerifyButton />
<Text fontSize={14} color="$gray10">
You need to verify your identity via GoodDollar to continue.
</Text>
Expand Down
10 changes: 3 additions & 7 deletions apps/demo-identity-app/src/components/ClaimButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,21 @@ export const ClaimButton: React.FC = () => {
setError("ClaimSDK is not initialized.")
return
}
console.log("handleClaim called")

setIsClaiming(true)
setError(null)
setTxHash(null)

try {
const callBackExample = async () => {
console.log("waiting for claim for 2 seconds...")
await new Promise((resolve) => setTimeout(resolve, 2000)) // wait 2 seconds
console.log("tx start")
const txConfirm = async () => {
await new Promise((resolve) => setTimeout(resolve, 2000))
}

const tx = await sdk.claim(callBackExample)
const tx = await sdk.claim(txConfirm)
if (!tx) return

setTxHash(tx.transactionHash)
} catch (err: any) {
console.error("Claim failed:", err)
setError(err.message || "An unexpected error occurred.")
} finally {
setIsClaiming(false)
Expand Down
15 changes: 4 additions & 11 deletions apps/demo-identity-app/src/components/VerifyButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@ import { Button } from "tamagui"
import { useIdentitySDK } from "@goodsdks/react-hooks"
import { useAccount } from "wagmi"

interface VerifyButtonProps {
onVerificationSuccess: () => void
}

export const VerifyButton: React.FC<VerifyButtonProps> = ({
onVerificationSuccess,
}) => {
export const VerifyButton: React.FC = () => {
const { address } = useAccount()
const { sdk: identitySDK } = useIdentitySDK("development")

Expand All @@ -20,13 +14,12 @@ export const VerifyButton: React.FC<VerifyButtonProps> = ({
const fvLink = await identitySDK.generateFVLink(
false,
window.location.href,
42220,
Copy link
Contributor

Choose a reason for hiding this comment

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

why was this removed

undefined,
)

window.location.href = fvLink
} catch (error) {
console.error("Verification failed:", error)
// Handle error (e.g., show toast)
} catch (e) {
console.error("Verification failed:", e)
}
}

Expand Down
10 changes: 7 additions & 3 deletions apps/demo-identity-app/src/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import React, { ReactNode } from "react"

const queryClient = new QueryClient()

const projectId = "71dd03d057d89d0af68a4c627ec59694"

const projectId = "71dd03d057d89d0af68a4c627ec59694"
const metadata = {
name: "AppKit",
description: "AppKit Example",
url: "https://example.com", // origin must match your domain & subdomain
url: "http://localhost:3000",
icons: ["https://avatars.githubusercontent.com/u/179229932"],
}

Expand Down Expand Up @@ -43,6 +42,11 @@ createAppKit({
"--w3m-color-mix-strength": 40,
"--w3m-accent": "#00BB7F",
},
allowUnsupportedChain: false,
enableWalletConnect: true,
enableInjected: true,
enableEIP6963: true,
enableCoinbase: true,
})

type ComponentProps = {
Expand Down
2 changes: 2 additions & 0 deletions apps/demo-identity-app/src/globals.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import React from "react"

declare namespace JSX {
interface IntrinsicElements {
"claim-button": React.DetailedHTMLProps<
Expand Down
2 changes: 1 addition & 1 deletion apps/demo-identity-app/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ export default defineConfig({
"process.browser": true,
"process.env": process.env,
},
});
});
2 changes: 1 addition & 1 deletion apps/demo-webcomponents/vite.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ export default defineConfig({
"process.browser": true,
"process.env": process.env,
},
})
})
26 changes: 26 additions & 0 deletions packages/citizen-sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,32 @@ For Wagmi-based React projects, use the hooks exposed from `@goodsdks/react-hook

Explore the generated TypeScript definitions in `dist/` for the complete surface, including helper enums (`contractEnv`, `SupportedChains`, etc.).

## Farcaster MiniApp Support

The SDK includes support for Farcaster miniapps, which enables proper navigation and callback handling when running inside a Farcaster miniapp environment.

### Configuration

**Important**: The `FarcasterAppConfigs` in `constants.ts` are placeholder values. If you're integrating the SDK into a Farcaster miniapp, you must provide your Farcaster app ID and slug when initializing the SDK.

1. Go to the [Farcaster Developer Portal](https://warpcast.com/~/developers)
2. Register your Mini App and obtain your `appId` and `appSlug`
3. Pass these values to `IdentitySDK.init`:

```ts
const identitySDK = await IdentitySDK.init({
publicClient,
walletClient,
env: "production",
farcasterConfig: {
appId: "YOUR_APP_ID",
appSlug: "YOUR_APP_SLUG"
}
})
```

The SDK will automatically detect if it's running in a Farcaster miniapp and use Universal Links for callbacks, which ensures proper navigation back to your miniapp after face verification.

## References

- [Demo Identity App](https://demo-identity-app.vercel.app/)
Expand Down
1 change: 1 addition & 0 deletions packages/citizen-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"wagmi": "*"
},
"dependencies": {
"@farcaster/miniapp-sdk": "^0.1.8",
Copy link
Contributor

Choose a reason for hiding this comment

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

dev?peer? @L03TJ3

"lz-string": "^1.5.0",
"tsup": "^8.4.0"
},
Expand Down
23 changes: 23 additions & 0 deletions packages/citizen-sdk/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,29 @@ export const Envs: Record<string, Record<string, string>> = {
},
}

/**
Copy link
Collaborator

Choose a reason for hiding this comment

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

If these are just placeholders and not working this should be clarified in a readme and should be configurable options for developers to pass down
(we are not hosting a farcaster app, developers integrating the identitysdk/claim-sdk might)

* Farcaster Mini App configurations for different environments
* @notice THESE ARE EXAMPLE / PLACEHOLDER VALUES ONLY.
* Developers integrating the SDK SHOULD:
* 1. Pass a custom `farcasterConfig` when initializing IdentitySDK / ClaimSDK
*
* The SDK will default to these values if no config is provided, which will likely fail in production.
*/
export const FarcasterAppConfigs: Record<string, { appId: string; appSlug: string }> = {
Copy link
Contributor

Choose a reason for hiding this comment

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

apps dont have envs in farcaster

production: {
appId: "", // REQUIRED: Replace with actual app ID from Farcaster Developer Portal
appSlug: "", // REQUIRED: Replace with actual app slug
},
staging: {
appId: "", // REQUIRED: Replace with actual app ID from Farcaster Developer Portal
appSlug: "", // REQUIRED: Replace with actual app slug
},
development: {
appId: "", // REQUIRED: Replace with actual app ID from Farcaster Developer Portal
appSlug: "", // REQUIRED: Replace with actual app slug
},
}

export interface ContractAddresses {
identityContract: `0x${string}`
ubiContract: `0x${string}`
Expand Down
2 changes: 2 additions & 0 deletions packages/citizen-sdk/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export * from "./sdks"
export * from "./constants"
export * from "./utils/auth"

1 change: 0 additions & 1 deletion packages/citizen-sdk/src/sdks/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from "./viem-identity-sdk"
export * from "./viem-claim-sdk"
export * from "./viem-custodial-claim-sdk"
export * from "./viem-custodial-identity-sdk"
63 changes: 53 additions & 10 deletions packages/citizen-sdk/src/sdks/viem-claim-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
type WalletClient,
ContractFunctionExecutionError,
TransactionReceipt,
zeroAddress,
} from "viem"

import { waitForTransactionReceipt } from "viem/actions"
Expand All @@ -20,9 +21,16 @@ import {
faucetABI,
isSupportedChain,
ubiSchemeV2ABI,
FarcasterAppConfigs,
} from "../constants"
import type { ContractAddresses } from "../constants"
import { resolveChainAndContract } from "../utils/chains"
import {
createVerificationCallbackUrl,
createFarcasterCallbackUniversalLink,
isInFarcasterMiniApp,
navigateToUrl
} from "../utils/auth"
Copy link
Contributor

Choose a reason for hiding this comment

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

why is this here? this is the claim sdk.
the claim sdk can use the identity sdk

import { triggerFaucet as triggerFaucetUtil } from "../utils/triggerFaucet"
import {
createRpcIteratorRegistry,
Expand Down Expand Up @@ -82,7 +90,7 @@ export class ClaimSDK {
private readonly faucetAddress: Address
private readonly account: Address
private readonly env: contractEnv
public readonly rdu: string
public rdu: string

constructor({
account,
Expand All @@ -99,9 +107,11 @@ export class ClaimSDK {
this.walletClient = walletClient
this.identitySDK = identitySDK
this.account = account ?? walletClient.account.address
this.env = env

// Initialize callback URL - will be set properly in initializeCallbackUrl
this.rdu = rdu
this.env = env
this.initializeCallbackUrl(rdu);

const { chainId, contractEnvAddresses } = resolveChainAndContract(
walletClient,
Expand Down Expand Up @@ -142,6 +152,37 @@ export class ClaimSDK {
this.faucetAddress = contractEnvAddresses.faucetContract as Address
}

/**
* Initialize the callback URL with proper Farcaster Universal Link support
* @param rdu - The redirect URL after claim
*/
private async initializeCallbackUrl(rdu?: string): Promise<void> {
Copy link
Contributor

Choose a reason for hiding this comment

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

this is duplicate. it should be in the identity sdk

if (!rdu) return;

try {
// Check if we're in a Farcaster context and should use Universal Links
const isFarcaster = await isInFarcasterMiniApp();

if (isFarcaster && FarcasterAppConfigs[this.env]) {
// Create proper Farcaster Universal Link
const farcasterConfig = FarcasterAppConfigs[this.env];
this.rdu = createFarcasterCallbackUniversalLink(
farcasterConfig,
'claim',
{ source: "gooddollar_claim_verification" }
);
} else {
// Fallback to direct callback URL for non-Farcaster environments
this.rdu = await createVerificationCallbackUrl(rdu, {
source: "gooddollar_claim_verification"
});
}
} catch (error) {
// Fallback to original URL on error
this.rdu = rdu;
}
}

private getContractsForChain(chainId: SupportedChains): ContractAddresses {
const contracts = this.chainContracts.get(chainId)

Expand Down Expand Up @@ -454,19 +495,21 @@ export class ClaimSDK {
* @throws If face verification redirect fails.
*/
private async fvRedirect(): Promise<void> {
const fvChainId = this.fvDefaultChain ?? this.chainId
// Generate link with current chain ID as default, respecting identity-contract logic
const fvLink = await this.identitySDK.generateFVLink(
false,
this.rdu,
fvChainId,
this.chainId
)
if (typeof window !== "undefined") {
window.location.href = fvLink
} else {
throw new Error(
"Face verification redirect is only supported in browser environments.",
)

// Handle Farcaster Context
if (await isInFarcasterMiniApp()) {
await navigateToUrl(fvLink, false)
return
}

// Default legacy behavior
window.location.href = fvLink
}

/**
Expand Down
Loading