mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-05 14:23:06 +00:00
209 lines
7.1 KiB
JavaScript
209 lines
7.1 KiB
JavaScript
import { getEnsName, verifyMessage as verifyEthereumMessage, } from '@wagmi/core';
|
|
import { config } from './config';
|
|
let bitcoinMessagePromise = null;
|
|
const loadBitcoinMessage = () => (bitcoinMessagePromise ?? (bitcoinMessagePromise = import('bitcoinjs-message')));
|
|
export class WalletManager {
|
|
constructor(appKit, bitcoinAccount, ethereumAccount) {
|
|
this.appKit = appKit;
|
|
// Determine active wallet (Bitcoin takes priority)
|
|
if (bitcoinAccount.isConnected && bitcoinAccount.address) {
|
|
this.activeAccount = bitcoinAccount;
|
|
this.activeWalletType = 'bitcoin';
|
|
this.namespace = 'bip122';
|
|
}
|
|
else if (ethereumAccount.isConnected && ethereumAccount.address) {
|
|
this.activeAccount = ethereumAccount;
|
|
this.activeWalletType = 'ethereum';
|
|
this.namespace = 'eip155';
|
|
}
|
|
else {
|
|
throw new Error('No wallet is connected');
|
|
}
|
|
}
|
|
/**
|
|
* Create or get the singleton instance
|
|
*/
|
|
static create(appKit, bitcoinAccount, ethereumAccount) {
|
|
// Always create a new instance to reflect current wallet state
|
|
WalletManager.instance = new WalletManager(appKit, bitcoinAccount, ethereumAccount);
|
|
return WalletManager.instance;
|
|
}
|
|
/**
|
|
* Get the current instance (throws if not created)
|
|
*/
|
|
static getInstance() {
|
|
if (!WalletManager.instance) {
|
|
throw new Error('WalletManager not initialized. Call WalletManager.create() first.');
|
|
}
|
|
return WalletManager.instance;
|
|
}
|
|
/**
|
|
* Check if instance exists
|
|
*/
|
|
static hasInstance() {
|
|
return WalletManager.instance !== null;
|
|
}
|
|
/**
|
|
* Clear the singleton instance
|
|
*/
|
|
static clear() {
|
|
WalletManager.instance = null;
|
|
}
|
|
/**
|
|
* Resolve ENS name for an Ethereum address
|
|
*/
|
|
static async resolveENS(address) {
|
|
try {
|
|
const ensName = await getEnsName(config, {
|
|
address: address,
|
|
});
|
|
return ensName || null;
|
|
}
|
|
catch (error) {
|
|
console.warn('Failed to resolve ENS name:', error);
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Get the currently active wallet
|
|
*/
|
|
getActiveWallet() {
|
|
return {
|
|
type: this.activeWalletType,
|
|
address: this.activeAccount.address,
|
|
isConnected: true,
|
|
};
|
|
}
|
|
/**
|
|
* Check if wallet is connected
|
|
*/
|
|
isConnected() {
|
|
return this.activeAccount.isConnected;
|
|
}
|
|
/**
|
|
* Get the active wallet type
|
|
*/
|
|
getWalletType() {
|
|
return this.activeWalletType;
|
|
}
|
|
/**
|
|
* Get address of the active wallet
|
|
*/
|
|
getAddress() {
|
|
return this.activeAccount.address;
|
|
}
|
|
/**
|
|
* Sign a message using the active wallet
|
|
*/
|
|
async signMessage(message) {
|
|
try {
|
|
// Access the adapter through the appKit instance
|
|
const adapter = this.appKit.chainAdapters?.[this.namespace];
|
|
if (!adapter) {
|
|
throw new Error(`No adapter found for namespace: ${this.namespace}`);
|
|
}
|
|
// Get the provider for the current connection
|
|
const provider = this.appKit.getProvider(this.namespace);
|
|
if (!provider) {
|
|
throw new Error(`No provider found for namespace: ${this.namespace}`);
|
|
}
|
|
if (!this.activeAccount.address) {
|
|
throw new Error('No address found for active account');
|
|
}
|
|
// Call the adapter's signMessage method
|
|
const result = await adapter.signMessage({
|
|
message,
|
|
address: this.activeAccount.address,
|
|
provider: provider,
|
|
});
|
|
return result.signature;
|
|
}
|
|
catch (error) {
|
|
console.error(`Error signing message with ${this.activeWalletType} wallet:`, error);
|
|
throw new Error(`Failed to sign message with ${this.activeWalletType} wallet: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
}
|
|
}
|
|
/**
|
|
* Verify a message signature against a wallet address
|
|
* @param message - The original message that was signed
|
|
* @param signature - The signature to verify
|
|
* @param walletAddress - The expected signer's address
|
|
* @param walletType - The type of wallet (bitcoin/ethereum)
|
|
* @returns Promise<boolean> - True if signature is valid
|
|
*/
|
|
static async verifySignature(message, signature, walletAddress, walletType) {
|
|
try {
|
|
if (process.env.NODE_ENV === 'development') {
|
|
// Keep this lightweight in dev; avoid logging full message/signature repeatedly
|
|
console.debug('WalletManager.verifySignature', { walletType });
|
|
}
|
|
if (walletType === 'ethereum') {
|
|
return await verifyEthereumMessage(config, {
|
|
address: walletAddress,
|
|
message,
|
|
signature: signature,
|
|
});
|
|
}
|
|
else if (walletType === 'bitcoin') {
|
|
if (process.env.NODE_ENV === 'development') {
|
|
console.debug('WalletManager.verifySignature (bitcoin)');
|
|
}
|
|
const bitcoinMessage = await loadBitcoinMessage();
|
|
const result = bitcoinMessage.verify(message, walletAddress, signature);
|
|
if (process.env.NODE_ENV === 'development') {
|
|
console.debug('WalletManager.verifySignature (bitcoin) result', result);
|
|
}
|
|
return result;
|
|
}
|
|
console.error('WalletManager.verifySignature - unknown wallet type:', walletType);
|
|
return false;
|
|
}
|
|
catch (error) {
|
|
console.error('WalletManager.verifySignature - error verifying signature:', error);
|
|
return false;
|
|
}
|
|
}
|
|
/**
|
|
* Get comprehensive wallet info including ENS resolution for Ethereum
|
|
*/
|
|
async getWalletInfo() {
|
|
const address = this.activeAccount.address;
|
|
if (this.activeWalletType === 'bitcoin') {
|
|
return {
|
|
address,
|
|
walletType: 'bitcoin',
|
|
isConnected: true,
|
|
};
|
|
}
|
|
// For Ethereum, try to resolve ENS name
|
|
let ensName;
|
|
try {
|
|
const resolvedName = await getEnsName(config, {
|
|
address: address,
|
|
});
|
|
ensName = resolvedName || undefined;
|
|
}
|
|
catch (error) {
|
|
console.warn('Failed to resolve ENS name:', error);
|
|
}
|
|
return {
|
|
address,
|
|
walletType: 'ethereum',
|
|
ensName,
|
|
isConnected: true,
|
|
};
|
|
}
|
|
}
|
|
WalletManager.instance = null;
|
|
// Convenience exports for singleton access
|
|
export const walletManager = {
|
|
create: WalletManager.create,
|
|
getInstance: WalletManager.getInstance,
|
|
hasInstance: WalletManager.hasInstance,
|
|
clear: WalletManager.clear,
|
|
resolveENS: WalletManager.resolveENS,
|
|
verifySignature: WalletManager.verifySignature,
|
|
};
|
|
export * from './types';
|
|
export * from './config';
|
|
//# sourceMappingURL=index.js.map
|