mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-03 05:13:09 +00:00
feat: add ordinal check
This commit is contained in:
parent
1dd4ca7304
commit
97d7926659
@ -14,6 +14,8 @@ import {
|
||||
} from '@/lib/delegation';
|
||||
import { localDatabase } from '@/lib/database/LocalDatabase';
|
||||
import { useAppKitAccount, useDisconnect, modal } from '@reown/appkit/react';
|
||||
import { MessageService } from '@/lib/services/MessageService';
|
||||
import { UserIdentityService } from '@/lib/services/UserIdentityService';
|
||||
|
||||
interface AuthContextType {
|
||||
currentUser: User | null;
|
||||
@ -56,6 +58,14 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
// Create manager instances
|
||||
const delegationManager = useMemo(() => new DelegationManager(), []);
|
||||
const messageService = useMemo(
|
||||
() => new MessageService(delegationManager),
|
||||
[delegationManager]
|
||||
);
|
||||
const userIdentityService = useMemo(
|
||||
() => new UserIdentityService(messageService),
|
||||
[messageService]
|
||||
);
|
||||
|
||||
// Create wallet manager when we have all dependencies
|
||||
useEffect(() => {
|
||||
@ -89,51 +99,39 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function for ownership verification
|
||||
// Helper function for ownership verification (via UserIdentityService)
|
||||
const verifyUserOwnership = async (user: User): Promise<User> => {
|
||||
if (user.walletType === 'bitcoin') {
|
||||
// TODO: revert when the API is ready
|
||||
// const response = await ordinalApi.getOperatorDetails(user.address);
|
||||
// const hasOperators = response.has_operators;
|
||||
const hasOperators = true;
|
||||
|
||||
return {
|
||||
...user,
|
||||
ordinalDetails: hasOperators
|
||||
? { ordinalId: 'mock', ordinalDetails: 'Mock ordinal for testing' }
|
||||
: undefined,
|
||||
verificationStatus: hasOperators
|
||||
? EVerificationStatus.ENS_ORDINAL_VERIFIED
|
||||
: EVerificationStatus.WALLET_CONNECTED,
|
||||
lastChecked: Date.now(),
|
||||
};
|
||||
} else if (user.walletType === 'ethereum') {
|
||||
try {
|
||||
const walletInfo = WalletManager.hasInstance()
|
||||
? await WalletManager.getInstance().getWalletInfo()
|
||||
: null;
|
||||
const hasENS = !!walletInfo?.ensName;
|
||||
const ensName = walletInfo?.ensName;
|
||||
|
||||
return {
|
||||
...user,
|
||||
ensDetails: hasENS && ensName ? { ensName } : undefined,
|
||||
verificationStatus: hasENS
|
||||
? EVerificationStatus.ENS_ORDINAL_VERIFIED
|
||||
: EVerificationStatus.WALLET_CONNECTED,
|
||||
lastChecked: Date.now(),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error verifying ENS ownership:', error);
|
||||
try {
|
||||
// Force fresh resolution to ensure API call happens during verification
|
||||
const identity = await userIdentityService.getUserIdentityFresh(
|
||||
user.address
|
||||
);
|
||||
if (!identity) {
|
||||
return {
|
||||
...user,
|
||||
ensDetails: undefined,
|
||||
ordinalDetails: undefined,
|
||||
verificationStatus: EVerificationStatus.WALLET_CONNECTED,
|
||||
lastChecked: Date.now(),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
throw new Error('Unknown wallet type');
|
||||
|
||||
return {
|
||||
...user,
|
||||
ensDetails: identity.ensName ? { ensName: identity.ensName } : undefined,
|
||||
ordinalDetails: identity.ordinalDetails,
|
||||
verificationStatus: identity.verificationStatus,
|
||||
lastChecked: Date.now(),
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error verifying ownership via UserIdentityService:', error);
|
||||
return {
|
||||
...user,
|
||||
ensDetails: undefined,
|
||||
ordinalDetails: undefined,
|
||||
verificationStatus: EVerificationStatus.WALLET_CONNECTED,
|
||||
lastChecked: Date.now(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
36
src/lib/services/Ordinals.ts
Normal file
36
src/lib/services/Ordinals.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import {Ordiscan, Inscription} from 'ordiscan'
|
||||
const API_KEY = import.meta.env.VITE_ORDISCAN_API;
|
||||
|
||||
class Ordinals {
|
||||
private static instance: Ordinals | null = null;
|
||||
private ordiscan: Ordiscan;
|
||||
private readonly PARENT_INSCRIPTION_ID = "add60add0325f7c82e80d4852a8b8d5c46dbde4317e76fe4def2e718dd84b87ci0"
|
||||
|
||||
private constructor(ordiscan: Ordiscan) {
|
||||
this.ordiscan = ordiscan;
|
||||
}
|
||||
|
||||
static getInstance(): Ordinals {
|
||||
if (!Ordinals.instance) {
|
||||
Ordinals.instance = new Ordinals(new Ordiscan(API_KEY));
|
||||
}
|
||||
return Ordinals.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Ordinal details for a Bitcoin address
|
||||
*/
|
||||
async getOrdinalDetails(address: string): Promise<Inscription[] | null> {
|
||||
const inscriptions = await this.ordiscan.address.getInscriptions({address})
|
||||
if (inscriptions.length > 0) {
|
||||
if (inscriptions.some(inscription => inscription.parent_inscription_id === this.PARENT_INSCRIPTION_ID)) {
|
||||
return inscriptions.filter(inscription => inscription.parent_inscription_id === this.PARENT_INSCRIPTION_ID)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export const ordinals = Ordinals.getInstance();
|
||||
@ -8,6 +8,7 @@ import {
|
||||
import { MessageService } from './MessageService';
|
||||
import messageManager from '@/lib/waku';
|
||||
import { localDatabase } from '@/lib/database/LocalDatabase';
|
||||
import { WalletManager } from '@/lib/wallet';
|
||||
|
||||
export interface UserIdentity {
|
||||
address: string;
|
||||
@ -155,6 +156,29 @@ export class UserIdentityService {
|
||||
return identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a fresh identity resolution bypassing caches and LocalDatabase.
|
||||
* Useful for explicit verification flows where we must hit upstream resolvers.
|
||||
*/
|
||||
async getUserIdentityFresh(address: string): Promise<UserIdentity | null> {
|
||||
if (import.meta.env?.DEV) {
|
||||
console.debug('UserIdentityService: fresh resolve requested');
|
||||
}
|
||||
const identity = await this.resolveUserIdentity(address);
|
||||
if (identity) {
|
||||
// Update in-memory cache to reflect the fresh result
|
||||
this.userIdentityCache[address] = {
|
||||
ensName: identity.ensName,
|
||||
ordinalDetails: identity.ordinalDetails,
|
||||
callSign: identity.callSign,
|
||||
displayPreference: identity.displayPreference,
|
||||
lastUpdated: identity.lastUpdated,
|
||||
verificationStatus: identity.verificationStatus,
|
||||
};
|
||||
}
|
||||
return identity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all cached user identities
|
||||
*/
|
||||
@ -329,8 +353,19 @@ export class UserIdentityService {
|
||||
address: string
|
||||
): Promise<{ ordinalId: string; ordinalDetails: string } | null> {
|
||||
try {
|
||||
//TODO: add Ordinal API call
|
||||
console.log('resolveOrdinalDetails', address);
|
||||
if (address.startsWith('0x')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const inscriptions = await WalletManager.resolveOperatorOrdinals(address);
|
||||
if (Array.isArray(inscriptions) && inscriptions.length > 0) {
|
||||
const first = inscriptions[0]!;
|
||||
return {
|
||||
ordinalId: first.inscription_id,
|
||||
ordinalDetails:
|
||||
first.parent_inscription_id || 'Operator badge present',
|
||||
};
|
||||
}
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error('Failed to resolve Ordinal details:', error);
|
||||
@ -375,12 +410,22 @@ export class UserIdentityService {
|
||||
*/
|
||||
private mapVerificationStatus(status: string): EVerificationStatus {
|
||||
switch (status) {
|
||||
// Legacy message-cache statuses
|
||||
case 'verified-basic':
|
||||
return EVerificationStatus.WALLET_CONNECTED;
|
||||
case 'verified-owner':
|
||||
return EVerificationStatus.ENS_ORDINAL_VERIFIED;
|
||||
case 'verifying':
|
||||
return EVerificationStatus.WALLET_CONNECTED; // Temporary state during verification
|
||||
|
||||
// Enum string values persisted in LocalDatabase
|
||||
case EVerificationStatus.WALLET_UNCONNECTED:
|
||||
return EVerificationStatus.WALLET_UNCONNECTED;
|
||||
case EVerificationStatus.WALLET_CONNECTED:
|
||||
return EVerificationStatus.WALLET_CONNECTED;
|
||||
case EVerificationStatus.ENS_ORDINAL_VERIFIED:
|
||||
return EVerificationStatus.ENS_ORDINAL_VERIFIED;
|
||||
|
||||
default:
|
||||
return EVerificationStatus.WALLET_UNCONNECTED;
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { UseAppKitAccountReturn } from '@reown/appkit/react';
|
||||
import { AppKit } from '@reown/appkit';
|
||||
import { ordinals } from '@/lib/services/Ordinals';
|
||||
import {
|
||||
getEnsName,
|
||||
verifyMessage as verifyEthereumMessage,
|
||||
@ -8,6 +9,7 @@ import { ChainNamespace } from '@reown/appkit-common';
|
||||
import { config } from './config';
|
||||
import { Provider } from '@reown/appkit-controllers';
|
||||
import { WalletInfo, ActiveWallet } from './types';
|
||||
import { Inscription } from 'ordiscan';
|
||||
export class WalletManager {
|
||||
private static instance: WalletManager | null = null;
|
||||
|
||||
@ -95,6 +97,18 @@ export class WalletManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve Ordinal details for a Bitcoin address
|
||||
*/
|
||||
static async resolveOperatorOrdinals(address: string): Promise<Inscription[] | null> {
|
||||
try {
|
||||
return await ordinals.getOrdinalDetails(address);
|
||||
} catch (error) {
|
||||
console.warn('Failed to resolve Ordinal details:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently active wallet
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user