OpChan/src/lib/identity/wallets/ReOwnWalletService.ts

210 lines
6.5 KiB
TypeScript
Raw Normal View History

2025-08-06 17:21:56 +05:30
import { UseAppKitAccountReturn } from '@reown/appkit/react';
import { KeyDelegation } from '../signatures/key-delegation';
2025-08-06 17:21:56 +05:30
import { AppKit } from '@reown/appkit';
import { ChainNamespace } from '@reown/appkit-common';
import { Provider} from '@reown/appkit-controllers';
export class ReOwnWalletService {
private keyDelegation: KeyDelegation;
private bitcoinAccount?: UseAppKitAccountReturn;
private ethereumAccount?: UseAppKitAccountReturn;
2025-08-06 17:21:56 +05:30
private appKit?: AppKit;
constructor() {
this.keyDelegation = new KeyDelegation();
}
/**
* Set account references from AppKit hooks
*/
setAccounts(bitcoinAccount: UseAppKitAccountReturn, ethereumAccount: UseAppKitAccountReturn) {
this.bitcoinAccount = bitcoinAccount;
this.ethereumAccount = ethereumAccount;
}
2025-08-06 17:21:56 +05:30
/**
* Set the AppKit instance for accessing adapters
*/
setAppKit(appKit: AppKit) {
this.appKit = appKit;
}
/**
* Check if a wallet type is available and connected
*/
isWalletAvailable(walletType: 'bitcoin' | 'ethereum'): boolean {
if (walletType === 'bitcoin') {
2025-08-06 17:21:56 +05:30
return this.bitcoinAccount?.isConnected ?? false;
} else {
2025-08-06 17:21:56 +05:30
return this.ethereumAccount?.isConnected ?? false;
}
}
/**
2025-08-06 17:21:56 +05:30
* Get the active account based on wallet type
*/
2025-08-06 17:21:56 +05:30
private getActiveAccount(walletType: 'bitcoin' | 'ethereum'): UseAppKitAccountReturn | undefined {
return walletType === 'bitcoin' ? this.bitcoinAccount : this.ethereumAccount;
}
/**
2025-08-06 17:21:56 +05:30
* Get the active address for a given wallet type
*/
2025-08-06 17:21:56 +05:30
getActiveAddress(walletType: 'bitcoin' | 'ethereum'): string | undefined {
const account = this.getActiveAccount(walletType);
return account?.address;
}
/**
2025-08-06 17:21:56 +05:30
* Get the appropriate namespace for the wallet type
*/
2025-08-06 17:21:56 +05:30
private getNamespace(walletType: 'bitcoin' | 'ethereum'): ChainNamespace {
return walletType === 'bitcoin' ? 'bip122' : 'eip155';
}
/**
2025-08-06 17:21:56 +05:30
* Sign a message using the appropriate adapter
*/
2025-08-06 17:21:56 +05:30
async signMessage(messageBytes: Uint8Array, walletType: 'bitcoin' | 'ethereum'): Promise<string> {
if (!this.appKit) {
throw new Error('AppKit instance not set. Call setAppKit() first.');
}
2025-08-06 17:21:56 +05:30
const account = this.getActiveAccount(walletType);
if (!account?.address) {
throw new Error(`No ${walletType} wallet connected`);
}
2025-08-06 17:21:56 +05:30
const namespace = this.getNamespace(walletType);
2025-08-06 17:21:56 +05:30
// Convert message bytes to string for signing
const messageString = new TextDecoder().decode(messageBytes);
try {
// Access the adapter through the appKit instance
// The adapter is available through the appKit's chainAdapters property
const adapter = this.appKit.chainAdapters?.[namespace];
if (!adapter) {
throw new Error(`No adapter found for namespace: ${namespace}`);
}
2025-08-06 17:21:56 +05:30
// Get the provider for the current connection
const provider = this.appKit.getProvider(namespace);
if (!provider) {
throw new Error(`No provider found for namespace: ${namespace}`);
}
// Call the adapter's signMessage method
const result = await adapter.signMessage({
message: messageString,
address: account.address,
provider: provider as Provider
});
return result.signature;
} catch (error) {
console.error(`Error signing message with ${walletType} wallet:`, error);
throw new Error(`Failed to sign message with ${walletType} wallet: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
2025-08-06 17:21:56 +05:30
* Create a key delegation for the connected wallet
*/
2025-08-06 17:21:56 +05:30
async createKeyDelegation(walletType: 'bitcoin' | 'ethereum'): Promise<boolean> {
try {
const account = this.getActiveAccount(walletType);
if (!account?.address) {
throw new Error(`No ${walletType} wallet connected`);
}
// Generate a new browser keypair
const keypair = this.keyDelegation.generateKeypair();
// Create delegation message with expiry
const expiryTimestamp = Date.now() + (24 * 60 * 60 * 1000); // 24 hours
const delegationMessage = this.keyDelegation.createDelegationMessage(
keypair.publicKey,
account.address,
expiryTimestamp
);
const messageBytes = new TextEncoder().encode(delegationMessage);
// Sign the delegation message
const signature = await this.signMessage(messageBytes, walletType);
// Create and store the delegation
const delegationInfo = this.keyDelegation.createDelegation(
account.address,
signature,
keypair.publicKey,
keypair.privateKey,
24, // 24 hours
walletType
);
this.keyDelegation.storeDelegation(delegationInfo);
return true;
} catch (error) {
console.error(`Error creating key delegation for ${walletType}:`, error);
return false;
}
}
/**
2025-08-06 17:21:56 +05:30
* Sign a message using the delegated key (if available) or fall back to wallet signing
*/
2025-08-06 17:21:56 +05:30
async signMessageWithDelegation(messageBytes: Uint8Array, walletType: 'bitcoin' | 'ethereum'): Promise<string> {
const account = this.getActiveAccount(walletType);
if (!account?.address) {
throw new Error(`No ${walletType} wallet connected`);
}
2025-08-06 17:21:56 +05:30
// Check if we have a valid delegation for this specific wallet
if (this.keyDelegation.isDelegationValid(account.address, walletType)) {
// Use delegated key for signing
const messageString = new TextDecoder().decode(messageBytes);
const signature = this.keyDelegation.signMessage(messageString);
if (signature) {
return signature;
}
}
2025-08-06 17:21:56 +05:30
// Fall back to wallet signing
return this.signMessage(messageBytes, walletType);
}
/**
2025-08-06 17:21:56 +05:30
* Get delegation status for the connected wallet
*/
2025-08-06 17:21:56 +05:30
getDelegationStatus(walletType: 'bitcoin' | 'ethereum'): {
hasDelegation: boolean;
isValid: boolean;
timeRemaining?: number;
} {
const account = this.getActiveAccount(walletType);
const currentAddress = account?.address;
2025-08-06 17:21:56 +05:30
const hasDelegation = this.keyDelegation.retrieveDelegation() !== null;
const isValid = this.keyDelegation.isDelegationValid(currentAddress, walletType);
const timeRemaining = this.keyDelegation.getDelegationTimeRemaining();
2025-08-06 17:21:56 +05:30
return {
hasDelegation,
isValid,
timeRemaining: timeRemaining > 0 ? timeRemaining : undefined
};
}
/**
2025-08-06 17:21:56 +05:30
* Clear delegation for the connected wallet
*/
2025-08-06 17:21:56 +05:30
clearDelegation(walletType: 'bitcoin' | 'ethereum'): void {
this.keyDelegation.clearDelegation();
}
}