diff --git a/src/services/__tests__/providerService.security.test.ts b/src/services/__tests__/providerService.security.test.ts index ae2007fa..d6bfc465 100644 --- a/src/services/__tests__/providerService.security.test.ts +++ b/src/services/__tests__/providerService.security.test.ts @@ -94,6 +94,7 @@ describe('ProviderService Security Tests', () => { name: 'Test Wallet', type: 'mnemonic', addressFormat: 'p2wpkh', + isTestOnly: false, addresses: [] }]), getActiveWallet: vi.fn().mockResolvedValue({ @@ -101,11 +102,13 @@ describe('ProviderService Security Tests', () => { name: 'Test Wallet', type: 'mnemonic', addressFormat: 'p2wpkh', + isTestOnly: false, addresses: [] }), getActiveAddress: vi.fn().mockResolvedValue({ id: 'addr1', address: 'bc1qtest123', + pubKey: '038ca117a13f9d5671e5d98e7f4e522bce3ca6d207e771080e0b70ce0ba11e2ec4', label: 'Test Address', walletId: 'wallet1', walletName: 'Test Wallet', @@ -493,7 +496,7 @@ describe('ProviderService Security Tests', () => { expect(accounts).toHaveLength(1); expect(accounts[0]).toBe('bc1qtest123'); }); - + it('should hide accounts when wallet is locked', async () => { vi.mocked(walletManager.getSettings).mockReturnValue({ ...DEFAULT_SETTINGS, @@ -517,4 +520,4 @@ describe('ProviderService Security Tests', () => { expect(accounts).toEqual([]); }); }); -}); \ No newline at end of file +}); diff --git a/src/services/__tests__/providerService.test.ts b/src/services/__tests__/providerService.test.ts index eb23af85..059abfc0 100644 --- a/src/services/__tests__/providerService.test.ts +++ b/src/services/__tests__/providerService.test.ts @@ -173,6 +173,7 @@ describe('ProviderService', () => { name: 'Test Wallet', type: 'mnemonic', addressFormat: 'p2wpkh', + isTestOnly: false, addresses: [] }]), getActiveWallet: vi.fn().mockResolvedValue({ @@ -180,6 +181,7 @@ describe('ProviderService', () => { name: 'Test Wallet', type: 'mnemonic', addressFormat: 'p2wpkh', + isTestOnly: false, addresses: [] }), setActiveWallet: vi.fn().mockResolvedValue(undefined), @@ -208,6 +210,7 @@ describe('ProviderService', () => { getActiveAddress: vi.fn().mockResolvedValue({ id: 'addr1', address: 'bc1qtest123', + pubKey: '038ca117a13f9d5671e5d98e7f4e522bce3ca6d207e771080e0b70ce0ba11e2ec4', label: 'Test Address', walletId: 'wallet1', walletName: 'Test Wallet', @@ -316,6 +319,17 @@ describe('ProviderService', () => { ) as any; expect(result.accounts).toEqual(['bc1qtest123']); + expect(result.account).toEqual({ + address: 'bc1qtest123', + publicKey: '038ca117a13f9d5671e5d98e7f4e522bce3ca6d207e771080e0b70ce0ba11e2ec4', + xOnlyPublicKey: '8ca117a13f9d5671e5d98e7f4e522bce3ca6d207e771080e0b70ce0ba11e2ec4', + addressType: 'p2wpkh', + network: 'mainnet', + walletType: 'software', + }); + expect(result.account).not.toHaveProperty('path'); + expect(result.account).not.toHaveProperty('xpub'); + expect(result.account).not.toHaveProperty('privateKey'); expect(result.proof).toBeDefined(); }); @@ -341,6 +355,8 @@ describe('ProviderService', () => { // Should return accounts with proof expect((result as any).accounts).toEqual(['bc1qtest123']); + expect((result as any).account.publicKey).toBe('038ca117a13f9d5671e5d98e7f4e522bce3ca6d207e771080e0b70ce0ba11e2ec4'); + expect((result as any).account.xOnlyPublicKey).toBe('8ca117a13f9d5671e5d98e7f4e522bce3ca6d207e771080e0b70ce0ba11e2ec4'); expect((result as any).proof).toBeDefined(); }); @@ -761,4 +777,4 @@ describe('ProviderService', () => { }); }); -}); \ No newline at end of file +}); diff --git a/src/services/providerService.ts b/src/services/providerService.ts index bd7b2903..2f842973 100644 --- a/src/services/providerService.ts +++ b/src/services/providerService.ts @@ -81,6 +81,15 @@ export type ProviderRequestParams = unknown[]; export type ProviderMetadata = Record; export type ProviderResponse = unknown; +interface ProviderAccountInfo { + address: string; + publicKey: string | null; + xOnlyPublicKey: string | null; + addressType: string; + network: 'mainnet'; + walletType: 'software' | 'hardware' | 'test'; +} + export interface ProviderService { /** * Handle provider requests from dApps @@ -183,10 +192,38 @@ export function createProviderService(): ProviderService { return isConnected ? [activeAddress.address] : []; } + async function getAccountInfo(accounts: string[]): Promise { + if (accounts.length === 0) return null; + + const walletService = getWalletService(); + const activeAddress = await walletService.getActiveAddress(); + const activeWallet = await walletService.getActiveWallet(); + if (!activeAddress || !activeWallet) return null; + + const publicKey = activeAddress.pubKey || null; + const xOnlyPublicKey = publicKey ? toXOnlyPublicKey(publicKey) : null; + + return { + address: activeAddress.address, + publicKey, + xOnlyPublicKey, + addressType: activeWallet.addressFormat, + network: 'mainnet', + walletType: activeWallet.isTestOnly ? 'test' : activeWallet.type === 'hardware' ? 'hardware' : 'software', + }; + } + + function toXOnlyPublicKey(publicKey: string): string | null { + if (/^[0-9a-fA-F]{64}$/.test(publicKey)) return publicKey.toLowerCase(); + if (/^(02|03)[0-9a-fA-F]{64}$/.test(publicKey)) return publicKey.slice(2).toLowerCase(); + return null; + } + /** Build the standard response for xcp_requestAccounts with proof. */ async function buildConnectResponse(origin: string, accounts: string[]) { + const account = await getAccountInfo(accounts); const proof = accounts.length > 0 ? await generateConnectionProof(origin) : null; - return { accounts, proof }; + return { accounts, account, proof }; } /** @@ -959,4 +996,4 @@ export function createProviderService(): ProviderService { export const [registerProviderService, getProviderService] = defineProxyService( 'ProviderService', createProviderService -); \ No newline at end of file +);