mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-02 12:53:10 +00:00
fix contract, add emojis to user display
This commit is contained in:
parent
7a9e8ccd78
commit
539e118e30
@ -31,6 +31,7 @@ interface IZKVerifier {
|
||||
contract ZKPassportVerifier {
|
||||
// Structure to store user verification data
|
||||
struct Verification {
|
||||
bool initialized; // Whether the user has set their verification data
|
||||
bool adult; // Whether user is 18+
|
||||
string country; // User's country of nationality
|
||||
string gender; // User's gender
|
||||
@ -39,8 +40,9 @@ contract ZKPassportVerifier {
|
||||
// Mapping from user address to their verification data
|
||||
mapping(address => Verification) public verifications;
|
||||
|
||||
// Mapping to track used unique identifiers
|
||||
mapping(bytes32 => bool) public usedUniqueIdentifiers;
|
||||
// Mapping to track used unique identifiers per wallet
|
||||
// This prevents cross-wallet correlation while maintaining Sybil resistance
|
||||
mapping(address => mapping(bytes32 => bool)) public walletUniqueIdentifiers;
|
||||
|
||||
// Address of the ZKVerifier contract
|
||||
IZKVerifier public zkVerifier;
|
||||
@ -81,14 +83,47 @@ contract ZKPassportVerifier {
|
||||
// Revert if proof is not valid
|
||||
require(verified, "Proof verification failed");
|
||||
|
||||
// Check if this unique identifier has already been used
|
||||
require(!usedUniqueIdentifiers[uniqueIdentifier], "Unique identifier already used");
|
||||
// Always enforce wallet-scoped uniqueness
|
||||
require(!walletUniqueIdentifiers[msg.sender][uniqueIdentifier], "Unique identifier already used by this wallet");
|
||||
|
||||
// Mark this unique identifier as used
|
||||
usedUniqueIdentifiers[uniqueIdentifier] = true;
|
||||
// Mark this unique identifier as used by this wallet
|
||||
walletUniqueIdentifiers[msg.sender][uniqueIdentifier] = true;
|
||||
|
||||
// Store the verification data
|
||||
verifications[msg.sender] = Verification({
|
||||
initialized: true,
|
||||
adult: adult,
|
||||
country: country,
|
||||
gender: gender
|
||||
});
|
||||
|
||||
emit VerificationUpdated(msg.sender, adult, country, gender);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Update verification data without requiring a new proof
|
||||
* @param adult Whether the sender is 18+
|
||||
* @param country The sender's country of nationality
|
||||
* @param gender The sender's gender
|
||||
*/
|
||||
function updateVerification(
|
||||
bool adult,
|
||||
string calldata country,
|
||||
string calldata gender,
|
||||
ProofVerificationParams calldata params
|
||||
) external {
|
||||
// Verify the proof first using the ZKVerifier contract
|
||||
(bool verified, bytes32 uniqueIdentifier) = zkVerifier.verifyProof(params);
|
||||
|
||||
// Revert if proof is not valid
|
||||
require(verified, "Proof verification failed");
|
||||
|
||||
// Always enforce wallet-scoped uniqueness
|
||||
require(walletUniqueIdentifiers[msg.sender][uniqueIdentifier], "Unique identifier already used by this wallet");
|
||||
|
||||
// Update the verification data
|
||||
verifications[msg.sender] = Verification({
|
||||
initialized: true,
|
||||
adult: adult,
|
||||
country: country,
|
||||
gender: gender
|
||||
@ -106,9 +141,9 @@ contract ZKPassportVerifier {
|
||||
*/
|
||||
function getVerification(address user)
|
||||
external view
|
||||
returns (bool, string memory, string memory)
|
||||
returns (bool, bool, string memory, string memory)
|
||||
{
|
||||
Verification storage verification = verifications[user];
|
||||
return (verification.adult, verification.country, verification.gender);
|
||||
return (verification.initialized, verification.adult, verification.country, verification.gender);
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ export function AuthorDisplay({
|
||||
className = '',
|
||||
showBadge = true,
|
||||
}: AuthorDisplayProps) {
|
||||
const { displayName, callSign, ensName, ordinalDetails } =
|
||||
const { displayName, callSign, ensName, ordinalDetails, countryFlag, ageEmoji, genderEmoji } =
|
||||
useUserDisplay(address);
|
||||
|
||||
// Only show a badge if the author has ENS, Ordinal, or Call Sign
|
||||
@ -21,7 +21,12 @@ export function AuthorDisplay({
|
||||
|
||||
return (
|
||||
<div className={`flex items-center gap-1.5 ${className}`}>
|
||||
<span className="text-xs text-muted-foreground">{displayName}</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{displayName}
|
||||
{countryFlag && <span className="ml-1">{countryFlag}</span>}
|
||||
{ageEmoji && <span className="ml-1">{ageEmoji}</span>}
|
||||
{genderEmoji && <span className="ml-1">{genderEmoji}</span>}
|
||||
</span>
|
||||
|
||||
{shouldShowBadge && (
|
||||
<Badge
|
||||
|
||||
@ -12,6 +12,9 @@ export interface UserDisplayInfo {
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
identityProviders: IdentityProvider[] | null;
|
||||
countryFlag: string | null;
|
||||
ageEmoji: string | null;
|
||||
genderEmoji: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -29,6 +32,9 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
|
||||
isLoading: true,
|
||||
error: null,
|
||||
identityProviders: null,
|
||||
countryFlag: null,
|
||||
ageEmoji: null,
|
||||
genderEmoji: null,
|
||||
});
|
||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||
|
||||
@ -85,6 +91,10 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
|
||||
displayPreference: null,
|
||||
isLoading: false,
|
||||
error: null,
|
||||
identityProviders: null,
|
||||
countryFlag: null,
|
||||
ageEmoji: null,
|
||||
genderEmoji: null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -95,6 +105,32 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
|
||||
if (identity) {
|
||||
const displayName = userIdentityService.getDisplayName(address);
|
||||
|
||||
// Extract country, adult, and gender claims from identity providers
|
||||
let countryFlag = null;
|
||||
let ageEmoji = null;
|
||||
let genderEmoji = null;
|
||||
|
||||
if (identity.identityProviders) {
|
||||
for (const provider of identity.identityProviders) {
|
||||
if (provider.type === 'zkpassport') {
|
||||
for (const claim of provider.claims) {
|
||||
if (claim.key === 'country' && claim.verified && typeof claim.value === 'string') {
|
||||
// Convert country code to flag emoji
|
||||
countryFlag = getCountryFlag(claim.value);
|
||||
}
|
||||
if (claim.key === 'adult' && claim.verified && typeof claim.value === 'boolean') {
|
||||
ageEmoji = claim.value ? '🧓' : '👶';
|
||||
}
|
||||
if (claim.key === 'gender' && claim.verified && typeof claim.value === 'string') {
|
||||
// Map gender to emoji
|
||||
genderEmoji = claim.value.toLowerCase() === 'm' ? '♂️' :
|
||||
claim.value.toLowerCase() === 'f' ? '♀️' : '⚧️';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setDisplayInfo({
|
||||
displayName,
|
||||
callSign: identity.callSign || null,
|
||||
@ -107,6 +143,9 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
|
||||
isLoading: false,
|
||||
error: null,
|
||||
identityProviders: identity.identityProviders || null,
|
||||
countryFlag,
|
||||
ageEmoji,
|
||||
genderEmoji,
|
||||
});
|
||||
} else {
|
||||
setDisplayInfo({
|
||||
@ -121,6 +160,9 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
|
||||
isLoading: false,
|
||||
error: null,
|
||||
identityProviders: null,
|
||||
countryFlag: null,
|
||||
ageEmoji: null,
|
||||
genderEmoji: null,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
@ -137,6 +179,10 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
|
||||
displayPreference: null,
|
||||
isLoading: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
identityProviders: null,
|
||||
countryFlag: null,
|
||||
ageEmoji: null,
|
||||
genderEmoji: null,
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -161,6 +207,17 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
|
||||
verificationInfo,
|
||||
]);
|
||||
|
||||
// Helper function to convert country code to flag emoji
|
||||
function getCountryFlag(countryCode: string): string {
|
||||
// Convert country code to flag emoji using regional indicator symbols
|
||||
// For example, 'US' -> '🇺🇸'
|
||||
return countryCode
|
||||
.toUpperCase()
|
||||
.replace(/./g, char =>
|
||||
String.fromCodePoint(127397 + char.charCodeAt(0))
|
||||
);
|
||||
}
|
||||
|
||||
return displayInfo;
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@ import { MessageService } from './MessageService';
|
||||
import messageManager from '@/lib/waku';
|
||||
import { localDatabase } from '@/lib/database/LocalDatabase';
|
||||
import { WalletManager } from '@/lib/wallet';
|
||||
import { getVerification } from '../zkPassport';
|
||||
|
||||
export interface UserIdentity {
|
||||
address: string;
|
||||
@ -393,6 +394,7 @@ export class UserIdentityService {
|
||||
displayPreference,
|
||||
lastUpdated: timestamp,
|
||||
verificationStatus: EVerificationStatus.WALLET_UNCONNECTED,
|
||||
identityProviders: []
|
||||
};
|
||||
}
|
||||
|
||||
@ -711,4 +713,187 @@ export class UserIdentityService {
|
||||
|
||||
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ZKPassport claims with multi-layer cache support
|
||||
*/
|
||||
async getZKPassportClaims(address: string): Promise<Claim[] | null> {
|
||||
// 1. Check in-memory cache first (fastest)
|
||||
if (this.userIdentityCache[address]?.identityProviders) {
|
||||
const zkPassportProvider = this.userIdentityCache[address].identityProviders?.find(p => p.type === 'zkpassport');
|
||||
if (zkPassportProvider) {
|
||||
return zkPassportProvider.claims;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check LocalDatabase persistence (warm start)
|
||||
const persisted = localDatabase.cache.userIdentities[address];
|
||||
if (persisted?.identityProviders) {
|
||||
const zkPassportProvider = persisted.identityProviders.find(p => p.type === 'zkpassport');
|
||||
if (zkPassportProvider) {
|
||||
// Restore in memory cache
|
||||
this.userIdentityCache[address] = {
|
||||
...persisted,
|
||||
identityProviders: persisted.identityProviders
|
||||
};
|
||||
return zkPassportProvider.claims;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Check Waku message cache (network cache)
|
||||
const cacheServiceData = messageManager.messageCache.userIdentities[address];
|
||||
if (cacheServiceData?.identityProviders) {
|
||||
const zkPassportProvider = cacheServiceData.identityProviders.find(p => p.type === 'zkpassport');
|
||||
if (zkPassportProvider) {
|
||||
// Store in internal cache for future use
|
||||
this.userIdentityCache[address] = {
|
||||
...cacheServiceData,
|
||||
identityProviders: cacheServiceData.identityProviders
|
||||
};
|
||||
return zkPassportProvider.claims;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Fetch from blockchain (source of truth)
|
||||
return this.resolveZKPassportClaims(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Force fresh resolution of ZKPassport claims (bypass caches)
|
||||
*/
|
||||
async getZKPassportClaimsFresh(address: string): Promise<Claim[] | null> {
|
||||
return this.resolveZKPassportClaims(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve ZKPassport claims from blockchain contract with TTL caching
|
||||
*/
|
||||
private async resolveZKPassportClaims(address: string): Promise<Claim[] | null> {
|
||||
try {
|
||||
// Check if we have a recent cached version
|
||||
const cached = this.userIdentityCache[address]?.identityProviders?.find(p => p.type === 'zkpassport');
|
||||
const now = Date.now();
|
||||
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes TTL
|
||||
|
||||
// If we have a recent cache and it's still valid, return it
|
||||
if (cached && cached.verifiedAt && (now - cached.verifiedAt) < CACHE_TTL) {
|
||||
return cached.claims;
|
||||
}
|
||||
|
||||
const claimsData = await getVerification(address);
|
||||
if (!claimsData) return null;
|
||||
|
||||
const claims: Claim[] = [];
|
||||
|
||||
// Process adult claim
|
||||
if (claimsData.adult !== undefined) {
|
||||
claims.push({
|
||||
key: 'adult',
|
||||
value: claimsData.adult,
|
||||
verified: true
|
||||
});
|
||||
}
|
||||
|
||||
// Process country claim
|
||||
if (claimsData.country) {
|
||||
claims.push({
|
||||
key: 'country',
|
||||
value: claimsData.country,
|
||||
verified: true
|
||||
});
|
||||
}
|
||||
|
||||
// Process gender claim
|
||||
if (claimsData.gender) {
|
||||
claims.push({
|
||||
key: 'gender',
|
||||
value: claimsData.gender,
|
||||
verified: true
|
||||
});
|
||||
}
|
||||
|
||||
// Update identity with claims (this updates all cache layers)
|
||||
this.updateUserIdentityWithZKPassportClaims(address, claims);
|
||||
|
||||
return claims;
|
||||
} catch (error) {
|
||||
console.error('Failed to resolve ZKPassport claims:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user identity with ZKPassport claims (updates all cache layers)
|
||||
*/
|
||||
updateUserIdentityWithZKPassportClaims(address: string, claims: Claim[]): void {
|
||||
const timestamp = Date.now();
|
||||
|
||||
// Initialize identity if it doesn't exist
|
||||
if (!this.userIdentityCache[address]) {
|
||||
this.userIdentityCache[address] = {
|
||||
ensName: undefined,
|
||||
ordinalDetails: undefined,
|
||||
callSign: undefined,
|
||||
displayPreference: EDisplayPreference.WALLET_ADDRESS,
|
||||
lastUpdated: timestamp,
|
||||
verificationStatus: EVerificationStatus.WALLET_CONNECTED,
|
||||
identityProviders: []
|
||||
};
|
||||
}
|
||||
|
||||
// Create or update ZKPassport provider with TTL
|
||||
const zkPassportProvider: IdentityProvider = {
|
||||
type: 'zkpassport',
|
||||
verifiedAt: timestamp,
|
||||
expiresAt: timestamp + 24 * 60 * 60 * 1000, // 24 hours validity
|
||||
claims: [...claims]
|
||||
};
|
||||
|
||||
// Replace or add provider
|
||||
const existingProviderIndex = this.userIdentityCache[address].identityProviders!.findIndex(
|
||||
p => p.type === 'zkpassport'
|
||||
);
|
||||
|
||||
if (existingProviderIndex >= 0) {
|
||||
this.userIdentityCache[address].identityProviders![existingProviderIndex] = zkPassportProvider;
|
||||
} else {
|
||||
this.userIdentityCache[address].identityProviders!.push(zkPassportProvider);
|
||||
}
|
||||
|
||||
// Update last updated timestamp
|
||||
this.userIdentityCache[address].lastUpdated = timestamp;
|
||||
|
||||
// Update verification status if user has verified claims
|
||||
if (claims.some(c => c.verified)) {
|
||||
this.userIdentityCache[address].verificationStatus = EVerificationStatus.ENS_ORDINAL_VERIFIED;
|
||||
}
|
||||
|
||||
// Notify listeners that the user identity has been updated
|
||||
this.notifyRefreshListeners(address);
|
||||
|
||||
// Persist to IndexedDB
|
||||
localDatabase.upsertUserIdentity(address, {
|
||||
identityProviders: this.userIdentityCache[address].identityProviders,
|
||||
lastUpdated: timestamp
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch resolve multiple user identities for post processing
|
||||
*/
|
||||
async resolveMultipleUsers(addresses: string[]): Promise<Map<string, UserIdentity>> {
|
||||
const result = new Map<string, UserIdentity>();
|
||||
|
||||
// Process all resolutions in parallel
|
||||
await Promise.all(
|
||||
addresses.map(async (address) => {
|
||||
const identity = await this.getUserIdentity(address);
|
||||
if (identity) {
|
||||
result.set(address, identity);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { BrowserProvider, Contract } from 'ethers';
|
||||
import { config } from '@/lib/wallet/config';
|
||||
// Contract configuration - these should be moved to environment variables in production
|
||||
export const CONTRACT_ADDRESS = "0xaA649E71A6d7347742e3642AAe209d580913f021"; // Hardhat default deploy address
|
||||
export const CONTRACT_ADDRESS = "0x1753dbd9f4bb6473ee2905b2db183760B95be475"; // Hardhat default deploy address
|
||||
const CONTRACT_ABI = [
|
||||
{
|
||||
"inputs": [
|
||||
@ -57,17 +57,22 @@ const CONTRACT_ABI = [
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"name": "initialized",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "adult",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"name": "country",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "",
|
||||
"name": "gender",
|
||||
"type": "string"
|
||||
}
|
||||
],
|
||||
@ -151,21 +156,77 @@ const CONTRACT_ABI = [
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "usedUniqueIdentifiers",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"name": "adult",
|
||||
"type": "bool"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "country",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "gender",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"components": [
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "vkeyHash",
|
||||
"type": "bytes32"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "proof",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32[]",
|
||||
"name": "publicInputs",
|
||||
"type": "bytes32[]"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes",
|
||||
"name": "committedInputs",
|
||||
"type": "bytes"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[]",
|
||||
"name": "committedInputCounts",
|
||||
"type": "uint256[]"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "validityPeriodInSeconds",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "domain",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"internalType": "string",
|
||||
"name": "scope",
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "devMode",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"internalType": "struct ProofVerificationParams",
|
||||
"name": "params",
|
||||
"type": "tuple"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"name": "updateVerification",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
@ -197,6 +258,30 @@ const CONTRACT_ABI = [
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "bytes32",
|
||||
"name": "",
|
||||
"type": "bytes32"
|
||||
}
|
||||
],
|
||||
"name": "walletUniqueIdentifiers",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "bool",
|
||||
"name": "",
|
||||
"type": "bool"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "zkVerifier",
|
||||
@ -449,6 +534,7 @@ export const submitVerificationToContract = async (
|
||||
|
||||
try {
|
||||
const signer = await getSigner();
|
||||
console.log(signer)
|
||||
if (!signer) {
|
||||
setProgress('Failed to connect to wallet');
|
||||
return null;
|
||||
@ -496,13 +582,130 @@ export const getVerification = async (address: string): Promise<{ adult: boolean
|
||||
try {
|
||||
const provider = new BrowserProvider(window.ethereum as any);
|
||||
const contract = new Contract(CONTRACT_ADDRESS, CONTRACT_ABI, provider) as unknown as {
|
||||
getVerification: (address: string) => Promise<[boolean, string, string]>;
|
||||
getVerification: (address: string) => Promise<[boolean, boolean, string, string]>;
|
||||
};
|
||||
|
||||
const [adult, country, gender] = await contract.getVerification(address);
|
||||
const [initialized, adult, country, gender] = await contract.getVerification(address);
|
||||
if (!initialized) {
|
||||
return null; // No verification data set for this user
|
||||
}
|
||||
return { adult, country, gender };
|
||||
} catch (error) {
|
||||
console.error('Error fetching verification data:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Update verification data for a user without requiring a new proof
|
||||
* @param adult Whether the user is 18+
|
||||
* @param country The user's country of nationality
|
||||
* @param gender The user's gender
|
||||
* @param setProgress Function to update progress status
|
||||
* @returns Promise resolving to transaction hash on success, null on failure
|
||||
*/
|
||||
export const updateVerification = async (
|
||||
adult: boolean,
|
||||
country: string,
|
||||
gender: string,
|
||||
proof: ProofResult,
|
||||
setProgress: (status: string) => void
|
||||
): Promise<string | null> => {
|
||||
setProgress('Initializing blockchain connection...');
|
||||
const zkPassport = new ZKPassport();
|
||||
|
||||
// Get verification parameters
|
||||
const verifierParams = zkPassport.getSolidityVerifierParameters({
|
||||
proof: proof,
|
||||
// Use the same scope as the one you specified with the request function
|
||||
scope: "identity",
|
||||
// Enable dev mode if you want to use mock passports, otherwise keep it false
|
||||
devMode: true,
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
const signer = await getSigner();
|
||||
if (!signer) {
|
||||
setProgress('Failed to connect to wallet');
|
||||
return null;
|
||||
}
|
||||
|
||||
setProgress('Connecting to contract...');
|
||||
if (!signer) {
|
||||
setProgress('Failed to get signer');
|
||||
return null;
|
||||
}
|
||||
const contract = new Contract(CONTRACT_ADDRESS, CONTRACT_ABI, signer) as unknown as {
|
||||
updateVerification: (adult: boolean, country: string, gender: string, verifierParams: SolidityVerifierParameters) => Promise<any>;
|
||||
};
|
||||
|
||||
setProgress('Updating verification data on blockchain...');
|
||||
const tx = await contract.updateVerification(adult, country, gender, verifierParams);
|
||||
|
||||
setProgress('Waiting for blockchain confirmation...');
|
||||
const receipt = await tx.wait();
|
||||
|
||||
if (receipt && receipt.hash) {
|
||||
setProgress('Verification successfully updated on blockchain!');
|
||||
return receipt.hash;
|
||||
} else {
|
||||
setProgress('Transaction completed but no hash received');
|
||||
return null;
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error updating verification:', error);
|
||||
if (error.message) {
|
||||
setProgress(`Error: ${error.message}`);
|
||||
} else {
|
||||
setProgress('Failed to update verification on contract');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch ZKPassport claims for a user with proper typing
|
||||
* @param address The wallet address of the user to fetch claims for
|
||||
* @returns Promise resolving to claims array or null if not found
|
||||
*/
|
||||
export const fetchZKPassportClaims = async (address: string): Promise<Claim[] | null> => {
|
||||
try {
|
||||
const claimsData = await getVerification(address);
|
||||
if (!claimsData) return null;
|
||||
|
||||
const claims: Claim[] = [];
|
||||
|
||||
// Process adult claim
|
||||
if (claimsData.adult !== undefined) {
|
||||
claims.push({
|
||||
key: 'adult',
|
||||
value: claimsData.adult,
|
||||
verified: true
|
||||
});
|
||||
}
|
||||
|
||||
// Process country claim
|
||||
if (claimsData.country) {
|
||||
claims.push({
|
||||
key: 'country',
|
||||
value: claimsData.country,
|
||||
verified: true
|
||||
});
|
||||
}
|
||||
|
||||
// Process gender claim
|
||||
if (claimsData.gender) {
|
||||
claims.push({
|
||||
key: 'gender',
|
||||
value: claimsData.gender,
|
||||
verified: true
|
||||
});
|
||||
}
|
||||
|
||||
return claims.length > 0 ? claims : null;
|
||||
} catch (error) {
|
||||
console.error('Error fetching ZKPassport claims:', error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@ -8,7 +8,7 @@ import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { ContractVerificationButton } from '@/components/ui/contract-verification-button';
|
||||
import { CONTRACT_ADDRESS, getVerification, submitVerificationToContract } from '@/lib/zkPassport';
|
||||
import { CONTRACT_ADDRESS, getVerification, submitVerificationToContract, updateVerification } from '@/lib/zkPassport';
|
||||
import { verifyWithZKPassport, ZKPassportVerificationOptions } from '@/lib/zkPassport';
|
||||
import { UserIdentityService } from '@/lib/services/UserIdentityService';
|
||||
import { useForum } from '@/contexts/useForum';
|
||||
@ -795,22 +795,43 @@ export default function ProfilePage() {
|
||||
const countryClaim = userInfo.identityProviders?.flatMap(p => p.claims).find(c => c.key === 'country');
|
||||
const genderClaim = userInfo.identityProviders?.flatMap(p => p.claims).find(c => c.key === 'gender');
|
||||
|
||||
|
||||
const tx = await submitVerificationToContract(
|
||||
// Check if verification already exists
|
||||
const existingVerification = await getVerification(address!);
|
||||
const hasExistingVerification = existingVerification && (
|
||||
existingVerification.adult !== undefined ||
|
||||
existingVerification.country !== '' ||
|
||||
existingVerification.gender !== ''
|
||||
);
|
||||
console.log('Existing verification:', existingVerification, hasExistingVerification);
|
||||
|
||||
let tx;
|
||||
if (hasExistingVerification) {
|
||||
// Use updateVerification for existing verifications
|
||||
tx = await updateVerification(
|
||||
adulthoodClaim?.value as boolean || false,
|
||||
countryClaim?.value as string || '',
|
||||
genderClaim?.value as string || '',
|
||||
proof,
|
||||
setProgress
|
||||
);
|
||||
|
||||
if (tx) {
|
||||
toast({
|
||||
title: 'Verification Submitted',
|
||||
description: 'Your verification has been submitted to the contract.',
|
||||
});
|
||||
}
|
||||
return tx;
|
||||
} else {
|
||||
// Use submitVerificationToContract for new verifications
|
||||
tx = await submitVerificationToContract(
|
||||
adulthoodClaim?.value as boolean || false,
|
||||
countryClaim?.value as string || '',
|
||||
genderClaim?.value as string || '',
|
||||
proof,
|
||||
setProgress
|
||||
);
|
||||
}
|
||||
|
||||
if (tx) {
|
||||
toast({
|
||||
title: 'Verification Submitted',
|
||||
description: 'Your verification has been submitted to the contract.',
|
||||
});
|
||||
}
|
||||
return tx;
|
||||
}}
|
||||
isVerifying={isVerifying}
|
||||
verificationType="adult"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user