import { PublicKey, Transaction, VersionedTransaction } from './js/lib/vendor/solana-publickey-bundle.js'; const PAGE_REQUEST = 'shine-wallet-page-request'; const PAGE_RESPONSE = 'shine-wallet-page-response'; function bytesToBase64(bytes) { let binary = ''; const chunk = 0x8000; for (let i = 0; i < bytes.length; i += chunk) { const slice = bytes.subarray(i, i + chunk); binary += String.fromCharCode(...slice); } return btoa(binary); } function base64ToBytes(value) { const binary = atob(String(value || '').trim()); const out = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i += 1) { out[i] = binary.charCodeAt(i); } return out; } function createProviderError(message, code = '') { const error = new Error(String(message || 'Wallet provider error')); if (code === 'USER_REJECTED' || code === 'NOT_TRUSTED') { error.code = 4001; } else if (code) { error.code = code; } return error; } function createRequest(method, params = {}) { const id = `shine-wallet-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`; return new Promise((resolve, reject) => { const onMessage = (event) => { if (event.source !== window) return; const data = event.data || {}; if (data?.target !== PAGE_RESPONSE || String(data?.id || '') !== id) return; window.removeEventListener('message', onMessage); if (!data?.ok) { reject(createProviderError(data?.error || 'Wallet request failed', String(data?.code || ''))); return; } resolve(data?.result || {}); }; window.addEventListener('message', onMessage); window.postMessage({ target: PAGE_REQUEST, id, method, params, }, window.location.origin); }); } function serializeTransactionBase64(transaction) { if (!transaction || typeof transaction.serialize !== 'function') { throw createProviderError('Unsupported transaction object', 'UNSUPPORTED_TRANSACTION'); } let raw; try { raw = transaction.serialize({ requireAllSignatures: false, verifySignatures: false }); } catch { raw = transaction.serialize(); } const bytes = raw instanceof Uint8Array ? raw : new Uint8Array(raw); return bytesToBase64(bytes); } function deserializeSignedTransaction(base64, originalTransaction) { const bytes = base64ToBytes(base64); const ctor = originalTransaction?.constructor; if (ctor && typeof ctor.deserialize === 'function') { return ctor.deserialize(bytes); } if (ctor && typeof ctor.from === 'function') { return ctor.from(bytes); } if (typeof originalTransaction?.version === 'number') { return VersionedTransaction.deserialize(bytes); } return Transaction.from(bytes); } class ShineSolanaProvider { constructor() { this.isSHiNE = true; this.isPhantom = true; this.publicKey = null; this.isConnected = false; this._listeners = new Map(); } on(event, handler) { const key = String(event || ''); if (!this._listeners.has(key)) { this._listeners.set(key, new Set()); } this._listeners.get(key).add(handler); return this; } off(event, handler) { const bucket = this._listeners.get(String(event || '')); if (!bucket) return this; bucket.delete(handler); if (!bucket.size) { this._listeners.delete(String(event || '')); } return this; } removeListener(event, handler) { return this.off(event, handler); } emit(event, payload) { const bucket = this._listeners.get(String(event || '')); if (!bucket?.size) return; for (const handler of [...bucket]) { try { handler(payload); } catch {} } } async connect(options = {}) { const onlyIfTrusted = !!options?.onlyIfTrusted; if (!onlyIfTrusted) { const confirmed = window.confirm(`Connect SHiNE Wallet to ${window.location.origin}?`); if (!confirmed) { throw createProviderError('User rejected wallet connection', 'USER_REJECTED'); } } const result = await createRequest('connect', { onlyIfTrusted }); const nextKey = new PublicKey(String(result?.publicKeyBase58 || '').trim()); this.publicKey = nextKey; this.isConnected = true; this.emit('connect', nextKey); this.emit('accountChanged', nextKey); return { publicKey: nextKey }; } async disconnect() { await createRequest('disconnect', {}); this.isConnected = false; this.publicKey = null; this.emit('disconnect'); this.emit('accountChanged', null); } async signTransaction(transaction) { if (!this.publicKey) { await this.connect(); } const transactionBase64 = serializeTransactionBase64(transaction); const comment = `Site ${window.location.origin} requested transaction signature`; const result = await createRequest('signTransaction', { publicKeyBase58: this.publicKey?.toBase58?.() || '', transactionBase64, comment, }); return deserializeSignedTransaction(String(result?.signedTransactionBase64 || ''), transaction); } async request(args = {}) { const method = String(args?.method || ''); const params = args?.params; if (method === 'connect') { return this.connect(Array.isArray(params) ? params[0] : params || {}); } if (method === 'disconnect') { return this.disconnect(); } if (method === 'signTransaction') { const tx = Array.isArray(params) ? params[0] : params?.transaction || params; return this.signTransaction(tx); } throw createProviderError(`Unsupported request method: ${method}`, 'UNSUPPORTED_METHOD'); } } if (!window.solana) { const provider = new ShineSolanaProvider(); window.solana = provider; window.phantom = window.phantom || {}; window.phantom.solana = provider; window.dispatchEvent(new Event('solana#initialized')); }