Monorepo for Aesthetic.Computer
aesthetic.computer
1// Keeps Wallet - Inpage Script
2// Creates window.keeps API for dApps to interact with
3
4(function() {
5 'use strict';
6
7 // Request ID counter for matching responses
8 let requestId = 0;
9 const pendingRequests = new Map();
10
11 // Listen for responses from content script
12 window.addEventListener('message', (event) => {
13 if (event.source !== window) return;
14
15 const { type, requestId: resId, payload } = event.data;
16 if (!type?.endsWith('_RESPONSE')) return;
17
18 const pending = pendingRequests.get(resId);
19 if (pending) {
20 pendingRequests.delete(resId);
21 if (payload?.error) {
22 pending.reject(new Error(payload.error));
23 } else {
24 pending.resolve(payload);
25 }
26 }
27 });
28
29 // Handle lock events
30 window.addEventListener('message', (event) => {
31 if (event.data?.type === 'KEEPS_LOCKED') {
32 window.dispatchEvent(new CustomEvent('keeps:locked'));
33 }
34 });
35
36 // Send message to extension and wait for response
37 function sendMessage(type, payload = {}) {
38 return new Promise((resolve, reject) => {
39 const id = ++requestId;
40 pendingRequests.set(id, { resolve, reject });
41
42 // Timeout after 30 seconds
43 setTimeout(() => {
44 if (pendingRequests.has(id)) {
45 pendingRequests.delete(id);
46 reject(new Error('Request timeout'));
47 }
48 }, 30000);
49
50 window.postMessage({ type, requestId: id, payload }, '*');
51 });
52 }
53
54 // Public API
55 window.keeps = {
56 // Check if extension is installed
57 async isInstalled() {
58 try {
59 const res = await sendMessage('KEEPS_PING');
60 return res.success === true;
61 } catch {
62 return false;
63 }
64 },
65
66 // Get wallet state
67 async getState() {
68 return sendMessage('KEEPS_GET_STATE');
69 },
70
71 // Get connected address (null if locked/no wallet)
72 async getAddress() {
73 const res = await sendMessage('KEEPS_GET_ADDRESS');
74 return res.address;
75 },
76
77 // Request connection (will prompt unlock if needed)
78 async connect() {
79 const state = await this.getState();
80 if (!state.exists) {
81 throw new Error('No wallet found. Please create one in the extension.');
82 }
83 if (!state.unlocked) {
84 throw new Error('Wallet is locked. Please unlock in the extension.');
85 }
86 return state.address;
87 },
88
89 // Get balance
90 async getBalance(network) {
91 return sendMessage('KEEPS_GET_BALANCE', { network });
92 },
93
94 // Get keeps (NFTs)
95 async getKeeps(network) {
96 return sendMessage('KEEPS_GET_KEEPS', { network });
97 },
98
99 // Request signing an operation
100 async sign(operation) {
101 return sendMessage('KEEPS_SIGN_OPERATION', { operation });
102 },
103
104 // Events
105 onLocked(callback) {
106 window.addEventListener('keeps:locked', callback);
107 return () => window.removeEventListener('keeps:locked', callback);
108 },
109 };
110
111 // Announce availability
112 window.dispatchEvent(new CustomEvent('keeps:ready'));
113 console.log('🔐 Keeps Wallet API available at window.keeps');
114})();