toncorp-wallet-connect-sdk 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +21 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -27,6 +27,27 @@ export declare class BaseWalletConnect {
|
|
|
27
27
|
private storeSession;
|
|
28
28
|
private restoreSession;
|
|
29
29
|
private removeStoredSession;
|
|
30
|
+
private get pendingKey();
|
|
31
|
+
private storePending;
|
|
32
|
+
private readPending;
|
|
33
|
+
private clearPending;
|
|
34
|
+
/**
|
|
35
|
+
* Gọi 1 lần lúc DApp startup. Nếu lần trước onedex bị đóng giữa chừng
|
|
36
|
+
* (connect/tx đang pending), check status server và TRẢ VỀ kết quả để DApp tự
|
|
37
|
+
* hiển thị. Trả null nếu không có pending / chưa xong / lỗi tạm.
|
|
38
|
+
*
|
|
39
|
+
* KHÔNG emit event, KHÔNG tự chạy -> luồng cũ (không đóng) hoàn toàn không bị
|
|
40
|
+
* ảnh hưởng; chỉ DApp chủ động gọi lúc mount.
|
|
41
|
+
*/
|
|
42
|
+
resume(): Promise<{
|
|
43
|
+
type: 'connect';
|
|
44
|
+
status: string;
|
|
45
|
+
result?: ConnectResult;
|
|
46
|
+
} | {
|
|
47
|
+
type: 'tx';
|
|
48
|
+
status: string;
|
|
49
|
+
result: TxStatusResult;
|
|
50
|
+
} | null>;
|
|
30
51
|
private registerGlobal;
|
|
31
52
|
/** Sync connect result to the specific wallet instance on window (e.g. window.onedollar) */
|
|
32
53
|
private syncToWalletInstance;
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";class e{constructor(e,n,t,s,i,o){this.apiUrl=e,this.sessionToken=n,this.onConfirmed=t,this.onRejected=s,this.onExpired=i,this.onError=o,this.ws=null,this.reconnectTimer=null,this.closed=!1}connect(){this.closed=!1;this.apiUrl.replace(/^http/,"ws").replace(/\/$/,"");this.sessionToken;try{this.connectSocketIO()}catch{this.onError("WebSocket connection failed")}}connectSocketIO(){const e=this.apiUrl.replace(/\/$/,"");fetch(`${e}/walletconnect/?EIO=4&transport=polling`).then(e=>e.text()).then(n=>{const t=n.replace(/^\d+/,""),s=JSON.parse(t).sid,i=`${e.replace(/^http/,"ws")}/walletconnect/?EIO=4&transport=websocket&sid=${s}`;this.ws=new WebSocket(i),this.ws.onopen=()=>{this.ws.send("2probe")},this.ws.onmessage=e=>{const n=e.data;if("3probe"!==n)if(n.startsWith("42"))try{const e=JSON.parse(n.slice(2)),[t,s]=e;this.handleEvent(t,s)}catch{}else"2"===n&&this.ws.send("3");else this.ws.send("5")},this.ws.onclose=()=>{this.closed||this.scheduleReconnect()},this.ws.onerror=()=>{this.closed||this.scheduleReconnect()}}).catch(()=>{this.closed||this.scheduleReconnect()})}handleEvent(e,n){switch(e){case"session_confirmed":this.onConfirmed(n),this.close();break;case"session_rejected":this.onRejected(),this.close();break;case"session_expired":this.onExpired(),this.close()}}scheduleReconnect(){this.closed||this.reconnectTimer||(this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connectSocketIO()},3e3))}close(){this.closed=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(),this.ws=null)}}class n{constructor(e,n,t,s,i,o){this.apiUrl=e,this.sessionToken=n,this.onConfirmed=t,this.onRejected=s,this.onExpired=i,this.onError=o,this.timer=null,this.stopped=!1}start(){this.stopped=!1,this.poll(),this.timer=setInterval(()=>this.poll(),2e3)}async poll(){if(!this.stopped)try{const e=this.apiUrl.replace(/\/$/,""),n=await fetch(`${e}/connect/status?token=${this.sessionToken}`);if(!n.ok)return;const t=await n.json();switch(t.status){case"CONFIRMED":this.onConfirmed({wallet:t.wallet,user:t.user}),this.stop();break;case"REJECTED":this.onRejected(),this.stop();break;case"EXPIRED":this.onExpired(),this.stop()}}catch{}}stop(){this.stopped=!0,this.timer&&(clearInterval(this.timer),this.timer=null)}}const t="__wc_modal__";class s{constructor(e,n){this.overlay=null,this.theme=e,this.onClose=n}showWalletPicker(e,n){const t=`\n <div class="wc-header">\n <h3 class="wc-title">Connect Wallet</h3>\n <button class="wc-close" data-action="close">✕</button>\n </div>\n <div class="wc-mascot">\n <img src="https://dailytrip.1dollar.day/assets/character-mascot.png" alt="mascot" />\n </div>\n <p class="wc-subtitle">Choose a wallet to get started</p>\n <div class="wc-body">${e.length>=3?`<ul class="wc-wallet-grid">${e.map(e=>`\n <li class="wc-wallet-grid-item" data-wallet-id="${e.walletId}">\n ${e.icon?`<img class="wc-wallet-grid-icon" src="${e.icon}" alt="${e.name}" />`:`<div class="wc-wallet-grid-icon-placeholder">${e.name.charAt(0)}</div>`}\n <span class="wc-wallet-grid-name">${e.name}</span>\n </li>`).join("")}</ul>`:`<ul class="wc-wallet-list">${e.map(e=>`\n <li class="wc-wallet-item" data-wallet-id="${e.walletId}">\n ${e.icon?`<img class="wc-wallet-list-icon" src="${e.icon}" alt="${e.name}" />`:`<div class="wc-wallet-list-icon-placeholder">${e.name.charAt(0)}</div>`}\n <div class="wc-wallet-item-info">\n <div class="wc-wallet-item-name">${e.name}</div>\n ${e.description?`<div class="wc-wallet-item-desc">${e.description}</div>`:""}\n </div>\n <span class="wc-wallet-item-arrow">›</span>\n </li>`).join("")}</ul>`}</div>\n <div class="wc-footer">Powered by TonCorp</div>\n `;this.render(t),e.forEach(e=>{this.overlay?.querySelector(`[data-wallet-id="${e.walletId}"]`)?.addEventListener("click",()=>n(e))})}showConnect(e,n,t,s){const i=function(e,n=220){return`https://api.qrserver.com/v1/create-qr-code/?size=${n}x${n}&data=${encodeURIComponent(e)}&margin=8`}(n),o=`\n <div class="wc-header">\n <div class="wc-header-left">\n ${t?'<button class="wc-back" data-action="back">←</button>':""}\n ${e.icon?`<img class="wc-header-icon" src="${e.icon}" />`:""}\n <h3 class="wc-title">${e.name}</h3>\n </div>\n <button class="wc-close" data-action="close">✕</button>\n </div>\n <p class="wc-subtitle">Approve the connection in your wallet app</p>\n <div class="wc-body">\n <div class="wc-tabs">\n <button class="wc-tab active" data-tab="mobile"><span>Mobile</span></button>\n <button class="wc-tab" data-tab="qr"><span>QR Code</span></button>\n </div>\n <div data-panel="mobile">\n <a class="wc-deeplink-btn" href="${n}" target="_blank">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M11.944 0A12 12 0 1 0 24 12.056A12.01 12.01 0 0 0 11.944 0Zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472c-.18 1.898-.962 6.502-1.36 8.627c-.168.9-.499 1.201-.82 1.23c-.696.065-1.225-.46-1.9-.902c-1.056-.693-1.653-1.124-2.678-1.8c-1.185-.78-.417-1.21.258-1.91c.177-.184 3.247-2.977 3.307-3.23c.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345c-.48.33-.913.49-1.302.48c-.428-.008-1.252-.241-1.865-.44c-.752-.245-1.349-.374-1.297-.789c.027-.216.325-.437.893-.663c3.498-1.524 5.83-2.529 6.998-3.014c3.332-1.386 4.025-1.627 4.476-1.635Z"/></svg>\n <span>Open in Telegram</span>\n </a>\n </div>\n <div data-panel="qr" style="display:none">\n <div class="wc-qr-container">\n <div class="wc-qr-wrap">\n <img src="${i}" width="200" height="200" alt="QR" />\n </div>\n <div class="wc-qr-hint">Scan with your phone camera or wallet app</div>\n </div>\n </div>\n <div class="wc-status">\n <div class="wc-spinner"></div>\n <span>Waiting for confirmation...</span>\n </div>\n </div>\n <div class="wc-footer">Powered by ${e.name}</div>\n `;this.render(o);const r=this.overlay?.querySelectorAll(".wc-tab"),a=this.overlay?.querySelectorAll("[data-panel]");r?.forEach(e=>{e.addEventListener("click",()=>{const n=e.dataset.tab;r.forEach(e=>e.classList.remove("active")),e.classList.add("active"),a?.forEach(e=>{e.style.display=e.dataset.panel===n?"block":"none"})})}),t&&s&&this.overlay?.querySelector('[data-action="back"]')?.addEventListener("click",s)}showSuccess(e,n,t,s){const i=t?.username||"User",o=i.slice(0,2).toUpperCase(),r=`\n <div class="wc-header">\n <h3 class="wc-title">Connected</h3>\n <button class="wc-close" data-action="close">✕</button>\n </div>\n <div class="wc-body">\n <div class="wc-success">\n <div class="wc-success-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></div>\n <div class="wc-success-title">Connection Successful</div>\n <div class="wc-success-user">\n ${t?.avatar?`<img class="wc-success-avatar" src="${t.avatar}" alt="${i}" />`:`<div class="wc-success-avatar-placeholder">${o}</div>`}\n <div class="wc-success-user-info">\n <div class="wc-success-user-name">${i}</div>\n ${n?`<div class="wc-success-user-addr" title="${n}">${n}</div>`:""}\n </div>\n </div>\n <button class="wc-success-done" data-action="done"><span>Done</span></button>\n </div>\n </div>\n <div class="wc-footer">Powered by ${e.name}</div>\n `;this.render(r),this.overlay?.querySelector('[data-action="done"]')?.addEventListener("click",s)}updateStatus(e,n=!0){const t=this.overlay?.querySelector(".wc-status");t&&(t.innerHTML=`\n ${n?'<div class="wc-spinner"></div>':""}\n <span>${e}</span>\n `)}render(e){this.destroy();const n=document.createElement("div");n.id=t,n.innerHTML=`\n <style>${function(e){const n="dark"===e,t=n?"#222834":"#f0f7ff",s=n?"rgba(34,40,52,0.9)":"rgba(255,255,255,0.9)",i=n?"#f0f4f8":"#1a1f2e",o=n?"#6b7a8d":"#6b7c8d",r=n?"#2d3545":"#e0eaf4",a="#2bea6a",c="linear-gradient(135deg, #1db954, #2bea6a, #4af58a)",l="rgba(43,234,106,0.15)",d="rgba(43,234,106,0.3)";return`\n @keyframes wc-fadeIn { from{opacity:0} to{opacity:1} }\n @keyframes wc-slideUp {\n from{opacity:0;transform:translateY(40px) scale(0.96)}\n to{opacity:1;transform:translateY(0) scale(1)}\n }\n @keyframes wc-float {\n 0%,100%{transform:translateY(0)}\n 50%{transform:translateY(-6px)}\n }\n @keyframes wc-glow {\n 0%,100%{box-shadow:0 0 20px ${l}}\n 50%{box-shadow:0 0 40px ${d}}\n }\n @keyframes wc-popIn {\n from{transform:scale(0);opacity:0}\n 50%{transform:scale(1.15)}\n to{transform:scale(1);opacity:1}\n }\n @keyframes wc-spin { to{transform:rotate(360deg)} }\n @keyframes wc-shimmer {\n 0%{background-position:-200% 0}\n 100%{background-position:200% 0}\n }\n\n .wc-overlay {\n position:fixed; inset:0; z-index:99999;\n background:rgba(0,0,0,${n?.6:.35});\n display:flex; align-items:center; justify-content:center;\n font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\n backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);\n animation:wc-fadeIn .25s ease;\n }\n .wc-modal {\n background:${n?"linear-gradient(180deg, #1a1f2e 0%, #222834 100%)":"linear-gradient(135deg, #f0f7ff 0%, #e8f5e9 50%, #f0f7ff 100%)"}; width:100%; max-width:400px;\n border-radius:28px; overflow:hidden; color:${i};\n border:1px solid ${r};\n box-shadow:0 24px 80px rgba(0,0,0,${n?.5:.12}),\n 0 0 0 1px ${n?"rgba(255,255,255,0.04)":"rgba(0,0,0,0.03)"};\n animation:wc-slideUp .35s cubic-bezier(.16,1,.3,1);\n max-height:90vh; overflow-y:auto; position:relative;\n }\n\n /* Decorative glow orb */\n .wc-modal::before {\n content:''; position:absolute; top:-60px; right:-60px;\n width:180px; height:180px; border-radius:50%;\n background:radial-gradient(circle, ${l} 0%, transparent 70%);\n pointer-events:none; z-index:0;\n }\n\n /* Header */\n .wc-header {\n display:flex; align-items:center; justify-content:space-between;\n padding:24px 24px 0; position:relative; z-index:1;\n }\n .wc-header-left {\n display:flex; align-items:center; gap:10px;\n }\n .wc-header-icon {\n width:36px; height:36px; border-radius:12px; object-fit:cover;\n border:2px solid ${r}; box-shadow:0 2px 8px rgba(0,0,0,0.1);\n }\n .wc-title {\n font-size:18px; font-weight:800; margin:0;\n background:${c}; -webkit-background-clip:text;\n -webkit-text-fill-color:transparent; background-clip:text;\n }\n .wc-close {\n width:32px; height:32px; display:flex; align-items:center; justify-content:center;\n background:${t}; border:1px solid ${r}; border-radius:50%;\n cursor:pointer; font-size:14px; color:${o};\n transition:all .2s;\n }\n .wc-close:hover { background:${r}; color:${i}; transform:rotate(90deg); }\n\n .wc-subtitle {\n font-size:13px; color:${o}; margin:12px 24px 0;\n line-height:1.5; position:relative; z-index:1;\n }\n\n .wc-body { padding:20px 24px 24px; position:relative; z-index:1; }\n\n /* Mascot */\n .wc-mascot {\n display:flex; justify-content:center; margin:8px 0 4px;\n }\n .wc-mascot img {\n width:100px; height:100px; object-fit:contain;\n animation:wc-float 3s ease-in-out infinite;\n filter:drop-shadow(0 8px 24px rgba(43,234,106,0.2));\n }\n\n /* Wallet list */\n .wc-wallet-list { list-style:none; margin:0; padding:0; }\n .wc-wallet-item {\n display:flex; align-items:center; gap:14px;\n padding:14px 16px; border-radius:16px; cursor:pointer;\n background:${s}; border:1.5px solid ${r};\n margin-bottom:10px; transition:all .2s; position:relative;\n overflow:hidden;\n }\n .wc-wallet-item::before {\n content:''; position:absolute; inset:0;\n background:${c}; opacity:0;\n transition:opacity .2s; border-radius:16px;\n }\n .wc-wallet-item:hover::before { opacity:0.06; }\n .wc-wallet-item:hover {\n border-color:${a}; transform:translateY(-2px);\n box-shadow:0 8px 24px ${l};\n }\n .wc-wallet-list-icon {\n width:44px; height:44px; border-radius:14px; object-fit:cover;\n position:relative; z-index:1;\n border:2px solid ${r}; box-shadow:0 2px 8px rgba(0,0,0,0.08);\n }\n .wc-wallet-list-icon-placeholder {\n width:44px; height:44px; border-radius:14px;\n background:${c};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:18px;\n position:relative; z-index:1;\n }\n .wc-wallet-item-info { flex:1; position:relative; z-index:1; }\n .wc-wallet-item-name { font-size:15px; font-weight:700; }\n .wc-wallet-item-desc { font-size:12px; color:${o}; margin-top:2px; }\n .wc-wallet-item-arrow {\n color:${a}; font-size:18px; font-weight:700;\n position:relative; z-index:1; transition:transform .2s;\n }\n .wc-wallet-item:hover .wc-wallet-item-arrow { transform:translateX(4px); }\n\n /* Grid picker */\n .wc-wallet-grid {\n display:grid; grid-template-columns:repeat(3,1fr);\n gap:12px; list-style:none; margin:0; padding:0;\n }\n .wc-wallet-grid-item {\n display:flex; flex-direction:column; align-items:center;\n gap:10px; cursor:pointer; padding:16px 8px; border-radius:20px;\n background:${s}; border:1.5px solid ${r};\n transition:all .2s;\n }\n .wc-wallet-grid-item:hover {\n border-color:${a}; transform:translateY(-3px);\n box-shadow:0 8px 24px ${l};\n }\n .wc-wallet-grid-icon {\n width:52px; height:52px; border-radius:16px;\n object-fit:cover; border:2px solid ${r};\n box-shadow:0 4px 12px rgba(0,0,0,0.08);\n }\n .wc-wallet-grid-icon-placeholder {\n width:52px; height:52px; border-radius:16px;\n background:${c};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:20px;\n }\n .wc-wallet-grid-name {\n font-size:12px; font-weight:600; text-align:center;\n color:${i}; line-height:1.3;\n }\n\n /* Tabs */\n .wc-tabs {\n display:flex; background:${t}; border-radius:14px;\n padding:4px; margin-bottom:20px; border:1px solid ${r};\n }\n .wc-tab {\n flex:1; padding:10px; text-align:center; cursor:pointer;\n font-size:13px; font-weight:700; background:transparent;\n border:none; color:${o}; border-radius:11px;\n transition:all .25s;\n }\n .wc-tab:hover { color:${i}; }\n .wc-tab.active {\n background:${c}; color:#fff;\n box-shadow:0 4px 16px ${l};\n }\n\n /* Deeplink */\n .wc-deeplink-btn {\n display:flex; align-items:center; justify-content:center; gap:10px;\n width:100%; padding:16px; border:none; border-radius:16px;\n background:${c}; color:#fff;\n font-size:15px; font-weight:800; cursor:pointer;\n text-decoration:none; transition:all .25s;\n box-shadow:0 4px 24px ${l};\n box-sizing:border-box; position:relative; overflow:hidden;\n }\n .wc-deeplink-btn::after {\n content:''; position:absolute; inset:0;\n background:linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n background-size:200% 100%;\n animation:wc-shimmer 2.5s ease-in-out infinite;\n }\n .wc-deeplink-btn:hover {\n transform:translateY(-2px);\n box-shadow:0 8px 32px ${d};\n }\n .wc-deeplink-btn svg { position:relative; z-index:1; }\n .wc-deeplink-btn span { position:relative; z-index:1; }\n\n /* QR */\n .wc-qr-container {\n display:flex; flex-direction:column; align-items:center; gap:16px;\n }\n .wc-qr-wrap {\n padding:16px; background:#fff; border-radius:20px;\n box-shadow:0 4px 24px rgba(0,0,0,${n?.3:.08});\n border:2px solid ${l};\n }\n .wc-qr-wrap img { display:block; border-radius:8px; }\n .wc-qr-hint { font-size:12px; color:${o}; text-align:center; }\n\n /* Status */\n .wc-status {\n display:flex; align-items:center; justify-content:center;\n gap:10px; padding:20px 0 4px; font-size:13px; color:${o};\n }\n .wc-spinner {\n width:18px; height:18px; border:2.5px solid ${r};\n border-top-color:${a}; border-radius:50%;\n animation:wc-spin .7s linear infinite;\n }\n\n /* Back */\n .wc-back {\n background:none; border:none; cursor:pointer; padding:0;\n color:${o}; display:flex; align-items:center;\n transition:all .2s; font-size:18px;\n }\n .wc-back:hover { color:${a}; transform:translateX(-3px); }\n\n /* Footer */\n .wc-footer {\n text-align:center; padding:4px 0 20px;\n font-size:11px; color:${n?"#3d5468":"#b0bec5"};\n position:relative; z-index:1;\n }\n\n /* ── Success screen ── */\n .wc-success {\n display:flex; flex-direction:column; align-items:center;\n padding:8px 0 4px; gap:20px;\n }\n .wc-success-check {\n width:72px; height:72px; border-radius:50%;\n background:${c};\n display:flex; align-items:center; justify-content:center;\n box-shadow:0 8px 32px ${d};\n animation:wc-popIn .4s cubic-bezier(.16,1,.3,1), wc-glow 2s ease-in-out infinite;\n }\n .wc-success-check svg { width:36px; height:36px; color:#fff; }\n .wc-success-title {\n font-size:20px; font-weight:800;\n background:${c}; -webkit-background-clip:text;\n -webkit-text-fill-color:transparent; background-clip:text;\n }\n .wc-success-user {\n display:flex; align-items:center; gap:14px;\n background:${s}; border:1.5px solid ${r};\n border-radius:18px; padding:16px 18px; width:100%;\n box-sizing:border-box; backdrop-filter:blur(8px);\n }\n .wc-success-avatar {\n width:50px; height:50px; border-radius:50%; object-fit:cover;\n flex-shrink:0; border:2.5px solid ${a};\n box-shadow:0 4px 16px ${l};\n }\n .wc-success-avatar-placeholder {\n width:50px; height:50px; border-radius:50%;\n background:${c};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:18px; flex-shrink:0;\n box-shadow:0 4px 16px ${l};\n }\n .wc-success-user-info { flex:1; min-width:0; }\n .wc-success-user-name { font-size:16px; font-weight:700; color:${i}; }\n .wc-success-user-addr {\n font-size:11px; color:${o}; margin-top:4px;\n font-family:'SF Mono',Monaco,Consolas,monospace;\n word-break:break-all; line-height:1.4;\n }\n .wc-success-done {\n width:100%; padding:16px; border:none; border-radius:16px;\n background:${c}; color:#fff;\n font-size:15px; font-weight:800; cursor:pointer;\n transition:all .25s; position:relative; overflow:hidden;\n box-shadow:0 4px 24px ${l};\n }\n .wc-success-done::after {\n content:''; position:absolute; inset:0;\n background:linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n background-size:200% 100%;\n animation:wc-shimmer 2.5s ease-in-out infinite;\n }\n .wc-success-done:hover {\n transform:translateY(-2px);\n box-shadow:0 8px 32px ${d};\n }\n `}(this.theme)}</style>\n <div class="wc-overlay">\n <div class="wc-modal">${e}</div>\n </div>\n `,document.body.appendChild(n),this.overlay=n,this.overlay.querySelector('[data-action="close"]')?.addEventListener("click",()=>{this.onClose(),this.destroy()}),this.overlay.querySelector(".wc-overlay")?.addEventListener("click",e=>{e.target.classList.contains("wc-overlay")&&(this.onClose(),this.destroy())})}destroy(){document.getElementById(t)?.remove(),this.overlay=null}}const i="__wc_session_";class o{constructor(e){this.listeners=new Map,this.modal=null,this.wsTransport=null,this.pollingTransport=null,this.currentResult=null,this.config={appId:e.appId,registryUrl:e.registryUrl||"https://cdn.jsdelivr.net/npm/toncorp-wallet-connect-sdk@latest/registry.json",walletId:e.walletId,theme:e.theme||"dark",siteTmaUrl:e.siteTmaUrl},this.restoreSession(),this.registerGlobal()}async connect(){if(this.currentResult)return this.currentResult;const e=await this.fetchWallets(),n=this.filterWallets(e);if(0===n.length)throw new Error("No supported wallets available");return new Promise((e,t)=>{this.modal=new s(this.config.theme,()=>{this.cleanup(),t(new Error("User closed modal"))}),1===n.length?this.startConnect(n[0],!1,e,t):this.modal.showWalletPicker(n,n=>{this.startConnect(n,!0,e,t)})})}disconnect(){let e=this.currentResult?.session;if(!e&&"undefined"!=typeof window)for(const n of Object.keys(window)){const t=window[n];if(t instanceof o&&t.currentResult?.session){e=t.currentResult.session;break}}if(e){const n=e.apiUrl.replace(/\/$/,"");fetch(`${n}/connect/disconnect-by-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionToken:e.sessionToken})}).catch(()=>{})}this.currentResult=null,this.removeStoredSession(),this.clearAllRelatedSessions(),this.cleanup(),this.emit("disconnect")}async sendTransaction(e){const n=this.currentResult?.session;if(!n?.apiUrl)throw new Error("Wallet not connected");const t=n.apiUrl.replace(/\/$/,"");let s;try{s=await fetch(`${t}/dex/sendTx`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...e,appId:this.config.appId,source:"widget"})})}catch{throw new Error("Connection failed")}if(401===s.status)throw new Error("Wallet not connected");if(429===s.status)throw new Error("Too many pending transactions");if(!s.ok)throw new Error("Failed to send transaction");return{txId:(await s.json()).txId}}async waitForResult(e,n){const t=this.currentResult?.session;if(!t?.apiUrl)throw new Error("Wallet not connected");const s=n?.interval??2e3,i=n?.timeout??12e4,o=t.apiUrl.replace(/\/$/,""),r=Date.now();for(;;){let n;try{n=await fetch(`${o}/dex/status/${e}`)}catch{throw new Error("Connection failed")}if(!n.ok)throw new Error("Failed to fetch transaction status");const t=await n.json(),a={status:t.status,msgHash:t.msgHash,action:t.action};if("PENDING"!==t.status)return this.emit("resolved",a),a;if(this.emit("pending",a),Date.now()-r>=i)throw new Error("Timeout");await this.sleep(s)}}isConnected(){return null!==this.currentResult}getWallet(){return this.currentResult}on(e,n){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(n)}off(e,n){this.listeners.get(e)?.delete(n)}get storageKey(){const e=this.config.walletId,n=Array.isArray(e)?e.sort().join("_"):e||"all";return`${i}${n}__`}async fetchWallets(){const e=this.config.registryUrl.replace(/\/$/,""),n=await fetch(e);if(!n.ok)throw new Error("Failed to fetch wallets");return n.json()}filterWallets(e){if(!this.config.walletId)return e;const n=Array.isArray(this.config.walletId)?this.config.walletId:[this.config.walletId];return e.filter(e=>n.includes(e.walletId))}async startConnect(e,n,t,s){try{const i=await this.initSession(e);this.modal?.showConnect(e,i.deeplink,n,()=>{this.cleanup(),this.connect().then(t).catch(s)}),this.listenForConfirmation(e.apiUrl,i.sessionToken,n=>{n.session={sessionToken:i.sessionToken,walletId:e.walletId,apiUrl:e.apiUrl},this.currentResult=n,this.storeSession(n),this.syncToWalletInstance(e.walletId,n),this.emit("connect",n),this.modal?.showSuccess(e,n.wallet?.address||null,n.user||null,()=>{this.modal?.destroy(),this.modal=null,t(n)})},()=>{this.modal?.updateStatus("Connection rejected",!1),setTimeout(()=>{this.modal?.destroy(),this.modal=null,s(new Error("Connection rejected by user"))},1500)},()=>{this.modal?.updateStatus("Session expired",!1),this.emit("session_expired"),setTimeout(()=>{this.modal?.destroy(),this.modal=null,s(new Error("Session expired"))},1500)})}catch(e){this.modal?.destroy(),this.modal=null,s(e instanceof Error?e:new Error(String(e)))}}async initSession(e){const n=e.apiUrl.replace(/\/$/,""),t=await fetch(`${n}/connect/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.config.appId,walletId:e.walletId,origin:"undefined"!=typeof window?window.location.origin:"",siteTmaUrl:this.config.siteTmaUrl})});if(!t.ok)throw new Error("Failed to init session");return t.json()}listenForConfirmation(t,s,i,o,r){let a=!1;const c=e=>(...n)=>{a||(a=!0,this.wsTransport?.close(),this.pollingTransport?.stop(),e(...n))},l=c(i),d=c(o),p=c(r);this.pollingTransport=new n(t,s,l,d,p,e=>this.emit("error",e)),this.pollingTransport.start(),this.wsTransport=new e(t,s,l,d,p,()=>{}),this.wsTransport.connect()}cleanup(){this.wsTransport?.close(),this.wsTransport=null,this.pollingTransport?.stop(),this.pollingTransport=null,this.modal?.destroy(),this.modal=null}emit(e,n){this.listeners.get(e)?.forEach(e=>e(n))}sleep(e){return new Promise(n=>setTimeout(n,e))}storeSession(e){try{localStorage.setItem(this.storageKey,JSON.stringify(e))}catch{}}restoreSession(){try{const e=localStorage.getItem(this.storageKey);e&&(this.currentResult=JSON.parse(e))}catch{}}removeStoredSession(){try{localStorage.removeItem(this.storageKey)}catch{}}registerGlobal(){if("undefined"==typeof window)return;const e=this.config.walletId,n=Array.isArray(e)?e[0]:e||"walletconnect";window[n]=this}syncToWalletInstance(e,n){if("undefined"==typeof window)return;const t=window[e];t&&t!==this&&t instanceof o&&(t.currentResult=n,t.storeSession(n))}clearAllRelatedSessions(){if("undefined"!=typeof window){try{for(const e of Object.keys(window)){const n=window[e];n&&n!==this&&n instanceof o&&(n.currentResult=null,n.removeStoredSession())}}catch{}try{const e=[];for(let n=0;n<localStorage.length;n++){const t=localStorage.key(n);t&&t.startsWith(i)&&e.push(t)}e.forEach(e=>localStorage.removeItem(e))}catch{}}}}exports.BaseWalletConnect=o,exports.OneDollarConnect=class extends o{constructor(e){super({...e,walletId:"onedollar"})}},exports.TonPassConnect=class extends o{constructor(e){super({...e,walletId:"tonpass"})}};
|
|
1
|
+
"use strict";class e{constructor(e,n,t,s,i,o){this.apiUrl=e,this.sessionToken=n,this.onConfirmed=t,this.onRejected=s,this.onExpired=i,this.onError=o,this.ws=null,this.reconnectTimer=null,this.closed=!1}connect(){this.closed=!1;this.apiUrl.replace(/^http/,"ws").replace(/\/$/,"");this.sessionToken;try{this.connectSocketIO()}catch{this.onError("WebSocket connection failed")}}connectSocketIO(){const e=this.apiUrl.replace(/\/$/,"");fetch(`${e}/walletconnect/?EIO=4&transport=polling`).then(e=>e.text()).then(n=>{const t=n.replace(/^\d+/,""),s=JSON.parse(t).sid,i=`${e.replace(/^http/,"ws")}/walletconnect/?EIO=4&transport=websocket&sid=${s}`;this.ws=new WebSocket(i),this.ws.onopen=()=>{this.ws.send("2probe")},this.ws.onmessage=e=>{const n=e.data;if("3probe"!==n)if(n.startsWith("42"))try{const e=JSON.parse(n.slice(2)),[t,s]=e;this.handleEvent(t,s)}catch{}else"2"===n&&this.ws.send("3");else this.ws.send("5")},this.ws.onclose=()=>{this.closed||this.scheduleReconnect()},this.ws.onerror=()=>{this.closed||this.scheduleReconnect()}}).catch(()=>{this.closed||this.scheduleReconnect()})}handleEvent(e,n){switch(e){case"session_confirmed":this.onConfirmed(n),this.close();break;case"session_rejected":this.onRejected(),this.close();break;case"session_expired":this.onExpired(),this.close()}}scheduleReconnect(){this.closed||this.reconnectTimer||(this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connectSocketIO()},3e3))}close(){this.closed=!0,this.reconnectTimer&&(clearTimeout(this.reconnectTimer),this.reconnectTimer=null),this.ws&&(this.ws.close(),this.ws=null)}}class n{constructor(e,n,t,s,i,o){this.apiUrl=e,this.sessionToken=n,this.onConfirmed=t,this.onRejected=s,this.onExpired=i,this.onError=o,this.timer=null,this.stopped=!1}start(){this.stopped=!1,this.poll(),this.timer=setInterval(()=>this.poll(),2e3)}async poll(){if(!this.stopped)try{const e=this.apiUrl.replace(/\/$/,""),n=await fetch(`${e}/connect/status?token=${this.sessionToken}`);if(!n.ok)return;const t=await n.json();switch(t.status){case"CONFIRMED":this.onConfirmed({wallet:t.wallet,user:t.user}),this.stop();break;case"REJECTED":this.onRejected(),this.stop();break;case"EXPIRED":this.onExpired(),this.stop()}}catch{}}stop(){this.stopped=!0,this.timer&&(clearInterval(this.timer),this.timer=null)}}const t="__wc_modal__";class s{constructor(e,n){this.overlay=null,this.theme=e,this.onClose=n}showWalletPicker(e,n){const t=`\n <div class="wc-header">\n <h3 class="wc-title">Connect Wallet</h3>\n <button class="wc-close" data-action="close">✕</button>\n </div>\n <div class="wc-mascot">\n <img src="https://dailytrip.1dollar.day/assets/character-mascot.png" alt="mascot" />\n </div>\n <p class="wc-subtitle">Choose a wallet to get started</p>\n <div class="wc-body">${e.length>=3?`<ul class="wc-wallet-grid">${e.map(e=>`\n <li class="wc-wallet-grid-item" data-wallet-id="${e.walletId}">\n ${e.icon?`<img class="wc-wallet-grid-icon" src="${e.icon}" alt="${e.name}" />`:`<div class="wc-wallet-grid-icon-placeholder">${e.name.charAt(0)}</div>`}\n <span class="wc-wallet-grid-name">${e.name}</span>\n </li>`).join("")}</ul>`:`<ul class="wc-wallet-list">${e.map(e=>`\n <li class="wc-wallet-item" data-wallet-id="${e.walletId}">\n ${e.icon?`<img class="wc-wallet-list-icon" src="${e.icon}" alt="${e.name}" />`:`<div class="wc-wallet-list-icon-placeholder">${e.name.charAt(0)}</div>`}\n <div class="wc-wallet-item-info">\n <div class="wc-wallet-item-name">${e.name}</div>\n ${e.description?`<div class="wc-wallet-item-desc">${e.description}</div>`:""}\n </div>\n <span class="wc-wallet-item-arrow">›</span>\n </li>`).join("")}</ul>`}</div>\n <div class="wc-footer">Powered by TonCorp</div>\n `;this.render(t),e.forEach(e=>{this.overlay?.querySelector(`[data-wallet-id="${e.walletId}"]`)?.addEventListener("click",()=>n(e))})}showConnect(e,n,t,s){const i=function(e,n=220){return`https://api.qrserver.com/v1/create-qr-code/?size=${n}x${n}&data=${encodeURIComponent(e)}&margin=8`}(n),o=`\n <div class="wc-header">\n <div class="wc-header-left">\n ${t?'<button class="wc-back" data-action="back">←</button>':""}\n ${e.icon?`<img class="wc-header-icon" src="${e.icon}" />`:""}\n <h3 class="wc-title">${e.name}</h3>\n </div>\n <button class="wc-close" data-action="close">✕</button>\n </div>\n <p class="wc-subtitle">Approve the connection in your wallet app</p>\n <div class="wc-body">\n <div class="wc-tabs">\n <button class="wc-tab active" data-tab="mobile"><span>Mobile</span></button>\n <button class="wc-tab" data-tab="qr"><span>QR Code</span></button>\n </div>\n <div data-panel="mobile">\n <a class="wc-deeplink-btn" href="${n}" target="_blank">\n <svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor"><path d="M11.944 0A12 12 0 1 0 24 12.056A12.01 12.01 0 0 0 11.944 0Zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472c-.18 1.898-.962 6.502-1.36 8.627c-.168.9-.499 1.201-.82 1.23c-.696.065-1.225-.46-1.9-.902c-1.056-.693-1.653-1.124-2.678-1.8c-1.185-.78-.417-1.21.258-1.91c.177-.184 3.247-2.977 3.307-3.23c.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345c-.48.33-.913.49-1.302.48c-.428-.008-1.252-.241-1.865-.44c-.752-.245-1.349-.374-1.297-.789c.027-.216.325-.437.893-.663c3.498-1.524 5.83-2.529 6.998-3.014c3.332-1.386 4.025-1.627 4.476-1.635Z"/></svg>\n <span>Open in Telegram</span>\n </a>\n </div>\n <div data-panel="qr" style="display:none">\n <div class="wc-qr-container">\n <div class="wc-qr-wrap">\n <img src="${i}" width="200" height="200" alt="QR" />\n </div>\n <div class="wc-qr-hint">Scan with your phone camera or wallet app</div>\n </div>\n </div>\n <div class="wc-status">\n <div class="wc-spinner"></div>\n <span>Waiting for confirmation...</span>\n </div>\n </div>\n <div class="wc-footer">Powered by ${e.name}</div>\n `;this.render(o);const r=this.overlay?.querySelectorAll(".wc-tab"),a=this.overlay?.querySelectorAll("[data-panel]");r?.forEach(e=>{e.addEventListener("click",()=>{const n=e.dataset.tab;r.forEach(e=>e.classList.remove("active")),e.classList.add("active"),a?.forEach(e=>{e.style.display=e.dataset.panel===n?"block":"none"})})}),t&&s&&this.overlay?.querySelector('[data-action="back"]')?.addEventListener("click",s)}showSuccess(e,n,t,s){const i=t?.username||"User",o=i.slice(0,2).toUpperCase(),r=`\n <div class="wc-header">\n <h3 class="wc-title">Connected</h3>\n <button class="wc-close" data-action="close">✕</button>\n </div>\n <div class="wc-body">\n <div class="wc-success">\n <div class="wc-success-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg></div>\n <div class="wc-success-title">Connection Successful</div>\n <div class="wc-success-user">\n ${t?.avatar?`<img class="wc-success-avatar" src="${t.avatar}" alt="${i}" />`:`<div class="wc-success-avatar-placeholder">${o}</div>`}\n <div class="wc-success-user-info">\n <div class="wc-success-user-name">${i}</div>\n ${n?`<div class="wc-success-user-addr" title="${n}">${n}</div>`:""}\n </div>\n </div>\n <button class="wc-success-done" data-action="done"><span>Done</span></button>\n </div>\n </div>\n <div class="wc-footer">Powered by ${e.name}</div>\n `;this.render(r),this.overlay?.querySelector('[data-action="done"]')?.addEventListener("click",s)}updateStatus(e,n=!0){const t=this.overlay?.querySelector(".wc-status");t&&(t.innerHTML=`\n ${n?'<div class="wc-spinner"></div>':""}\n <span>${e}</span>\n `)}render(e){this.destroy();const n=document.createElement("div");n.id=t,n.innerHTML=`\n <style>${function(e){const n="dark"===e,t=n?"#222834":"#f0f7ff",s=n?"rgba(34,40,52,0.9)":"rgba(255,255,255,0.9)",i=n?"#f0f4f8":"#1a1f2e",o=n?"#6b7a8d":"#6b7c8d",r=n?"#2d3545":"#e0eaf4",a="#2bea6a",c="linear-gradient(135deg, #1db954, #2bea6a, #4af58a)",l="rgba(43,234,106,0.15)",d="rgba(43,234,106,0.3)";return`\n @keyframes wc-fadeIn { from{opacity:0} to{opacity:1} }\n @keyframes wc-slideUp {\n from{opacity:0;transform:translateY(40px) scale(0.96)}\n to{opacity:1;transform:translateY(0) scale(1)}\n }\n @keyframes wc-float {\n 0%,100%{transform:translateY(0)}\n 50%{transform:translateY(-6px)}\n }\n @keyframes wc-glow {\n 0%,100%{box-shadow:0 0 20px ${l}}\n 50%{box-shadow:0 0 40px ${d}}\n }\n @keyframes wc-popIn {\n from{transform:scale(0);opacity:0}\n 50%{transform:scale(1.15)}\n to{transform:scale(1);opacity:1}\n }\n @keyframes wc-spin { to{transform:rotate(360deg)} }\n @keyframes wc-shimmer {\n 0%{background-position:-200% 0}\n 100%{background-position:200% 0}\n }\n\n .wc-overlay {\n position:fixed; inset:0; z-index:99999;\n background:rgba(0,0,0,${n?.6:.35});\n display:flex; align-items:center; justify-content:center;\n font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\n backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);\n animation:wc-fadeIn .25s ease;\n }\n .wc-modal {\n background:${n?"linear-gradient(180deg, #1a1f2e 0%, #222834 100%)":"linear-gradient(135deg, #f0f7ff 0%, #e8f5e9 50%, #f0f7ff 100%)"}; width:100%; max-width:400px;\n border-radius:28px; overflow:hidden; color:${i};\n border:1px solid ${r};\n box-shadow:0 24px 80px rgba(0,0,0,${n?.5:.12}),\n 0 0 0 1px ${n?"rgba(255,255,255,0.04)":"rgba(0,0,0,0.03)"};\n animation:wc-slideUp .35s cubic-bezier(.16,1,.3,1);\n max-height:90vh; overflow-y:auto; position:relative;\n }\n\n /* Decorative glow orb */\n .wc-modal::before {\n content:''; position:absolute; top:-60px; right:-60px;\n width:180px; height:180px; border-radius:50%;\n background:radial-gradient(circle, ${l} 0%, transparent 70%);\n pointer-events:none; z-index:0;\n }\n\n /* Header */\n .wc-header {\n display:flex; align-items:center; justify-content:space-between;\n padding:24px 24px 0; position:relative; z-index:1;\n }\n .wc-header-left {\n display:flex; align-items:center; gap:10px;\n }\n .wc-header-icon {\n width:36px; height:36px; border-radius:12px; object-fit:cover;\n border:2px solid ${r}; box-shadow:0 2px 8px rgba(0,0,0,0.1);\n }\n .wc-title {\n font-size:18px; font-weight:800; margin:0;\n background:${c}; -webkit-background-clip:text;\n -webkit-text-fill-color:transparent; background-clip:text;\n }\n .wc-close {\n width:32px; height:32px; display:flex; align-items:center; justify-content:center;\n background:${t}; border:1px solid ${r}; border-radius:50%;\n cursor:pointer; font-size:14px; color:${o};\n transition:all .2s;\n }\n .wc-close:hover { background:${r}; color:${i}; transform:rotate(90deg); }\n\n .wc-subtitle {\n font-size:13px; color:${o}; margin:12px 24px 0;\n line-height:1.5; position:relative; z-index:1;\n }\n\n .wc-body { padding:20px 24px 24px; position:relative; z-index:1; }\n\n /* Mascot */\n .wc-mascot {\n display:flex; justify-content:center; margin:8px 0 4px;\n }\n .wc-mascot img {\n width:100px; height:100px; object-fit:contain;\n animation:wc-float 3s ease-in-out infinite;\n filter:drop-shadow(0 8px 24px rgba(43,234,106,0.2));\n }\n\n /* Wallet list */\n .wc-wallet-list { list-style:none; margin:0; padding:0; }\n .wc-wallet-item {\n display:flex; align-items:center; gap:14px;\n padding:14px 16px; border-radius:16px; cursor:pointer;\n background:${s}; border:1.5px solid ${r};\n margin-bottom:10px; transition:all .2s; position:relative;\n overflow:hidden;\n }\n .wc-wallet-item::before {\n content:''; position:absolute; inset:0;\n background:${c}; opacity:0;\n transition:opacity .2s; border-radius:16px;\n }\n .wc-wallet-item:hover::before { opacity:0.06; }\n .wc-wallet-item:hover {\n border-color:${a}; transform:translateY(-2px);\n box-shadow:0 8px 24px ${l};\n }\n .wc-wallet-list-icon {\n width:44px; height:44px; border-radius:14px; object-fit:cover;\n position:relative; z-index:1;\n border:2px solid ${r}; box-shadow:0 2px 8px rgba(0,0,0,0.08);\n }\n .wc-wallet-list-icon-placeholder {\n width:44px; height:44px; border-radius:14px;\n background:${c};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:18px;\n position:relative; z-index:1;\n }\n .wc-wallet-item-info { flex:1; position:relative; z-index:1; }\n .wc-wallet-item-name { font-size:15px; font-weight:700; }\n .wc-wallet-item-desc { font-size:12px; color:${o}; margin-top:2px; }\n .wc-wallet-item-arrow {\n color:${a}; font-size:18px; font-weight:700;\n position:relative; z-index:1; transition:transform .2s;\n }\n .wc-wallet-item:hover .wc-wallet-item-arrow { transform:translateX(4px); }\n\n /* Grid picker */\n .wc-wallet-grid {\n display:grid; grid-template-columns:repeat(3,1fr);\n gap:12px; list-style:none; margin:0; padding:0;\n }\n .wc-wallet-grid-item {\n display:flex; flex-direction:column; align-items:center;\n gap:10px; cursor:pointer; padding:16px 8px; border-radius:20px;\n background:${s}; border:1.5px solid ${r};\n transition:all .2s;\n }\n .wc-wallet-grid-item:hover {\n border-color:${a}; transform:translateY(-3px);\n box-shadow:0 8px 24px ${l};\n }\n .wc-wallet-grid-icon {\n width:52px; height:52px; border-radius:16px;\n object-fit:cover; border:2px solid ${r};\n box-shadow:0 4px 12px rgba(0,0,0,0.08);\n }\n .wc-wallet-grid-icon-placeholder {\n width:52px; height:52px; border-radius:16px;\n background:${c};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:20px;\n }\n .wc-wallet-grid-name {\n font-size:12px; font-weight:600; text-align:center;\n color:${i}; line-height:1.3;\n }\n\n /* Tabs */\n .wc-tabs {\n display:flex; background:${t}; border-radius:14px;\n padding:4px; margin-bottom:20px; border:1px solid ${r};\n }\n .wc-tab {\n flex:1; padding:10px; text-align:center; cursor:pointer;\n font-size:13px; font-weight:700; background:transparent;\n border:none; color:${o}; border-radius:11px;\n transition:all .25s;\n }\n .wc-tab:hover { color:${i}; }\n .wc-tab.active {\n background:${c}; color:#fff;\n box-shadow:0 4px 16px ${l};\n }\n\n /* Deeplink */\n .wc-deeplink-btn {\n display:flex; align-items:center; justify-content:center; gap:10px;\n width:100%; padding:16px; border:none; border-radius:16px;\n background:${c}; color:#fff;\n font-size:15px; font-weight:800; cursor:pointer;\n text-decoration:none; transition:all .25s;\n box-shadow:0 4px 24px ${l};\n box-sizing:border-box; position:relative; overflow:hidden;\n }\n .wc-deeplink-btn::after {\n content:''; position:absolute; inset:0;\n background:linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n background-size:200% 100%;\n animation:wc-shimmer 2.5s ease-in-out infinite;\n }\n .wc-deeplink-btn:hover {\n transform:translateY(-2px);\n box-shadow:0 8px 32px ${d};\n }\n .wc-deeplink-btn svg { position:relative; z-index:1; }\n .wc-deeplink-btn span { position:relative; z-index:1; }\n\n /* QR */\n .wc-qr-container {\n display:flex; flex-direction:column; align-items:center; gap:16px;\n }\n .wc-qr-wrap {\n padding:16px; background:#fff; border-radius:20px;\n box-shadow:0 4px 24px rgba(0,0,0,${n?.3:.08});\n border:2px solid ${l};\n }\n .wc-qr-wrap img { display:block; border-radius:8px; }\n .wc-qr-hint { font-size:12px; color:${o}; text-align:center; }\n\n /* Status */\n .wc-status {\n display:flex; align-items:center; justify-content:center;\n gap:10px; padding:20px 0 4px; font-size:13px; color:${o};\n }\n .wc-spinner {\n width:18px; height:18px; border:2.5px solid ${r};\n border-top-color:${a}; border-radius:50%;\n animation:wc-spin .7s linear infinite;\n }\n\n /* Back */\n .wc-back {\n background:none; border:none; cursor:pointer; padding:0;\n color:${o}; display:flex; align-items:center;\n transition:all .2s; font-size:18px;\n }\n .wc-back:hover { color:${a}; transform:translateX(-3px); }\n\n /* Footer */\n .wc-footer {\n text-align:center; padding:4px 0 20px;\n font-size:11px; color:${n?"#3d5468":"#b0bec5"};\n position:relative; z-index:1;\n }\n\n /* ── Success screen ── */\n .wc-success {\n display:flex; flex-direction:column; align-items:center;\n padding:8px 0 4px; gap:20px;\n }\n .wc-success-check {\n width:72px; height:72px; border-radius:50%;\n background:${c};\n display:flex; align-items:center; justify-content:center;\n box-shadow:0 8px 32px ${d};\n animation:wc-popIn .4s cubic-bezier(.16,1,.3,1), wc-glow 2s ease-in-out infinite;\n }\n .wc-success-check svg { width:36px; height:36px; color:#fff; }\n .wc-success-title {\n font-size:20px; font-weight:800;\n background:${c}; -webkit-background-clip:text;\n -webkit-text-fill-color:transparent; background-clip:text;\n }\n .wc-success-user {\n display:flex; align-items:center; gap:14px;\n background:${s}; border:1.5px solid ${r};\n border-radius:18px; padding:16px 18px; width:100%;\n box-sizing:border-box; backdrop-filter:blur(8px);\n }\n .wc-success-avatar {\n width:50px; height:50px; border-radius:50%; object-fit:cover;\n flex-shrink:0; border:2.5px solid ${a};\n box-shadow:0 4px 16px ${l};\n }\n .wc-success-avatar-placeholder {\n width:50px; height:50px; border-radius:50%;\n background:${c};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:18px; flex-shrink:0;\n box-shadow:0 4px 16px ${l};\n }\n .wc-success-user-info { flex:1; min-width:0; }\n .wc-success-user-name { font-size:16px; font-weight:700; color:${i}; }\n .wc-success-user-addr {\n font-size:11px; color:${o}; margin-top:4px;\n font-family:'SF Mono',Monaco,Consolas,monospace;\n word-break:break-all; line-height:1.4;\n }\n .wc-success-done {\n width:100%; padding:16px; border:none; border-radius:16px;\n background:${c}; color:#fff;\n font-size:15px; font-weight:800; cursor:pointer;\n transition:all .25s; position:relative; overflow:hidden;\n box-shadow:0 4px 24px ${l};\n }\n .wc-success-done::after {\n content:''; position:absolute; inset:0;\n background:linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n background-size:200% 100%;\n animation:wc-shimmer 2.5s ease-in-out infinite;\n }\n .wc-success-done:hover {\n transform:translateY(-2px);\n box-shadow:0 8px 32px ${d};\n }\n `}(this.theme)}</style>\n <div class="wc-overlay">\n <div class="wc-modal">${e}</div>\n </div>\n `,document.body.appendChild(n),this.overlay=n,this.overlay.querySelector('[data-action="close"]')?.addEventListener("click",()=>{this.onClose(),this.destroy()}),this.overlay.querySelector(".wc-overlay")?.addEventListener("click",e=>{e.target.classList.contains("wc-overlay")&&(this.onClose(),this.destroy())})}destroy(){document.getElementById(t)?.remove(),this.overlay=null}}const i="__wc_session_";class o{constructor(e){this.listeners=new Map,this.modal=null,this.wsTransport=null,this.pollingTransport=null,this.currentResult=null,this.config={appId:e.appId,registryUrl:e.registryUrl||"https://cdn.jsdelivr.net/npm/toncorp-wallet-connect-sdk@latest/registry.json",walletId:e.walletId,theme:e.theme||"dark",siteTmaUrl:e.siteTmaUrl},this.restoreSession(),this.registerGlobal()}async connect(){if(this.currentResult)return this.currentResult;const e=await this.fetchWallets(),n=this.filterWallets(e);if(0===n.length)throw new Error("No supported wallets available");return new Promise((e,t)=>{this.modal=new s(this.config.theme,()=>{this.cleanup(),t(new Error("User closed modal"))}),1===n.length?this.startConnect(n[0],!1,e,t):this.modal.showWalletPicker(n,n=>{this.startConnect(n,!0,e,t)})})}disconnect(){let e=this.currentResult?.session;if(!e&&"undefined"!=typeof window)for(const n of Object.keys(window)){const t=window[n];if(t instanceof o&&t.currentResult?.session){e=t.currentResult.session;break}}if(e){const n=e.apiUrl.replace(/\/$/,"");fetch(`${n}/connect/disconnect-by-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sessionToken:e.sessionToken})}).catch(()=>{})}this.currentResult=null,this.removeStoredSession(),this.clearAllRelatedSessions(),this.cleanup(),this.emit("disconnect")}async sendTransaction(e){const n=this.currentResult?.session;if(!n?.apiUrl)throw new Error("Wallet not connected");const t=n.apiUrl.replace(/\/$/,"");let s;try{s=await fetch(`${t}/dex/sendTx`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({...e,appId:this.config.appId,source:"widget"})})}catch{throw new Error("Connection failed")}if(401===s.status)throw new Error("Wallet not connected");if(429===s.status)throw new Error("Too many pending transactions");if(!s.ok)throw new Error("Failed to send transaction");const i=await s.json();return this.storePending({type:"tx",txId:i.txId,apiUrl:n.apiUrl,ts:Date.now()}),{txId:i.txId}}async waitForResult(e,n){const t=this.currentResult?.session;if(!t?.apiUrl)throw new Error("Wallet not connected");const s=n?.interval??2e3,i=n?.timeout??12e4,o=t.apiUrl.replace(/\/$/,""),r=Date.now();for(;;){let n;try{n=await fetch(`${o}/dex/status/${e}`)}catch{throw new Error("Connection failed")}if(!n.ok)throw new Error("Failed to fetch transaction status");const t=await n.json(),a={status:t.status,msgHash:t.msgHash,action:t.action};if("PENDING"!==t.status)return this.clearPending(),this.emit("resolved",a),a;if(this.emit("pending",a),Date.now()-r>=i)throw new Error("Timeout");await this.sleep(s)}}isConnected(){return null!==this.currentResult}getWallet(){return this.currentResult}on(e,n){this.listeners.has(e)||this.listeners.set(e,new Set),this.listeners.get(e).add(n)}off(e,n){this.listeners.get(e)?.delete(n)}get storageKey(){const e=this.config.walletId,n=Array.isArray(e)?e.sort().join("_"):e||"all";return`${i}${n}__`}async fetchWallets(){const e=this.config.registryUrl.replace(/\/$/,""),n=await fetch(e);if(!n.ok)throw new Error("Failed to fetch wallets");return n.json()}filterWallets(e){if(!this.config.walletId)return e;const n=Array.isArray(this.config.walletId)?this.config.walletId:[this.config.walletId];return e.filter(e=>n.includes(e.walletId))}async startConnect(e,n,t,s){try{const i=await this.initSession(e);this.storePending({type:"connect",sessionToken:i.sessionToken,walletId:e.walletId,apiUrl:e.apiUrl,ts:Date.now()}),this.modal?.showConnect(e,i.deeplink,n,()=>{this.cleanup(),this.connect().then(t).catch(s)}),this.listenForConfirmation(e.apiUrl,i.sessionToken,n=>{n.session={sessionToken:i.sessionToken,walletId:e.walletId,apiUrl:e.apiUrl},this.currentResult=n,this.storeSession(n),this.clearPending(),this.syncToWalletInstance(e.walletId,n),this.emit("connect",n),this.modal?.showSuccess(e,n.wallet?.address||null,n.user||null,()=>{this.modal?.destroy(),this.modal=null,t(n)})},()=>{this.clearPending(),this.modal?.updateStatus("Connection rejected",!1),setTimeout(()=>{this.modal?.destroy(),this.modal=null,s(new Error("Connection rejected by user"))},1500)},()=>{this.clearPending(),this.modal?.updateStatus("Session expired",!1),this.emit("session_expired"),setTimeout(()=>{this.modal?.destroy(),this.modal=null,s(new Error("Session expired"))},1500)})}catch(e){this.modal?.destroy(),this.modal=null,s(e instanceof Error?e:new Error(String(e)))}}async initSession(e){const n=e.apiUrl.replace(/\/$/,""),t=await fetch(`${n}/connect/init`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({appId:this.config.appId,walletId:e.walletId,origin:"undefined"!=typeof window?window.location.origin:"",siteTmaUrl:this.config.siteTmaUrl,platform:"undefined"!=typeof window&&window.Telegram?.WebApp?.platform||""})});if(!t.ok)throw new Error("Failed to init session");return t.json()}listenForConfirmation(t,s,i,o,r){let a=!1;const c=e=>(...n)=>{a||(a=!0,this.wsTransport?.close(),this.pollingTransport?.stop(),e(...n))},l=c(i),d=c(o),p=c(r);this.pollingTransport=new n(t,s,l,d,p,e=>this.emit("error",e)),this.pollingTransport.start(),this.wsTransport=new e(t,s,l,d,p,()=>{}),this.wsTransport.connect()}cleanup(){this.wsTransport?.close(),this.wsTransport=null,this.pollingTransport?.stop(),this.pollingTransport=null,this.modal?.destroy(),this.modal=null}emit(e,n){this.listeners.get(e)?.forEach(e=>e(n))}sleep(e){return new Promise(n=>setTimeout(n,e))}storeSession(e){try{localStorage.setItem(this.storageKey,JSON.stringify(e))}catch{}}restoreSession(){try{const e=localStorage.getItem(this.storageKey);e&&(this.currentResult=JSON.parse(e))}catch{}}removeStoredSession(){try{localStorage.removeItem(this.storageKey)}catch{}}get pendingKey(){return`${this.storageKey}pending`}storePending(e){try{localStorage.setItem(this.pendingKey,JSON.stringify(e))}catch{}}readPending(){try{const e=localStorage.getItem(this.pendingKey);return e?JSON.parse(e):null}catch{return null}}clearPending(){try{localStorage.removeItem(this.pendingKey)}catch{}}async resume(){const e=this.readPending();if(!e)return null;if(!e.ts||Date.now()-e.ts>3e5)return this.clearPending(),null;const n=String(e.apiUrl||"").replace(/\/$/,"");if(!n)return this.clearPending(),null;try{if("connect"===e.type){const t=await fetch(`${n}/connect/status?token=${e.sessionToken}`);if(!t.ok)return null;const s=await t.json();if("CONFIRMED"===s.status){const n={wallet:s.wallet??null,user:s.user??{},session:{sessionToken:e.sessionToken,walletId:e.walletId,apiUrl:e.apiUrl}};return this.currentResult=n,this.storeSession(n),this.syncToWalletInstance(e.walletId,n),this.clearPending(),{type:"connect",status:"CONFIRMED",result:n}}return"REJECTED"===s.status||"EXPIRED"===s.status?(this.clearPending(),{type:"connect",status:s.status}):null}if("tx"===e.type){const t=await fetch(`${n}/dex/status/${e.txId}`);if(!t.ok)return null;const s=await t.json();return"PENDING"!==s.status?(this.clearPending(),{type:"tx",status:s.status,result:{status:s.status,msgHash:s.msgHash,action:s.action}}):null}}catch{}return null}registerGlobal(){if("undefined"==typeof window)return;const e=this.config.walletId,n=Array.isArray(e)?e[0]:e||"walletconnect";window[n]=this}syncToWalletInstance(e,n){if("undefined"==typeof window)return;const t=window[e];t&&t!==this&&t instanceof o&&(t.currentResult=n,t.storeSession(n))}clearAllRelatedSessions(){if("undefined"!=typeof window){try{for(const e of Object.keys(window)){const n=window[e];n&&n!==this&&n instanceof o&&(n.currentResult=null,n.removeStoredSession())}}catch{}try{const e=[];for(let n=0;n<localStorage.length;n++){const t=localStorage.key(n);t&&t.startsWith(i)&&e.push(t)}e.forEach(e=>localStorage.removeItem(e))}catch{}}}}exports.BaseWalletConnect=o,exports.OneDollarConnect=class extends o{constructor(e){super({...e,walletId:"onedollar"})}},exports.TonPassConnect=class extends o{constructor(e){super({...e,walletId:"tonpass"})}};
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../../transport.ts","../../modal.ts","../../client.ts","../../onedollar.ts","../../tonpass.ts"],"sourcesContent":["import type { ConnectResult } from './types';\n\ntype TransportCallback = (data: ConnectResult) => void;\ntype TransportErrorCallback = (error: string) => void;\n\n// ── WebSocket Transport ──\n\nexport class WsTransport {\n private ws: WebSocket | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(\n private apiUrl: string,\n private sessionToken: string,\n private onConfirmed: TransportCallback,\n private onRejected: () => void,\n private onExpired: () => void,\n private onError: TransportErrorCallback,\n ) {}\n\n connect(): void {\n this.closed = false;\n const wsUrl = this.apiUrl\n .replace(/^http/, 'ws')\n .replace(/\\/$/, '');\n const url = `${wsUrl}/walletconnect?sessionToken=${this.sessionToken}`;\n\n try {\n // Socket.IO uses polling upgrade — use native WS for simplicity in SDK\n // The BE gateway uses socket.io, so we connect via socket.io protocol\n this.connectSocketIO();\n } catch {\n this.onError('WebSocket connection failed');\n }\n }\n\n private connectSocketIO(): void {\n const baseUrl = this.apiUrl.replace(/\\/$/, '');\n\n // Socket.IO handshake: first GET for sid, then upgrade to WS\n const pollingUrl = `${baseUrl}/walletconnect/?EIO=4&transport=polling`;\n\n fetch(pollingUrl)\n .then((res) => res.text())\n .then((body) => {\n // Socket.IO response format: \"0{...json...}\"\n const jsonStr = body.replace(/^\\d+/, '');\n const handshake = JSON.parse(jsonStr);\n const sid = handshake.sid;\n\n const wsBase = baseUrl.replace(/^http/, 'ws');\n const wsUrl = `${wsBase}/walletconnect/?EIO=4&transport=websocket&sid=${sid}`;\n\n this.ws = new WebSocket(wsUrl);\n\n this.ws.onopen = () => {\n // Socket.IO upgrade probe\n this.ws!.send('2probe');\n };\n\n this.ws.onmessage = (event) => {\n const msg = event.data as string;\n\n if (msg === '3probe') {\n // Upgrade confirmed, send upgrade packet + join room\n this.ws!.send('5');\n // Emit connect event with sessionToken as query (already in URL)\n // Socket.IO auto-joins based on handshake query\n return;\n }\n\n // Handle Socket.IO event messages: 42[\"event_name\", {...data}]\n if (msg.startsWith('42')) {\n try {\n const payload = JSON.parse(msg.slice(2));\n const [eventName, data] = payload;\n this.handleEvent(eventName, data);\n } catch {\n // ignore malformed\n }\n return;\n }\n\n // Ping from server\n if (msg === '2') {\n this.ws!.send('3');\n }\n };\n\n this.ws.onclose = () => {\n if (!this.closed) {\n this.scheduleReconnect();\n }\n };\n\n this.ws.onerror = () => {\n if (!this.closed) {\n this.scheduleReconnect();\n }\n };\n })\n .catch(() => {\n if (!this.closed) {\n this.scheduleReconnect();\n }\n });\n }\n\n private handleEvent(eventName: string, data: any): void {\n switch (eventName) {\n case 'session_confirmed':\n this.onConfirmed(data);\n this.close();\n break;\n case 'session_rejected':\n this.onRejected();\n this.close();\n break;\n case 'session_expired':\n this.onExpired();\n this.close();\n break;\n }\n }\n\n private scheduleReconnect(): void {\n if (this.closed || this.reconnectTimer) return;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connectSocketIO();\n }, 3000);\n }\n\n close(): void {\n this.closed = true;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n}\n\n// ── Long-Poll Fallback ──\n\nexport class PollingTransport {\n private timer: ReturnType<typeof setInterval> | null = null;\n private stopped = false;\n\n constructor(\n private apiUrl: string,\n private sessionToken: string,\n private onConfirmed: TransportCallback,\n private onRejected: () => void,\n private onExpired: () => void,\n private onError: TransportErrorCallback,\n ) {}\n\n start(): void {\n this.stopped = false;\n this.poll();\n this.timer = setInterval(() => this.poll(), 2000);\n }\n\n private async poll(): Promise<void> {\n if (this.stopped) return;\n\n try {\n const baseUrl = this.apiUrl.replace(/\\/$/, '');\n const res = await fetch(\n `${baseUrl}/connect/status?token=${this.sessionToken}`,\n );\n if (!res.ok) return;\n\n const data = await res.json();\n\n switch (data.status) {\n case 'CONFIRMED':\n this.onConfirmed({ wallet: data.wallet, user: data.user });\n this.stop();\n break;\n case 'REJECTED':\n this.onRejected();\n this.stop();\n break;\n case 'EXPIRED':\n this.onExpired();\n this.stop();\n break;\n }\n } catch {\n // silently retry next interval\n }\n }\n\n stop(): void {\n this.stopped = true;\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n}\n","import type { WalletMeta } from './types';\n\nconst MODAL_ID = '__wc_modal__';\nconst MASCOT_URL = 'https://dailytrip.1dollar.day/assets/character-mascot.png';\n\nfunction getStyles(theme: 'light' | 'dark'): string {\n const isDark = theme === 'dark';\n const bg = isDark ? '#1a1f2e' : '#ffffff';\n const bgCard = isDark ? '#222834' : '#f0f7ff';\n const bgGlass = isDark\n ? 'rgba(34,40,52,0.9)'\n : 'rgba(255,255,255,0.9)';\n const text = isDark ? '#f0f4f8' : '#1a1f2e';\n const textSecondary = isDark ? '#6b7a8d' : '#6b7c8d';\n const border = isDark ? '#2d3545' : '#e0eaf4';\n const accent = '#2bea6a';\n const accentDark = '#1db954';\n const gradientBg = isDark\n ? 'linear-gradient(180deg, #1a1f2e 0%, #222834 100%)'\n : 'linear-gradient(135deg, #f0f7ff 0%, #e8f5e9 50%, #f0f7ff 100%)';\n const gradientAccent = 'linear-gradient(135deg, #1db954, #2bea6a, #4af58a)';\n const glowAccent = 'rgba(43,234,106,0.15)';\n const glowStrong = 'rgba(43,234,106,0.3)';\n\n return `\n @keyframes wc-fadeIn { from{opacity:0} to{opacity:1} }\n @keyframes wc-slideUp {\n from{opacity:0;transform:translateY(40px) scale(0.96)}\n to{opacity:1;transform:translateY(0) scale(1)}\n }\n @keyframes wc-float {\n 0%,100%{transform:translateY(0)}\n 50%{transform:translateY(-6px)}\n }\n @keyframes wc-glow {\n 0%,100%{box-shadow:0 0 20px ${glowAccent}}\n 50%{box-shadow:0 0 40px ${glowStrong}}\n }\n @keyframes wc-popIn {\n from{transform:scale(0);opacity:0}\n 50%{transform:scale(1.15)}\n to{transform:scale(1);opacity:1}\n }\n @keyframes wc-spin { to{transform:rotate(360deg)} }\n @keyframes wc-shimmer {\n 0%{background-position:-200% 0}\n 100%{background-position:200% 0}\n }\n\n .wc-overlay {\n position:fixed; inset:0; z-index:99999;\n background:rgba(0,0,0,${isDark ? 0.6 : 0.35});\n display:flex; align-items:center; justify-content:center;\n font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\n backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);\n animation:wc-fadeIn .25s ease;\n }\n .wc-modal {\n background:${gradientBg}; width:100%; max-width:400px;\n border-radius:28px; overflow:hidden; color:${text};\n border:1px solid ${border};\n box-shadow:0 24px 80px rgba(0,0,0,${isDark ? 0.5 : 0.12}),\n 0 0 0 1px ${isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.03)'};\n animation:wc-slideUp .35s cubic-bezier(.16,1,.3,1);\n max-height:90vh; overflow-y:auto; position:relative;\n }\n\n /* Decorative glow orb */\n .wc-modal::before {\n content:''; position:absolute; top:-60px; right:-60px;\n width:180px; height:180px; border-radius:50%;\n background:radial-gradient(circle, ${glowAccent} 0%, transparent 70%);\n pointer-events:none; z-index:0;\n }\n\n /* Header */\n .wc-header {\n display:flex; align-items:center; justify-content:space-between;\n padding:24px 24px 0; position:relative; z-index:1;\n }\n .wc-header-left {\n display:flex; align-items:center; gap:10px;\n }\n .wc-header-icon {\n width:36px; height:36px; border-radius:12px; object-fit:cover;\n border:2px solid ${border}; box-shadow:0 2px 8px rgba(0,0,0,0.1);\n }\n .wc-title {\n font-size:18px; font-weight:800; margin:0;\n background:${gradientAccent}; -webkit-background-clip:text;\n -webkit-text-fill-color:transparent; background-clip:text;\n }\n .wc-close {\n width:32px; height:32px; display:flex; align-items:center; justify-content:center;\n background:${bgCard}; border:1px solid ${border}; border-radius:50%;\n cursor:pointer; font-size:14px; color:${textSecondary};\n transition:all .2s;\n }\n .wc-close:hover { background:${border}; color:${text}; transform:rotate(90deg); }\n\n .wc-subtitle {\n font-size:13px; color:${textSecondary}; margin:12px 24px 0;\n line-height:1.5; position:relative; z-index:1;\n }\n\n .wc-body { padding:20px 24px 24px; position:relative; z-index:1; }\n\n /* Mascot */\n .wc-mascot {\n display:flex; justify-content:center; margin:8px 0 4px;\n }\n .wc-mascot img {\n width:100px; height:100px; object-fit:contain;\n animation:wc-float 3s ease-in-out infinite;\n filter:drop-shadow(0 8px 24px rgba(43,234,106,0.2));\n }\n\n /* Wallet list */\n .wc-wallet-list { list-style:none; margin:0; padding:0; }\n .wc-wallet-item {\n display:flex; align-items:center; gap:14px;\n padding:14px 16px; border-radius:16px; cursor:pointer;\n background:${bgGlass}; border:1.5px solid ${border};\n margin-bottom:10px; transition:all .2s; position:relative;\n overflow:hidden;\n }\n .wc-wallet-item::before {\n content:''; position:absolute; inset:0;\n background:${gradientAccent}; opacity:0;\n transition:opacity .2s; border-radius:16px;\n }\n .wc-wallet-item:hover::before { opacity:0.06; }\n .wc-wallet-item:hover {\n border-color:${accent}; transform:translateY(-2px);\n box-shadow:0 8px 24px ${glowAccent};\n }\n .wc-wallet-list-icon {\n width:44px; height:44px; border-radius:14px; object-fit:cover;\n position:relative; z-index:1;\n border:2px solid ${border}; box-shadow:0 2px 8px rgba(0,0,0,0.08);\n }\n .wc-wallet-list-icon-placeholder {\n width:44px; height:44px; border-radius:14px;\n background:${gradientAccent};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:18px;\n position:relative; z-index:1;\n }\n .wc-wallet-item-info { flex:1; position:relative; z-index:1; }\n .wc-wallet-item-name { font-size:15px; font-weight:700; }\n .wc-wallet-item-desc { font-size:12px; color:${textSecondary}; margin-top:2px; }\n .wc-wallet-item-arrow {\n color:${accent}; font-size:18px; font-weight:700;\n position:relative; z-index:1; transition:transform .2s;\n }\n .wc-wallet-item:hover .wc-wallet-item-arrow { transform:translateX(4px); }\n\n /* Grid picker */\n .wc-wallet-grid {\n display:grid; grid-template-columns:repeat(3,1fr);\n gap:12px; list-style:none; margin:0; padding:0;\n }\n .wc-wallet-grid-item {\n display:flex; flex-direction:column; align-items:center;\n gap:10px; cursor:pointer; padding:16px 8px; border-radius:20px;\n background:${bgGlass}; border:1.5px solid ${border};\n transition:all .2s;\n }\n .wc-wallet-grid-item:hover {\n border-color:${accent}; transform:translateY(-3px);\n box-shadow:0 8px 24px ${glowAccent};\n }\n .wc-wallet-grid-icon {\n width:52px; height:52px; border-radius:16px;\n object-fit:cover; border:2px solid ${border};\n box-shadow:0 4px 12px rgba(0,0,0,0.08);\n }\n .wc-wallet-grid-icon-placeholder {\n width:52px; height:52px; border-radius:16px;\n background:${gradientAccent};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:20px;\n }\n .wc-wallet-grid-name {\n font-size:12px; font-weight:600; text-align:center;\n color:${text}; line-height:1.3;\n }\n\n /* Tabs */\n .wc-tabs {\n display:flex; background:${bgCard}; border-radius:14px;\n padding:4px; margin-bottom:20px; border:1px solid ${border};\n }\n .wc-tab {\n flex:1; padding:10px; text-align:center; cursor:pointer;\n font-size:13px; font-weight:700; background:transparent;\n border:none; color:${textSecondary}; border-radius:11px;\n transition:all .25s;\n }\n .wc-tab:hover { color:${text}; }\n .wc-tab.active {\n background:${gradientAccent}; color:#fff;\n box-shadow:0 4px 16px ${glowAccent};\n }\n\n /* Deeplink */\n .wc-deeplink-btn {\n display:flex; align-items:center; justify-content:center; gap:10px;\n width:100%; padding:16px; border:none; border-radius:16px;\n background:${gradientAccent}; color:#fff;\n font-size:15px; font-weight:800; cursor:pointer;\n text-decoration:none; transition:all .25s;\n box-shadow:0 4px 24px ${glowAccent};\n box-sizing:border-box; position:relative; overflow:hidden;\n }\n .wc-deeplink-btn::after {\n content:''; position:absolute; inset:0;\n background:linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n background-size:200% 100%;\n animation:wc-shimmer 2.5s ease-in-out infinite;\n }\n .wc-deeplink-btn:hover {\n transform:translateY(-2px);\n box-shadow:0 8px 32px ${glowStrong};\n }\n .wc-deeplink-btn svg { position:relative; z-index:1; }\n .wc-deeplink-btn span { position:relative; z-index:1; }\n\n /* QR */\n .wc-qr-container {\n display:flex; flex-direction:column; align-items:center; gap:16px;\n }\n .wc-qr-wrap {\n padding:16px; background:#fff; border-radius:20px;\n box-shadow:0 4px 24px rgba(0,0,0,${isDark ? 0.3 : 0.08});\n border:2px solid ${glowAccent};\n }\n .wc-qr-wrap img { display:block; border-radius:8px; }\n .wc-qr-hint { font-size:12px; color:${textSecondary}; text-align:center; }\n\n /* Status */\n .wc-status {\n display:flex; align-items:center; justify-content:center;\n gap:10px; padding:20px 0 4px; font-size:13px; color:${textSecondary};\n }\n .wc-spinner {\n width:18px; height:18px; border:2.5px solid ${border};\n border-top-color:${accent}; border-radius:50%;\n animation:wc-spin .7s linear infinite;\n }\n\n /* Back */\n .wc-back {\n background:none; border:none; cursor:pointer; padding:0;\n color:${textSecondary}; display:flex; align-items:center;\n transition:all .2s; font-size:18px;\n }\n .wc-back:hover { color:${accent}; transform:translateX(-3px); }\n\n /* Footer */\n .wc-footer {\n text-align:center; padding:4px 0 20px;\n font-size:11px; color:${isDark ? '#3d5468' : '#b0bec5'};\n position:relative; z-index:1;\n }\n\n /* ── Success screen ── */\n .wc-success {\n display:flex; flex-direction:column; align-items:center;\n padding:8px 0 4px; gap:20px;\n }\n .wc-success-check {\n width:72px; height:72px; border-radius:50%;\n background:${gradientAccent};\n display:flex; align-items:center; justify-content:center;\n box-shadow:0 8px 32px ${glowStrong};\n animation:wc-popIn .4s cubic-bezier(.16,1,.3,1), wc-glow 2s ease-in-out infinite;\n }\n .wc-success-check svg { width:36px; height:36px; color:#fff; }\n .wc-success-title {\n font-size:20px; font-weight:800;\n background:${gradientAccent}; -webkit-background-clip:text;\n -webkit-text-fill-color:transparent; background-clip:text;\n }\n .wc-success-user {\n display:flex; align-items:center; gap:14px;\n background:${bgGlass}; border:1.5px solid ${border};\n border-radius:18px; padding:16px 18px; width:100%;\n box-sizing:border-box; backdrop-filter:blur(8px);\n }\n .wc-success-avatar {\n width:50px; height:50px; border-radius:50%; object-fit:cover;\n flex-shrink:0; border:2.5px solid ${accent};\n box-shadow:0 4px 16px ${glowAccent};\n }\n .wc-success-avatar-placeholder {\n width:50px; height:50px; border-radius:50%;\n background:${gradientAccent};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:18px; flex-shrink:0;\n box-shadow:0 4px 16px ${glowAccent};\n }\n .wc-success-user-info { flex:1; min-width:0; }\n .wc-success-user-name { font-size:16px; font-weight:700; color:${text}; }\n .wc-success-user-addr {\n font-size:11px; color:${textSecondary}; margin-top:4px;\n font-family:'SF Mono',Monaco,Consolas,monospace;\n word-break:break-all; line-height:1.4;\n }\n .wc-success-done {\n width:100%; padding:16px; border:none; border-radius:16px;\n background:${gradientAccent}; color:#fff;\n font-size:15px; font-weight:800; cursor:pointer;\n transition:all .25s; position:relative; overflow:hidden;\n box-shadow:0 4px 24px ${glowAccent};\n }\n .wc-success-done::after {\n content:''; position:absolute; inset:0;\n background:linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n background-size:200% 100%;\n animation:wc-shimmer 2.5s ease-in-out infinite;\n }\n .wc-success-done:hover {\n transform:translateY(-2px);\n box-shadow:0 8px 32px ${glowStrong};\n }\n `;\n}\n\nconst TELEGRAM_SVG = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M11.944 0A12 12 0 1 0 24 12.056A12.01 12.01 0 0 0 11.944 0Zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472c-.18 1.898-.962 6.502-1.36 8.627c-.168.9-.499 1.201-.82 1.23c-.696.065-1.225-.46-1.9-.902c-1.056-.693-1.653-1.124-2.678-1.8c-1.185-.78-.417-1.21.258-1.91c.177-.184 3.247-2.977 3.307-3.23c.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345c-.48.33-.913.49-1.302.48c-.428-.008-1.252-.241-1.865-.44c-.752-.245-1.349-.374-1.297-.789c.027-.216.325-.437.893-.663c3.498-1.524 5.83-2.529 6.998-3.014c3.332-1.386 4.025-1.627 4.476-1.635Z\"/></svg>`;\n\nfunction generateQRDataUrl(text: string, size = 220): string {\n return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(text)}&margin=8`;\n}\n\nexport class Modal {\n private overlay: HTMLDivElement | null = null;\n private theme: 'light' | 'dark';\n private onClose: () => void;\n\n constructor(theme: 'light' | 'dark', onClose: () => void) {\n this.theme = theme;\n this.onClose = onClose;\n }\n\n showWalletPicker(\n wallets: WalletMeta[],\n onSelect: (wallet: WalletMeta) => void,\n ): void {\n const useGrid = wallets.length >= 3;\n\n const listHtml = useGrid\n ? `<ul class=\"wc-wallet-grid\">${wallets\n .map(\n (w) => `\n <li class=\"wc-wallet-grid-item\" data-wallet-id=\"${w.walletId}\">\n ${\n w.icon\n ? `<img class=\"wc-wallet-grid-icon\" src=\"${w.icon}\" alt=\"${w.name}\" />`\n : `<div class=\"wc-wallet-grid-icon-placeholder\">${w.name.charAt(0)}</div>`\n }\n <span class=\"wc-wallet-grid-name\">${w.name}</span>\n </li>`,\n )\n .join('')}</ul>`\n : `<ul class=\"wc-wallet-list\">${wallets\n .map(\n (w) => `\n <li class=\"wc-wallet-item\" data-wallet-id=\"${w.walletId}\">\n ${\n w.icon\n ? `<img class=\"wc-wallet-list-icon\" src=\"${w.icon}\" alt=\"${w.name}\" />`\n : `<div class=\"wc-wallet-list-icon-placeholder\">${w.name.charAt(0)}</div>`\n }\n <div class=\"wc-wallet-item-info\">\n <div class=\"wc-wallet-item-name\">${w.name}</div>\n ${w.description ? `<div class=\"wc-wallet-item-desc\">${w.description}</div>` : ''}\n </div>\n <span class=\"wc-wallet-item-arrow\">›</span>\n </li>`,\n )\n .join('')}</ul>`;\n\n const html = `\n <div class=\"wc-header\">\n <h3 class=\"wc-title\">Connect Wallet</h3>\n <button class=\"wc-close\" data-action=\"close\">✕</button>\n </div>\n <div class=\"wc-mascot\">\n <img src=\"${MASCOT_URL}\" alt=\"mascot\" />\n </div>\n <p class=\"wc-subtitle\">Choose a wallet to get started</p>\n <div class=\"wc-body\">${listHtml}</div>\n <div class=\"wc-footer\">Powered by TonCorp</div>\n `;\n\n this.render(html);\n\n wallets.forEach((w) => {\n this.overlay\n ?.querySelector(`[data-wallet-id=\"${w.walletId}\"]`)\n ?.addEventListener('click', () => onSelect(w));\n });\n }\n\n showConnect(\n wallet: WalletMeta,\n deeplink: string,\n showBack: boolean,\n onBack?: () => void,\n ): void {\n const qrUrl = generateQRDataUrl(deeplink);\n\n const html = `\n <div class=\"wc-header\">\n <div class=\"wc-header-left\">\n ${showBack ? '<button class=\"wc-back\" data-action=\"back\">←</button>' : ''}\n ${wallet.icon ? `<img class=\"wc-header-icon\" src=\"${wallet.icon}\" />` : ''}\n <h3 class=\"wc-title\">${wallet.name}</h3>\n </div>\n <button class=\"wc-close\" data-action=\"close\">✕</button>\n </div>\n <p class=\"wc-subtitle\">Approve the connection in your wallet app</p>\n <div class=\"wc-body\">\n <div class=\"wc-tabs\">\n <button class=\"wc-tab active\" data-tab=\"mobile\"><span>Mobile</span></button>\n <button class=\"wc-tab\" data-tab=\"qr\"><span>QR Code</span></button>\n </div>\n <div data-panel=\"mobile\">\n <a class=\"wc-deeplink-btn\" href=\"${deeplink}\" target=\"_blank\">\n ${TELEGRAM_SVG}\n <span>Open in Telegram</span>\n </a>\n </div>\n <div data-panel=\"qr\" style=\"display:none\">\n <div class=\"wc-qr-container\">\n <div class=\"wc-qr-wrap\">\n <img src=\"${qrUrl}\" width=\"200\" height=\"200\" alt=\"QR\" />\n </div>\n <div class=\"wc-qr-hint\">Scan with your phone camera or wallet app</div>\n </div>\n </div>\n <div class=\"wc-status\">\n <div class=\"wc-spinner\"></div>\n <span>Waiting for confirmation...</span>\n </div>\n </div>\n <div class=\"wc-footer\">Powered by ${wallet.name}</div>\n `;\n\n this.render(html);\n\n const tabs = this.overlay?.querySelectorAll('.wc-tab');\n const panels = this.overlay?.querySelectorAll('[data-panel]');\n tabs?.forEach((tab) => {\n tab.addEventListener('click', () => {\n const target = (tab as HTMLElement).dataset.tab;\n tabs.forEach((t) => t.classList.remove('active'));\n tab.classList.add('active');\n panels?.forEach((p) => {\n (p as HTMLElement).style.display =\n (p as HTMLElement).dataset.panel === target ? 'block' : 'none';\n });\n });\n });\n\n if (showBack && onBack) {\n this.overlay\n ?.querySelector('[data-action=\"back\"]')\n ?.addEventListener('click', onBack);\n }\n }\n\n showSuccess(\n wallet: WalletMeta,\n address: string | null,\n user: { username?: string; avatar?: string } | null,\n onDone: () => void,\n ): void {\n const checkSvg = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\n\n const displayName = user?.username || 'User';\n const initials = displayName.slice(0, 2).toUpperCase();\n\n const avatarHtml = user?.avatar\n ? `<img class=\"wc-success-avatar\" src=\"${user.avatar}\" alt=\"${displayName}\" />`\n : `<div class=\"wc-success-avatar-placeholder\">${initials}</div>`;\n\n const html = `\n <div class=\"wc-header\">\n <h3 class=\"wc-title\">Connected</h3>\n <button class=\"wc-close\" data-action=\"close\">✕</button>\n </div>\n <div class=\"wc-body\">\n <div class=\"wc-success\">\n <div class=\"wc-success-check\">${checkSvg}</div>\n <div class=\"wc-success-title\">Connection Successful</div>\n <div class=\"wc-success-user\">\n ${avatarHtml}\n <div class=\"wc-success-user-info\">\n <div class=\"wc-success-user-name\">${displayName}</div>\n ${address ? `<div class=\"wc-success-user-addr\" title=\"${address}\">${address}</div>` : ''}\n </div>\n </div>\n <button class=\"wc-success-done\" data-action=\"done\"><span>Done</span></button>\n </div>\n </div>\n <div class=\"wc-footer\">Powered by ${wallet.name}</div>\n `;\n\n this.render(html);\n\n this.overlay\n ?.querySelector('[data-action=\"done\"]')\n ?.addEventListener('click', onDone);\n }\n\n updateStatus(text: string, showSpinner = true): void {\n const el = this.overlay?.querySelector('.wc-status');\n if (el) {\n el.innerHTML = `\n ${showSpinner ? '<div class=\"wc-spinner\"></div>' : ''}\n <span>${text}</span>\n `;\n }\n }\n\n private render(innerHtml: string): void {\n this.destroy();\n\n const el = document.createElement('div');\n el.id = MODAL_ID;\n el.innerHTML = `\n <style>${getStyles(this.theme)}</style>\n <div class=\"wc-overlay\">\n <div class=\"wc-modal\">${innerHtml}</div>\n </div>\n `;\n\n document.body.appendChild(el);\n this.overlay = el;\n\n this.overlay\n .querySelector('[data-action=\"close\"]')\n ?.addEventListener('click', () => { this.onClose(); this.destroy(); });\n\n this.overlay\n .querySelector('.wc-overlay')\n ?.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).classList.contains('wc-overlay')) {\n this.onClose();\n this.destroy();\n }\n });\n }\n\n destroy(): void {\n document.getElementById(MODAL_ID)?.remove();\n this.overlay = null;\n }\n}\n","import type {\n WalletConnectConfig,\n WalletMeta,\n ConnectResult,\n ConnectSession,\n InitSessionResponse,\n SendTransactionParams,\n SendTransactionResult,\n WaitForResultOptions,\n TxStatusResult,\n WalletConnectEventType,\n WalletConnectEventCallback,\n} from './types';\nimport { WsTransport, PollingTransport } from './transport';\nimport { Modal } from './modal';\n\nconst STORAGE_KEY_PREFIX = '__wc_session_';\nconst DEFAULT_REGISTRY_URL = 'https://cdn.jsdelivr.net/npm/toncorp-wallet-connect-sdk@latest/registry.json';\n\nexport class BaseWalletConnect {\n protected config: Required<\n Pick<WalletConnectConfig, 'appId' | 'registryUrl' | 'theme'>\n > &\n Pick<WalletConnectConfig, 'walletId' | 'siteTmaUrl'>;\n\n private listeners: Map<WalletConnectEventType, Set<WalletConnectEventCallback>> = new Map();\n private modal: Modal | null = null;\n private wsTransport: WsTransport | null = null;\n private pollingTransport: PollingTransport | null = null;\n private currentResult: ConnectResult | null = null;\n\n constructor(config: WalletConnectConfig) {\n this.config = {\n appId: config.appId,\n registryUrl: config.registryUrl || DEFAULT_REGISTRY_URL,\n walletId: config.walletId,\n theme: config.theme || 'dark',\n siteTmaUrl: config.siteTmaUrl,\n };\n\n this.restoreSession();\n this.registerGlobal();\n }\n\n // ── Public API ──\n\n async connect(): Promise<ConnectResult> {\n if (this.currentResult) {\n return this.currentResult;\n }\n\n const wallets = await this.fetchWallets();\n const filtered = this.filterWallets(wallets);\n\n if (filtered.length === 0) {\n throw new Error('No supported wallets available');\n }\n\n return new Promise<ConnectResult>((resolve, reject) => {\n const onClose = () => {\n this.cleanup();\n reject(new Error('User closed modal'));\n };\n\n this.modal = new Modal(this.config.theme, onClose);\n\n if (filtered.length === 1) {\n this.startConnect(filtered[0], false, resolve, reject);\n } else {\n this.modal.showWalletPicker(filtered, (wallet) => {\n this.startConnect(wallet, true, resolve, reject);\n });\n }\n });\n }\n\n disconnect(): void {\n // Find session from this instance or any other connected instance\n let session = this.currentResult?.session;\n if (!session && typeof window !== 'undefined') {\n for (const key of Object.keys(window)) {\n const inst = (window as any)[key];\n if (inst instanceof BaseWalletConnect && inst.currentResult?.session) {\n session = inst.currentResult.session;\n break;\n }\n }\n }\n\n // Call disconnect API (fire-and-forget)\n if (session) {\n const baseUrl = session.apiUrl.replace(/\\/$/, '');\n fetch(`${baseUrl}/connect/disconnect-by-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionToken: session.sessionToken }),\n }).catch(() => {});\n }\n\n // Clear all localStorage keys and window instances\n this.currentResult = null;\n this.removeStoredSession();\n this.clearAllRelatedSessions();\n this.cleanup();\n this.emit('disconnect');\n }\n\n async sendTransaction(\n params: SendTransactionParams,\n ): Promise<SendTransactionResult> {\n const session = this.currentResult?.session;\n if (!session?.apiUrl) {\n throw new Error('Wallet not connected');\n }\n\n const baseUrl = session.apiUrl.replace(/\\/$/, '');\n\n let res: Response;\n try {\n res = await fetch(`${baseUrl}/dex/sendTx`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...params,\n appId: this.config.appId,\n source: 'widget',\n }),\n });\n } catch {\n throw new Error('Connection failed');\n }\n\n if (res.status === 401) {\n throw new Error('Wallet not connected');\n }\n if (res.status === 429) {\n throw new Error('Too many pending transactions');\n }\n if (!res.ok) {\n throw new Error('Failed to send transaction');\n }\n\n const data = await res.json();\n return { txId: data.txId };\n }\n\n async waitForResult(\n txId: string,\n options?: WaitForResultOptions,\n ): Promise<TxStatusResult> {\n const session = this.currentResult?.session;\n if (!session?.apiUrl) {\n throw new Error('Wallet not connected');\n }\n\n const interval = options?.interval ?? 2000;\n const timeout = options?.timeout ?? 120000;\n const baseUrl = session.apiUrl.replace(/\\/$/, '');\n const start = Date.now();\n\n while (true) {\n let res: Response;\n try {\n res = await fetch(`${baseUrl}/dex/status/${txId}`);\n } catch {\n throw new Error('Connection failed');\n }\n\n if (!res.ok) {\n throw new Error('Failed to fetch transaction status');\n }\n\n const data = await res.json();\n const result: TxStatusResult = {\n status: data.status,\n msgHash: data.msgHash,\n action: data.action,\n };\n\n if (data.status !== 'PENDING') {\n this.emit('resolved', result);\n return result;\n }\n\n this.emit('pending', result);\n\n if (Date.now() - start >= timeout) {\n throw new Error('Timeout');\n }\n\n await this.sleep(interval);\n }\n }\n\n isConnected(): boolean {\n return this.currentResult !== null;\n }\n\n getWallet(): ConnectResult | null {\n return this.currentResult;\n }\n\n on(event: WalletConnectEventType, cb: WalletConnectEventCallback): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(cb);\n }\n\n off(event: WalletConnectEventType, cb: WalletConnectEventCallback): void {\n this.listeners.get(event)?.delete(cb);\n }\n\n // ── Internal ──\n\n private get storageKey(): string {\n const walletId = this.config.walletId;\n const suffix = Array.isArray(walletId)\n ? walletId.sort().join('_')\n : walletId || 'all';\n return `${STORAGE_KEY_PREFIX}${suffix}__`;\n }\n\n private async fetchWallets(): Promise<WalletMeta[]> {\n const url = this.config.registryUrl.replace(/\\/$/, '');\n const res = await fetch(url);\n if (!res.ok) throw new Error('Failed to fetch wallets');\n return res.json();\n }\n\n private filterWallets(wallets: WalletMeta[]): WalletMeta[] {\n if (!this.config.walletId) return wallets;\n\n const ids = Array.isArray(this.config.walletId)\n ? this.config.walletId\n : [this.config.walletId];\n\n return wallets.filter((w) => ids.includes(w.walletId));\n }\n\n private async startConnect(\n wallet: WalletMeta,\n showBack: boolean,\n resolve: (result: ConnectResult) => void,\n reject: (error: Error) => void,\n ): Promise<void> {\n try {\n const session = await this.initSession(wallet);\n\n this.modal?.showConnect(wallet, session.deeplink, showBack, () => {\n this.cleanup();\n this.connect().then(resolve).catch(reject);\n });\n\n this.listenForConfirmation(\n wallet.apiUrl,\n session.sessionToken,\n (result) => {\n // Attach session info for disconnect\n result.session = {\n sessionToken: session.sessionToken,\n walletId: wallet.walletId,\n apiUrl: wallet.apiUrl,\n };\n this.currentResult = result;\n this.storeSession(result);\n this.syncToWalletInstance(wallet.walletId, result);\n this.emit('connect', result);\n\n // Show success screen\n this.modal?.showSuccess(\n wallet,\n result.wallet?.address || null,\n result.user || null,\n () => {\n this.modal?.destroy();\n this.modal = null;\n resolve(result);\n },\n );\n },\n () => {\n this.modal?.updateStatus('Connection rejected', false);\n setTimeout(() => {\n this.modal?.destroy();\n this.modal = null;\n reject(new Error('Connection rejected by user'));\n }, 1500);\n },\n () => {\n this.modal?.updateStatus('Session expired', false);\n this.emit('session_expired');\n setTimeout(() => {\n this.modal?.destroy();\n this.modal = null;\n reject(new Error('Session expired'));\n }, 1500);\n },\n );\n } catch (err) {\n this.modal?.destroy();\n this.modal = null;\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n private async initSession(wallet: WalletMeta): Promise<InitSessionResponse> {\n const baseUrl = wallet.apiUrl.replace(/\\/$/, '');\n const res = await fetch(`${baseUrl}/connect/init`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n appId: this.config.appId,\n walletId: wallet.walletId,\n origin: typeof window !== 'undefined' ? window.location.origin : '',\n siteTmaUrl: this.config.siteTmaUrl,\n }),\n });\n if (!res.ok) throw new Error('Failed to init session');\n return res.json();\n }\n\n private listenForConfirmation(\n apiUrl: string,\n sessionToken: string,\n onConfirmed: (result: ConnectResult) => void,\n onRejected: () => void,\n onExpired: () => void,\n ): void {\n let resolved = false;\n\n const wrap = <T extends (...args: any[]) => void>(fn: T): T =>\n ((...args: any[]) => {\n if (resolved) return;\n resolved = true;\n this.wsTransport?.close();\n this.pollingTransport?.stop();\n fn(...args);\n }) as unknown as T;\n\n const wrappedConfirm = wrap(onConfirmed);\n const wrappedReject = wrap(onRejected);\n const wrappedExpire = wrap(onExpired);\n\n // Start polling immediately as primary transport\n this.pollingTransport = new PollingTransport(\n apiUrl,\n sessionToken,\n wrappedConfirm,\n wrappedReject,\n wrappedExpire,\n (msg) => this.emit('error', msg),\n );\n this.pollingTransport.start();\n\n // Also try WS for faster response\n this.wsTransport = new WsTransport(\n apiUrl,\n sessionToken,\n wrappedConfirm,\n wrappedReject,\n wrappedExpire,\n () => {}, // WS fail is fine, polling is running\n );\n this.wsTransport.connect();\n }\n\n private cleanup(): void {\n this.wsTransport?.close();\n this.wsTransport = null;\n this.pollingTransport?.stop();\n this.pollingTransport = null;\n this.modal?.destroy();\n this.modal = null;\n }\n\n private emit(event: WalletConnectEventType, data?: any): void {\n this.listeners.get(event)?.forEach((cb) => cb(data));\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // ── Persistence ──\n\n private storeSession(result: ConnectResult): void {\n try {\n localStorage.setItem(this.storageKey, JSON.stringify(result));\n } catch {\n // localStorage unavailable\n }\n }\n\n private restoreSession(): void {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored) {\n this.currentResult = JSON.parse(stored);\n }\n } catch {\n // ignore\n }\n }\n\n private removeStoredSession(): void {\n try {\n localStorage.removeItem(this.storageKey);\n } catch {\n // ignore\n }\n }\n\n // ── Global window registration ──\n\n private registerGlobal(): void {\n if (typeof window === 'undefined') return;\n\n const walletId = this.config.walletId;\n const key = Array.isArray(walletId)\n ? walletId[0]\n : walletId || 'walletconnect';\n\n (window as any)[key] = this;\n }\n\n /** Sync connect result to the specific wallet instance on window (e.g. window.onedollar) */\n private syncToWalletInstance(walletId: string, result: ConnectResult): void {\n if (typeof window === 'undefined') return;\n\n const target = (window as any)[walletId];\n if (target && target !== this && target instanceof BaseWalletConnect) {\n (target as any).currentResult = result;\n (target as any).storeSession(result);\n }\n }\n\n /** Clear all related sessions from all window instances and localStorage */\n private clearAllRelatedSessions(): void {\n if (typeof window === 'undefined') return;\n\n // Find all BaseWalletConnect instances on window and clear them\n try {\n for (const key of Object.keys(window)) {\n const inst = (window as any)[key];\n if (inst && inst !== this && inst instanceof BaseWalletConnect) {\n (inst as any).currentResult = null;\n (inst as any).removeStoredSession();\n }\n }\n } catch {\n // ignore\n }\n\n // Clear all wc localStorage keys\n try {\n const toRemove: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i);\n if (k && k.startsWith(STORAGE_KEY_PREFIX)) {\n toRemove.push(k);\n }\n }\n toRemove.forEach((k) => localStorage.removeItem(k));\n } catch {\n // ignore\n }\n }\n}\n","import { BaseWalletConnect } from './client';\nimport type { WalletConnectConfig } from './types';\n\nexport class OneDollarConnect extends BaseWalletConnect {\n constructor(config: Omit<WalletConnectConfig, 'walletId'>) {\n super({ ...config, walletId: 'onedollar' });\n }\n}\n","import { BaseWalletConnect } from './client';\nimport type { WalletConnectConfig } from './types';\n\nexport class TonPassConnect extends BaseWalletConnect {\n constructor(config: Omit<WalletConnectConfig, 'walletId'>) {\n super({ ...config, walletId: 'tonpass' });\n }\n}\n"],"names":["WsTransport","constructor","apiUrl","sessionToken","onConfirmed","onRejected","onExpired","onError","this","ws","reconnectTimer","closed","connect","replace","connectSocketIO","baseUrl","fetch","then","res","text","body","jsonStr","sid","JSON","parse","wsUrl","WebSocket","onopen","send","onmessage","event","msg","data","startsWith","payload","slice","eventName","handleEvent","onclose","scheduleReconnect","onerror","catch","close","setTimeout","clearTimeout","PollingTransport","timer","stopped","start","poll","setInterval","ok","json","status","wallet","user","stop","clearInterval","MODAL_ID","Modal","theme","onClose","overlay","showWalletPicker","wallets","onSelect","html","length","map","w","walletId","icon","name","charAt","join","description","render","forEach","querySelector","addEventListener","showConnect","deeplink","showBack","onBack","qrUrl","size","encodeURIComponent","generateQRDataUrl","tabs","querySelectorAll","panels","tab","target","dataset","t","classList","remove","add","p","style","display","panel","showSuccess","address","onDone","displayName","username","initials","toUpperCase","avatar","updateStatus","showSpinner","el","innerHTML","innerHtml","destroy","document","createElement","id","isDark","bgCard","bgGlass","textSecondary","border","accent","gradientAccent","glowAccent","glowStrong","getStyles","appendChild","e","contains","getElementById","STORAGE_KEY_PREFIX","BaseWalletConnect","config","listeners","Map","modal","wsTransport","pollingTransport","currentResult","appId","registryUrl","siteTmaUrl","restoreSession","registerGlobal","fetchWallets","filtered","filterWallets","Error","Promise","resolve","reject","cleanup","startConnect","disconnect","session","window","key","Object","keys","inst","method","headers","stringify","removeStoredSession","clearAllRelatedSessions","emit","sendTransaction","params","source","txId","waitForResult","options","interval","timeout","Date","now","result","msgHash","action","sleep","isConnected","getWallet","on","cb","has","set","Set","get","off","delete","storageKey","suffix","Array","isArray","sort","url","ids","filter","includes","initSession","listenForConfirmation","storeSession","syncToWalletInstance","err","String","origin","location","resolved","wrap","fn","args","wrappedConfirm","wrappedReject","wrappedExpire","ms","localStorage","setItem","stored","getItem","removeItem","toRemove","i","k","push","super"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../../transport.ts","../../modal.ts","../../client.ts","../../onedollar.ts","../../tonpass.ts"],"sourcesContent":["import type { ConnectResult } from './types';\n\ntype TransportCallback = (data: ConnectResult) => void;\ntype TransportErrorCallback = (error: string) => void;\n\n// ── WebSocket Transport ──\n\nexport class WsTransport {\n private ws: WebSocket | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private closed = false;\n\n constructor(\n private apiUrl: string,\n private sessionToken: string,\n private onConfirmed: TransportCallback,\n private onRejected: () => void,\n private onExpired: () => void,\n private onError: TransportErrorCallback,\n ) {}\n\n connect(): void {\n this.closed = false;\n const wsUrl = this.apiUrl\n .replace(/^http/, 'ws')\n .replace(/\\/$/, '');\n const url = `${wsUrl}/walletconnect?sessionToken=${this.sessionToken}`;\n\n try {\n // Socket.IO uses polling upgrade — use native WS for simplicity in SDK\n // The BE gateway uses socket.io, so we connect via socket.io protocol\n this.connectSocketIO();\n } catch {\n this.onError('WebSocket connection failed');\n }\n }\n\n private connectSocketIO(): void {\n const baseUrl = this.apiUrl.replace(/\\/$/, '');\n\n // Socket.IO handshake: first GET for sid, then upgrade to WS\n const pollingUrl = `${baseUrl}/walletconnect/?EIO=4&transport=polling`;\n\n fetch(pollingUrl)\n .then((res) => res.text())\n .then((body) => {\n // Socket.IO response format: \"0{...json...}\"\n const jsonStr = body.replace(/^\\d+/, '');\n const handshake = JSON.parse(jsonStr);\n const sid = handshake.sid;\n\n const wsBase = baseUrl.replace(/^http/, 'ws');\n const wsUrl = `${wsBase}/walletconnect/?EIO=4&transport=websocket&sid=${sid}`;\n\n this.ws = new WebSocket(wsUrl);\n\n this.ws.onopen = () => {\n // Socket.IO upgrade probe\n this.ws!.send('2probe');\n };\n\n this.ws.onmessage = (event) => {\n const msg = event.data as string;\n\n if (msg === '3probe') {\n // Upgrade confirmed, send upgrade packet + join room\n this.ws!.send('5');\n // Emit connect event with sessionToken as query (already in URL)\n // Socket.IO auto-joins based on handshake query\n return;\n }\n\n // Handle Socket.IO event messages: 42[\"event_name\", {...data}]\n if (msg.startsWith('42')) {\n try {\n const payload = JSON.parse(msg.slice(2));\n const [eventName, data] = payload;\n this.handleEvent(eventName, data);\n } catch {\n // ignore malformed\n }\n return;\n }\n\n // Ping from server\n if (msg === '2') {\n this.ws!.send('3');\n }\n };\n\n this.ws.onclose = () => {\n if (!this.closed) {\n this.scheduleReconnect();\n }\n };\n\n this.ws.onerror = () => {\n if (!this.closed) {\n this.scheduleReconnect();\n }\n };\n })\n .catch(() => {\n if (!this.closed) {\n this.scheduleReconnect();\n }\n });\n }\n\n private handleEvent(eventName: string, data: any): void {\n switch (eventName) {\n case 'session_confirmed':\n this.onConfirmed(data);\n this.close();\n break;\n case 'session_rejected':\n this.onRejected();\n this.close();\n break;\n case 'session_expired':\n this.onExpired();\n this.close();\n break;\n }\n }\n\n private scheduleReconnect(): void {\n if (this.closed || this.reconnectTimer) return;\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.connectSocketIO();\n }, 3000);\n }\n\n close(): void {\n this.closed = true;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n}\n\n// ── Long-Poll Fallback ──\n\nexport class PollingTransport {\n private timer: ReturnType<typeof setInterval> | null = null;\n private stopped = false;\n\n constructor(\n private apiUrl: string,\n private sessionToken: string,\n private onConfirmed: TransportCallback,\n private onRejected: () => void,\n private onExpired: () => void,\n private onError: TransportErrorCallback,\n ) {}\n\n start(): void {\n this.stopped = false;\n this.poll();\n this.timer = setInterval(() => this.poll(), 2000);\n }\n\n private async poll(): Promise<void> {\n if (this.stopped) return;\n\n try {\n const baseUrl = this.apiUrl.replace(/\\/$/, '');\n const res = await fetch(\n `${baseUrl}/connect/status?token=${this.sessionToken}`,\n );\n if (!res.ok) return;\n\n const data = await res.json();\n\n switch (data.status) {\n case 'CONFIRMED':\n this.onConfirmed({ wallet: data.wallet, user: data.user });\n this.stop();\n break;\n case 'REJECTED':\n this.onRejected();\n this.stop();\n break;\n case 'EXPIRED':\n this.onExpired();\n this.stop();\n break;\n }\n } catch {\n // silently retry next interval\n }\n }\n\n stop(): void {\n this.stopped = true;\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n}\n","import type { WalletMeta } from './types';\n\nconst MODAL_ID = '__wc_modal__';\nconst MASCOT_URL = 'https://dailytrip.1dollar.day/assets/character-mascot.png';\n\nfunction getStyles(theme: 'light' | 'dark'): string {\n const isDark = theme === 'dark';\n const bg = isDark ? '#1a1f2e' : '#ffffff';\n const bgCard = isDark ? '#222834' : '#f0f7ff';\n const bgGlass = isDark\n ? 'rgba(34,40,52,0.9)'\n : 'rgba(255,255,255,0.9)';\n const text = isDark ? '#f0f4f8' : '#1a1f2e';\n const textSecondary = isDark ? '#6b7a8d' : '#6b7c8d';\n const border = isDark ? '#2d3545' : '#e0eaf4';\n const accent = '#2bea6a';\n const accentDark = '#1db954';\n const gradientBg = isDark\n ? 'linear-gradient(180deg, #1a1f2e 0%, #222834 100%)'\n : 'linear-gradient(135deg, #f0f7ff 0%, #e8f5e9 50%, #f0f7ff 100%)';\n const gradientAccent = 'linear-gradient(135deg, #1db954, #2bea6a, #4af58a)';\n const glowAccent = 'rgba(43,234,106,0.15)';\n const glowStrong = 'rgba(43,234,106,0.3)';\n\n return `\n @keyframes wc-fadeIn { from{opacity:0} to{opacity:1} }\n @keyframes wc-slideUp {\n from{opacity:0;transform:translateY(40px) scale(0.96)}\n to{opacity:1;transform:translateY(0) scale(1)}\n }\n @keyframes wc-float {\n 0%,100%{transform:translateY(0)}\n 50%{transform:translateY(-6px)}\n }\n @keyframes wc-glow {\n 0%,100%{box-shadow:0 0 20px ${glowAccent}}\n 50%{box-shadow:0 0 40px ${glowStrong}}\n }\n @keyframes wc-popIn {\n from{transform:scale(0);opacity:0}\n 50%{transform:scale(1.15)}\n to{transform:scale(1);opacity:1}\n }\n @keyframes wc-spin { to{transform:rotate(360deg)} }\n @keyframes wc-shimmer {\n 0%{background-position:-200% 0}\n 100%{background-position:200% 0}\n }\n\n .wc-overlay {\n position:fixed; inset:0; z-index:99999;\n background:rgba(0,0,0,${isDark ? 0.6 : 0.35});\n display:flex; align-items:center; justify-content:center;\n font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\n backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px);\n animation:wc-fadeIn .25s ease;\n }\n .wc-modal {\n background:${gradientBg}; width:100%; max-width:400px;\n border-radius:28px; overflow:hidden; color:${text};\n border:1px solid ${border};\n box-shadow:0 24px 80px rgba(0,0,0,${isDark ? 0.5 : 0.12}),\n 0 0 0 1px ${isDark ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.03)'};\n animation:wc-slideUp .35s cubic-bezier(.16,1,.3,1);\n max-height:90vh; overflow-y:auto; position:relative;\n }\n\n /* Decorative glow orb */\n .wc-modal::before {\n content:''; position:absolute; top:-60px; right:-60px;\n width:180px; height:180px; border-radius:50%;\n background:radial-gradient(circle, ${glowAccent} 0%, transparent 70%);\n pointer-events:none; z-index:0;\n }\n\n /* Header */\n .wc-header {\n display:flex; align-items:center; justify-content:space-between;\n padding:24px 24px 0; position:relative; z-index:1;\n }\n .wc-header-left {\n display:flex; align-items:center; gap:10px;\n }\n .wc-header-icon {\n width:36px; height:36px; border-radius:12px; object-fit:cover;\n border:2px solid ${border}; box-shadow:0 2px 8px rgba(0,0,0,0.1);\n }\n .wc-title {\n font-size:18px; font-weight:800; margin:0;\n background:${gradientAccent}; -webkit-background-clip:text;\n -webkit-text-fill-color:transparent; background-clip:text;\n }\n .wc-close {\n width:32px; height:32px; display:flex; align-items:center; justify-content:center;\n background:${bgCard}; border:1px solid ${border}; border-radius:50%;\n cursor:pointer; font-size:14px; color:${textSecondary};\n transition:all .2s;\n }\n .wc-close:hover { background:${border}; color:${text}; transform:rotate(90deg); }\n\n .wc-subtitle {\n font-size:13px; color:${textSecondary}; margin:12px 24px 0;\n line-height:1.5; position:relative; z-index:1;\n }\n\n .wc-body { padding:20px 24px 24px; position:relative; z-index:1; }\n\n /* Mascot */\n .wc-mascot {\n display:flex; justify-content:center; margin:8px 0 4px;\n }\n .wc-mascot img {\n width:100px; height:100px; object-fit:contain;\n animation:wc-float 3s ease-in-out infinite;\n filter:drop-shadow(0 8px 24px rgba(43,234,106,0.2));\n }\n\n /* Wallet list */\n .wc-wallet-list { list-style:none; margin:0; padding:0; }\n .wc-wallet-item {\n display:flex; align-items:center; gap:14px;\n padding:14px 16px; border-radius:16px; cursor:pointer;\n background:${bgGlass}; border:1.5px solid ${border};\n margin-bottom:10px; transition:all .2s; position:relative;\n overflow:hidden;\n }\n .wc-wallet-item::before {\n content:''; position:absolute; inset:0;\n background:${gradientAccent}; opacity:0;\n transition:opacity .2s; border-radius:16px;\n }\n .wc-wallet-item:hover::before { opacity:0.06; }\n .wc-wallet-item:hover {\n border-color:${accent}; transform:translateY(-2px);\n box-shadow:0 8px 24px ${glowAccent};\n }\n .wc-wallet-list-icon {\n width:44px; height:44px; border-radius:14px; object-fit:cover;\n position:relative; z-index:1;\n border:2px solid ${border}; box-shadow:0 2px 8px rgba(0,0,0,0.08);\n }\n .wc-wallet-list-icon-placeholder {\n width:44px; height:44px; border-radius:14px;\n background:${gradientAccent};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:18px;\n position:relative; z-index:1;\n }\n .wc-wallet-item-info { flex:1; position:relative; z-index:1; }\n .wc-wallet-item-name { font-size:15px; font-weight:700; }\n .wc-wallet-item-desc { font-size:12px; color:${textSecondary}; margin-top:2px; }\n .wc-wallet-item-arrow {\n color:${accent}; font-size:18px; font-weight:700;\n position:relative; z-index:1; transition:transform .2s;\n }\n .wc-wallet-item:hover .wc-wallet-item-arrow { transform:translateX(4px); }\n\n /* Grid picker */\n .wc-wallet-grid {\n display:grid; grid-template-columns:repeat(3,1fr);\n gap:12px; list-style:none; margin:0; padding:0;\n }\n .wc-wallet-grid-item {\n display:flex; flex-direction:column; align-items:center;\n gap:10px; cursor:pointer; padding:16px 8px; border-radius:20px;\n background:${bgGlass}; border:1.5px solid ${border};\n transition:all .2s;\n }\n .wc-wallet-grid-item:hover {\n border-color:${accent}; transform:translateY(-3px);\n box-shadow:0 8px 24px ${glowAccent};\n }\n .wc-wallet-grid-icon {\n width:52px; height:52px; border-radius:16px;\n object-fit:cover; border:2px solid ${border};\n box-shadow:0 4px 12px rgba(0,0,0,0.08);\n }\n .wc-wallet-grid-icon-placeholder {\n width:52px; height:52px; border-radius:16px;\n background:${gradientAccent};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:20px;\n }\n .wc-wallet-grid-name {\n font-size:12px; font-weight:600; text-align:center;\n color:${text}; line-height:1.3;\n }\n\n /* Tabs */\n .wc-tabs {\n display:flex; background:${bgCard}; border-radius:14px;\n padding:4px; margin-bottom:20px; border:1px solid ${border};\n }\n .wc-tab {\n flex:1; padding:10px; text-align:center; cursor:pointer;\n font-size:13px; font-weight:700; background:transparent;\n border:none; color:${textSecondary}; border-radius:11px;\n transition:all .25s;\n }\n .wc-tab:hover { color:${text}; }\n .wc-tab.active {\n background:${gradientAccent}; color:#fff;\n box-shadow:0 4px 16px ${glowAccent};\n }\n\n /* Deeplink */\n .wc-deeplink-btn {\n display:flex; align-items:center; justify-content:center; gap:10px;\n width:100%; padding:16px; border:none; border-radius:16px;\n background:${gradientAccent}; color:#fff;\n font-size:15px; font-weight:800; cursor:pointer;\n text-decoration:none; transition:all .25s;\n box-shadow:0 4px 24px ${glowAccent};\n box-sizing:border-box; position:relative; overflow:hidden;\n }\n .wc-deeplink-btn::after {\n content:''; position:absolute; inset:0;\n background:linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n background-size:200% 100%;\n animation:wc-shimmer 2.5s ease-in-out infinite;\n }\n .wc-deeplink-btn:hover {\n transform:translateY(-2px);\n box-shadow:0 8px 32px ${glowStrong};\n }\n .wc-deeplink-btn svg { position:relative; z-index:1; }\n .wc-deeplink-btn span { position:relative; z-index:1; }\n\n /* QR */\n .wc-qr-container {\n display:flex; flex-direction:column; align-items:center; gap:16px;\n }\n .wc-qr-wrap {\n padding:16px; background:#fff; border-radius:20px;\n box-shadow:0 4px 24px rgba(0,0,0,${isDark ? 0.3 : 0.08});\n border:2px solid ${glowAccent};\n }\n .wc-qr-wrap img { display:block; border-radius:8px; }\n .wc-qr-hint { font-size:12px; color:${textSecondary}; text-align:center; }\n\n /* Status */\n .wc-status {\n display:flex; align-items:center; justify-content:center;\n gap:10px; padding:20px 0 4px; font-size:13px; color:${textSecondary};\n }\n .wc-spinner {\n width:18px; height:18px; border:2.5px solid ${border};\n border-top-color:${accent}; border-radius:50%;\n animation:wc-spin .7s linear infinite;\n }\n\n /* Back */\n .wc-back {\n background:none; border:none; cursor:pointer; padding:0;\n color:${textSecondary}; display:flex; align-items:center;\n transition:all .2s; font-size:18px;\n }\n .wc-back:hover { color:${accent}; transform:translateX(-3px); }\n\n /* Footer */\n .wc-footer {\n text-align:center; padding:4px 0 20px;\n font-size:11px; color:${isDark ? '#3d5468' : '#b0bec5'};\n position:relative; z-index:1;\n }\n\n /* ── Success screen ── */\n .wc-success {\n display:flex; flex-direction:column; align-items:center;\n padding:8px 0 4px; gap:20px;\n }\n .wc-success-check {\n width:72px; height:72px; border-radius:50%;\n background:${gradientAccent};\n display:flex; align-items:center; justify-content:center;\n box-shadow:0 8px 32px ${glowStrong};\n animation:wc-popIn .4s cubic-bezier(.16,1,.3,1), wc-glow 2s ease-in-out infinite;\n }\n .wc-success-check svg { width:36px; height:36px; color:#fff; }\n .wc-success-title {\n font-size:20px; font-weight:800;\n background:${gradientAccent}; -webkit-background-clip:text;\n -webkit-text-fill-color:transparent; background-clip:text;\n }\n .wc-success-user {\n display:flex; align-items:center; gap:14px;\n background:${bgGlass}; border:1.5px solid ${border};\n border-radius:18px; padding:16px 18px; width:100%;\n box-sizing:border-box; backdrop-filter:blur(8px);\n }\n .wc-success-avatar {\n width:50px; height:50px; border-radius:50%; object-fit:cover;\n flex-shrink:0; border:2.5px solid ${accent};\n box-shadow:0 4px 16px ${glowAccent};\n }\n .wc-success-avatar-placeholder {\n width:50px; height:50px; border-radius:50%;\n background:${gradientAccent};\n display:flex; align-items:center; justify-content:center;\n color:#fff; font-weight:800; font-size:18px; flex-shrink:0;\n box-shadow:0 4px 16px ${glowAccent};\n }\n .wc-success-user-info { flex:1; min-width:0; }\n .wc-success-user-name { font-size:16px; font-weight:700; color:${text}; }\n .wc-success-user-addr {\n font-size:11px; color:${textSecondary}; margin-top:4px;\n font-family:'SF Mono',Monaco,Consolas,monospace;\n word-break:break-all; line-height:1.4;\n }\n .wc-success-done {\n width:100%; padding:16px; border:none; border-radius:16px;\n background:${gradientAccent}; color:#fff;\n font-size:15px; font-weight:800; cursor:pointer;\n transition:all .25s; position:relative; overflow:hidden;\n box-shadow:0 4px 24px ${glowAccent};\n }\n .wc-success-done::after {\n content:''; position:absolute; inset:0;\n background:linear-gradient(90deg, transparent, rgba(255,255,255,0.15), transparent);\n background-size:200% 100%;\n animation:wc-shimmer 2.5s ease-in-out infinite;\n }\n .wc-success-done:hover {\n transform:translateY(-2px);\n box-shadow:0 8px 32px ${glowStrong};\n }\n `;\n}\n\nconst TELEGRAM_SVG = `<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M11.944 0A12 12 0 1 0 24 12.056A12.01 12.01 0 0 0 11.944 0Zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472c-.18 1.898-.962 6.502-1.36 8.627c-.168.9-.499 1.201-.82 1.23c-.696.065-1.225-.46-1.9-.902c-1.056-.693-1.653-1.124-2.678-1.8c-1.185-.78-.417-1.21.258-1.91c.177-.184 3.247-2.977 3.307-3.23c.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345c-.48.33-.913.49-1.302.48c-.428-.008-1.252-.241-1.865-.44c-.752-.245-1.349-.374-1.297-.789c.027-.216.325-.437.893-.663c3.498-1.524 5.83-2.529 6.998-3.014c3.332-1.386 4.025-1.627 4.476-1.635Z\"/></svg>`;\n\nfunction generateQRDataUrl(text: string, size = 220): string {\n return `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(text)}&margin=8`;\n}\n\nexport class Modal {\n private overlay: HTMLDivElement | null = null;\n private theme: 'light' | 'dark';\n private onClose: () => void;\n\n constructor(theme: 'light' | 'dark', onClose: () => void) {\n this.theme = theme;\n this.onClose = onClose;\n }\n\n showWalletPicker(\n wallets: WalletMeta[],\n onSelect: (wallet: WalletMeta) => void,\n ): void {\n const useGrid = wallets.length >= 3;\n\n const listHtml = useGrid\n ? `<ul class=\"wc-wallet-grid\">${wallets\n .map(\n (w) => `\n <li class=\"wc-wallet-grid-item\" data-wallet-id=\"${w.walletId}\">\n ${\n w.icon\n ? `<img class=\"wc-wallet-grid-icon\" src=\"${w.icon}\" alt=\"${w.name}\" />`\n : `<div class=\"wc-wallet-grid-icon-placeholder\">${w.name.charAt(0)}</div>`\n }\n <span class=\"wc-wallet-grid-name\">${w.name}</span>\n </li>`,\n )\n .join('')}</ul>`\n : `<ul class=\"wc-wallet-list\">${wallets\n .map(\n (w) => `\n <li class=\"wc-wallet-item\" data-wallet-id=\"${w.walletId}\">\n ${\n w.icon\n ? `<img class=\"wc-wallet-list-icon\" src=\"${w.icon}\" alt=\"${w.name}\" />`\n : `<div class=\"wc-wallet-list-icon-placeholder\">${w.name.charAt(0)}</div>`\n }\n <div class=\"wc-wallet-item-info\">\n <div class=\"wc-wallet-item-name\">${w.name}</div>\n ${w.description ? `<div class=\"wc-wallet-item-desc\">${w.description}</div>` : ''}\n </div>\n <span class=\"wc-wallet-item-arrow\">›</span>\n </li>`,\n )\n .join('')}</ul>`;\n\n const html = `\n <div class=\"wc-header\">\n <h3 class=\"wc-title\">Connect Wallet</h3>\n <button class=\"wc-close\" data-action=\"close\">✕</button>\n </div>\n <div class=\"wc-mascot\">\n <img src=\"${MASCOT_URL}\" alt=\"mascot\" />\n </div>\n <p class=\"wc-subtitle\">Choose a wallet to get started</p>\n <div class=\"wc-body\">${listHtml}</div>\n <div class=\"wc-footer\">Powered by TonCorp</div>\n `;\n\n this.render(html);\n\n wallets.forEach((w) => {\n this.overlay\n ?.querySelector(`[data-wallet-id=\"${w.walletId}\"]`)\n ?.addEventListener('click', () => onSelect(w));\n });\n }\n\n showConnect(\n wallet: WalletMeta,\n deeplink: string,\n showBack: boolean,\n onBack?: () => void,\n ): void {\n const qrUrl = generateQRDataUrl(deeplink);\n\n const html = `\n <div class=\"wc-header\">\n <div class=\"wc-header-left\">\n ${showBack ? '<button class=\"wc-back\" data-action=\"back\">←</button>' : ''}\n ${wallet.icon ? `<img class=\"wc-header-icon\" src=\"${wallet.icon}\" />` : ''}\n <h3 class=\"wc-title\">${wallet.name}</h3>\n </div>\n <button class=\"wc-close\" data-action=\"close\">✕</button>\n </div>\n <p class=\"wc-subtitle\">Approve the connection in your wallet app</p>\n <div class=\"wc-body\">\n <div class=\"wc-tabs\">\n <button class=\"wc-tab active\" data-tab=\"mobile\"><span>Mobile</span></button>\n <button class=\"wc-tab\" data-tab=\"qr\"><span>QR Code</span></button>\n </div>\n <div data-panel=\"mobile\">\n <a class=\"wc-deeplink-btn\" href=\"${deeplink}\" target=\"_blank\">\n ${TELEGRAM_SVG}\n <span>Open in Telegram</span>\n </a>\n </div>\n <div data-panel=\"qr\" style=\"display:none\">\n <div class=\"wc-qr-container\">\n <div class=\"wc-qr-wrap\">\n <img src=\"${qrUrl}\" width=\"200\" height=\"200\" alt=\"QR\" />\n </div>\n <div class=\"wc-qr-hint\">Scan with your phone camera or wallet app</div>\n </div>\n </div>\n <div class=\"wc-status\">\n <div class=\"wc-spinner\"></div>\n <span>Waiting for confirmation...</span>\n </div>\n </div>\n <div class=\"wc-footer\">Powered by ${wallet.name}</div>\n `;\n\n this.render(html);\n\n const tabs = this.overlay?.querySelectorAll('.wc-tab');\n const panels = this.overlay?.querySelectorAll('[data-panel]');\n tabs?.forEach((tab) => {\n tab.addEventListener('click', () => {\n const target = (tab as HTMLElement).dataset.tab;\n tabs.forEach((t) => t.classList.remove('active'));\n tab.classList.add('active');\n panels?.forEach((p) => {\n (p as HTMLElement).style.display =\n (p as HTMLElement).dataset.panel === target ? 'block' : 'none';\n });\n });\n });\n\n if (showBack && onBack) {\n this.overlay\n ?.querySelector('[data-action=\"back\"]')\n ?.addEventListener('click', onBack);\n }\n }\n\n showSuccess(\n wallet: WalletMeta,\n address: string | null,\n user: { username?: string; avatar?: string } | null,\n onDone: () => void,\n ): void {\n const checkSvg = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`;\n\n const displayName = user?.username || 'User';\n const initials = displayName.slice(0, 2).toUpperCase();\n\n const avatarHtml = user?.avatar\n ? `<img class=\"wc-success-avatar\" src=\"${user.avatar}\" alt=\"${displayName}\" />`\n : `<div class=\"wc-success-avatar-placeholder\">${initials}</div>`;\n\n const html = `\n <div class=\"wc-header\">\n <h3 class=\"wc-title\">Connected</h3>\n <button class=\"wc-close\" data-action=\"close\">✕</button>\n </div>\n <div class=\"wc-body\">\n <div class=\"wc-success\">\n <div class=\"wc-success-check\">${checkSvg}</div>\n <div class=\"wc-success-title\">Connection Successful</div>\n <div class=\"wc-success-user\">\n ${avatarHtml}\n <div class=\"wc-success-user-info\">\n <div class=\"wc-success-user-name\">${displayName}</div>\n ${address ? `<div class=\"wc-success-user-addr\" title=\"${address}\">${address}</div>` : ''}\n </div>\n </div>\n <button class=\"wc-success-done\" data-action=\"done\"><span>Done</span></button>\n </div>\n </div>\n <div class=\"wc-footer\">Powered by ${wallet.name}</div>\n `;\n\n this.render(html);\n\n this.overlay\n ?.querySelector('[data-action=\"done\"]')\n ?.addEventListener('click', onDone);\n }\n\n updateStatus(text: string, showSpinner = true): void {\n const el = this.overlay?.querySelector('.wc-status');\n if (el) {\n el.innerHTML = `\n ${showSpinner ? '<div class=\"wc-spinner\"></div>' : ''}\n <span>${text}</span>\n `;\n }\n }\n\n private render(innerHtml: string): void {\n this.destroy();\n\n const el = document.createElement('div');\n el.id = MODAL_ID;\n el.innerHTML = `\n <style>${getStyles(this.theme)}</style>\n <div class=\"wc-overlay\">\n <div class=\"wc-modal\">${innerHtml}</div>\n </div>\n `;\n\n document.body.appendChild(el);\n this.overlay = el;\n\n this.overlay\n .querySelector('[data-action=\"close\"]')\n ?.addEventListener('click', () => { this.onClose(); this.destroy(); });\n\n this.overlay\n .querySelector('.wc-overlay')\n ?.addEventListener('click', (e) => {\n if ((e.target as HTMLElement).classList.contains('wc-overlay')) {\n this.onClose();\n this.destroy();\n }\n });\n }\n\n destroy(): void {\n document.getElementById(MODAL_ID)?.remove();\n this.overlay = null;\n }\n}\n","import type {\n WalletConnectConfig,\n WalletMeta,\n ConnectResult,\n ConnectSession,\n InitSessionResponse,\n SendTransactionParams,\n SendTransactionResult,\n WaitForResultOptions,\n TxStatusResult,\n WalletConnectEventType,\n WalletConnectEventCallback,\n} from './types';\nimport { WsTransport, PollingTransport } from './transport';\nimport { Modal } from './modal';\n\nconst STORAGE_KEY_PREFIX = '__wc_session_';\nconst PENDING_TTL_MS = 5 * 60 * 1000; // bỏ qua pending cũ hơn 5 phút (= session TTL)\nconst DEFAULT_REGISTRY_URL = 'https://cdn.jsdelivr.net/npm/toncorp-wallet-connect-sdk@latest/registry.json';\n\nexport class BaseWalletConnect {\n protected config: Required<\n Pick<WalletConnectConfig, 'appId' | 'registryUrl' | 'theme'>\n > &\n Pick<WalletConnectConfig, 'walletId' | 'siteTmaUrl'>;\n\n private listeners: Map<WalletConnectEventType, Set<WalletConnectEventCallback>> = new Map();\n private modal: Modal | null = null;\n private wsTransport: WsTransport | null = null;\n private pollingTransport: PollingTransport | null = null;\n private currentResult: ConnectResult | null = null;\n\n constructor(config: WalletConnectConfig) {\n this.config = {\n appId: config.appId,\n registryUrl: config.registryUrl || DEFAULT_REGISTRY_URL,\n walletId: config.walletId,\n theme: config.theme || 'dark',\n siteTmaUrl: config.siteTmaUrl,\n };\n\n this.restoreSession();\n this.registerGlobal();\n }\n\n // ── Public API ──\n\n async connect(): Promise<ConnectResult> {\n if (this.currentResult) {\n return this.currentResult;\n }\n\n const wallets = await this.fetchWallets();\n const filtered = this.filterWallets(wallets);\n\n if (filtered.length === 0) {\n throw new Error('No supported wallets available');\n }\n\n return new Promise<ConnectResult>((resolve, reject) => {\n const onClose = () => {\n this.cleanup();\n reject(new Error('User closed modal'));\n };\n\n this.modal = new Modal(this.config.theme, onClose);\n\n if (filtered.length === 1) {\n this.startConnect(filtered[0], false, resolve, reject);\n } else {\n this.modal.showWalletPicker(filtered, (wallet) => {\n this.startConnect(wallet, true, resolve, reject);\n });\n }\n });\n }\n\n disconnect(): void {\n // Find session from this instance or any other connected instance\n let session = this.currentResult?.session;\n if (!session && typeof window !== 'undefined') {\n for (const key of Object.keys(window)) {\n const inst = (window as any)[key];\n if (inst instanceof BaseWalletConnect && inst.currentResult?.session) {\n session = inst.currentResult.session;\n break;\n }\n }\n }\n\n // Call disconnect API (fire-and-forget)\n if (session) {\n const baseUrl = session.apiUrl.replace(/\\/$/, '');\n fetch(`${baseUrl}/connect/disconnect-by-token`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sessionToken: session.sessionToken }),\n }).catch(() => {});\n }\n\n // Clear all localStorage keys and window instances\n this.currentResult = null;\n this.removeStoredSession();\n this.clearAllRelatedSessions();\n this.cleanup();\n this.emit('disconnect');\n }\n\n async sendTransaction(\n params: SendTransactionParams,\n ): Promise<SendTransactionResult> {\n const session = this.currentResult?.session;\n if (!session?.apiUrl) {\n throw new Error('Wallet not connected');\n }\n\n const baseUrl = session.apiUrl.replace(/\\/$/, '');\n\n let res: Response;\n try {\n res = await fetch(`${baseUrl}/dex/sendTx`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...params,\n appId: this.config.appId,\n source: 'widget',\n }),\n });\n } catch {\n throw new Error('Connection failed');\n }\n\n if (res.status === 401) {\n throw new Error('Wallet not connected');\n }\n if (res.status === 429) {\n throw new Error('Too many pending transactions');\n }\n if (!res.ok) {\n throw new Error('Failed to send transaction');\n }\n\n const data = await res.json();\n\n // Lưu pending để nếu onedex đóng (mở onewallet ký) rồi mở lại -> tự check /dex/status.\n this.storePending({\n type: 'tx',\n txId: data.txId,\n apiUrl: session.apiUrl,\n ts: Date.now(),\n });\n\n return { txId: data.txId };\n }\n\n async waitForResult(\n txId: string,\n options?: WaitForResultOptions,\n ): Promise<TxStatusResult> {\n const session = this.currentResult?.session;\n if (!session?.apiUrl) {\n throw new Error('Wallet not connected');\n }\n\n const interval = options?.interval ?? 2000;\n const timeout = options?.timeout ?? 120000;\n const baseUrl = session.apiUrl.replace(/\\/$/, '');\n const start = Date.now();\n\n while (true) {\n let res: Response;\n try {\n res = await fetch(`${baseUrl}/dex/status/${txId}`);\n } catch {\n throw new Error('Connection failed');\n }\n\n if (!res.ok) {\n throw new Error('Failed to fetch transaction status');\n }\n\n const data = await res.json();\n const result: TxStatusResult = {\n status: data.status,\n msgHash: data.msgHash,\n action: data.action,\n };\n\n if (data.status !== 'PENDING') {\n this.clearPending(); // tx đã có kết quả -> không cần resume nữa\n this.emit('resolved', result);\n return result;\n }\n\n this.emit('pending', result);\n\n if (Date.now() - start >= timeout) {\n throw new Error('Timeout');\n }\n\n await this.sleep(interval);\n }\n }\n\n isConnected(): boolean {\n return this.currentResult !== null;\n }\n\n getWallet(): ConnectResult | null {\n return this.currentResult;\n }\n\n on(event: WalletConnectEventType, cb: WalletConnectEventCallback): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(cb);\n }\n\n off(event: WalletConnectEventType, cb: WalletConnectEventCallback): void {\n this.listeners.get(event)?.delete(cb);\n }\n\n // ── Internal ──\n\n private get storageKey(): string {\n const walletId = this.config.walletId;\n const suffix = Array.isArray(walletId)\n ? walletId.sort().join('_')\n : walletId || 'all';\n return `${STORAGE_KEY_PREFIX}${suffix}__`;\n }\n\n private async fetchWallets(): Promise<WalletMeta[]> {\n const url = this.config.registryUrl.replace(/\\/$/, '');\n const res = await fetch(url);\n if (!res.ok) throw new Error('Failed to fetch wallets');\n return res.json();\n }\n\n private filterWallets(wallets: WalletMeta[]): WalletMeta[] {\n if (!this.config.walletId) return wallets;\n\n const ids = Array.isArray(this.config.walletId)\n ? this.config.walletId\n : [this.config.walletId];\n\n return wallets.filter((w) => ids.includes(w.walletId));\n }\n\n private async startConnect(\n wallet: WalletMeta,\n showBack: boolean,\n resolve: (result: ConnectResult) => void,\n reject: (error: Error) => void,\n ): Promise<void> {\n try {\n const session = await this.initSession(wallet);\n\n // Lưu pending TRƯỚC khi mở onewallet (onedex có thể bị đóng ngay sau đó).\n // Mở lại sẽ đọc cái này để biết phải check /connect/status.\n this.storePending({\n type: 'connect',\n sessionToken: session.sessionToken,\n walletId: wallet.walletId,\n apiUrl: wallet.apiUrl,\n ts: Date.now(),\n });\n\n this.modal?.showConnect(wallet, session.deeplink, showBack, () => {\n this.cleanup();\n this.connect().then(resolve).catch(reject);\n });\n\n this.listenForConfirmation(\n wallet.apiUrl,\n session.sessionToken,\n (result) => {\n // Attach session info for disconnect\n result.session = {\n sessionToken: session.sessionToken,\n walletId: wallet.walletId,\n apiUrl: wallet.apiUrl,\n };\n this.currentResult = result;\n this.storeSession(result);\n this.clearPending(); // đã resolve ngay trong page -> không cần resume\n this.syncToWalletInstance(wallet.walletId, result);\n this.emit('connect', result);\n\n // Show success screen\n this.modal?.showSuccess(\n wallet,\n result.wallet?.address || null,\n result.user || null,\n () => {\n this.modal?.destroy();\n this.modal = null;\n resolve(result);\n },\n );\n },\n () => {\n this.clearPending();\n this.modal?.updateStatus('Connection rejected', false);\n setTimeout(() => {\n this.modal?.destroy();\n this.modal = null;\n reject(new Error('Connection rejected by user'));\n }, 1500);\n },\n () => {\n this.clearPending();\n this.modal?.updateStatus('Session expired', false);\n this.emit('session_expired');\n setTimeout(() => {\n this.modal?.destroy();\n this.modal = null;\n reject(new Error('Session expired'));\n }, 1500);\n },\n );\n } catch (err) {\n this.modal?.destroy();\n this.modal = null;\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n private async initSession(wallet: WalletMeta): Promise<InitSessionResponse> {\n const baseUrl = wallet.apiUrl.replace(/\\/$/, '');\n const res = await fetch(`${baseUrl}/connect/init`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n appId: this.config.appId,\n walletId: wallet.walletId,\n origin: typeof window !== 'undefined' ? window.location.origin : '',\n siteTmaUrl: this.config.siteTmaUrl,\n // Platform nơi DApp đang chạy — tự lấy từ Telegram WebApp (rỗng nếu DApp mở ngoài Telegram).\n platform:\n (typeof window !== 'undefined' &&\n (window as any).Telegram?.WebApp?.platform) || '',\n }),\n });\n if (!res.ok) throw new Error('Failed to init session');\n return res.json();\n }\n\n private listenForConfirmation(\n apiUrl: string,\n sessionToken: string,\n onConfirmed: (result: ConnectResult) => void,\n onRejected: () => void,\n onExpired: () => void,\n ): void {\n let resolved = false;\n\n const wrap = <T extends (...args: any[]) => void>(fn: T): T =>\n ((...args: any[]) => {\n if (resolved) return;\n resolved = true;\n this.wsTransport?.close();\n this.pollingTransport?.stop();\n fn(...args);\n }) as unknown as T;\n\n const wrappedConfirm = wrap(onConfirmed);\n const wrappedReject = wrap(onRejected);\n const wrappedExpire = wrap(onExpired);\n\n // Start polling immediately as primary transport\n this.pollingTransport = new PollingTransport(\n apiUrl,\n sessionToken,\n wrappedConfirm,\n wrappedReject,\n wrappedExpire,\n (msg) => this.emit('error', msg),\n );\n this.pollingTransport.start();\n\n // Also try WS for faster response\n this.wsTransport = new WsTransport(\n apiUrl,\n sessionToken,\n wrappedConfirm,\n wrappedReject,\n wrappedExpire,\n () => {}, // WS fail is fine, polling is running\n );\n this.wsTransport.connect();\n }\n\n private cleanup(): void {\n this.wsTransport?.close();\n this.wsTransport = null;\n this.pollingTransport?.stop();\n this.pollingTransport = null;\n this.modal?.destroy();\n this.modal = null;\n }\n\n private emit(event: WalletConnectEventType, data?: any): void {\n this.listeners.get(event)?.forEach((cb) => cb(data));\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n // ── Persistence ──\n\n private storeSession(result: ConnectResult): void {\n try {\n localStorage.setItem(this.storageKey, JSON.stringify(result));\n } catch {\n // localStorage unavailable\n }\n }\n\n private restoreSession(): void {\n try {\n const stored = localStorage.getItem(this.storageKey);\n if (stored) {\n this.currentResult = JSON.parse(stored);\n }\n } catch {\n // ignore\n }\n }\n\n private removeStoredSession(): void {\n try {\n localStorage.removeItem(this.storageKey);\n } catch {\n // ignore\n }\n }\n\n // ── Pending (resume sau khi onedex đóng/mở lại) ──\n\n private get pendingKey(): string {\n return `${this.storageKey}pending`;\n }\n\n private storePending(data: Record<string, any>): void {\n try {\n localStorage.setItem(this.pendingKey, JSON.stringify(data));\n } catch {\n // ignore\n }\n }\n\n private readPending(): Record<string, any> | null {\n try {\n const raw = localStorage.getItem(this.pendingKey);\n return raw ? JSON.parse(raw) : null;\n } catch {\n return null;\n }\n }\n\n private clearPending(): void {\n try {\n localStorage.removeItem(this.pendingKey);\n } catch {\n // ignore\n }\n }\n\n /**\n * Gọi 1 lần lúc DApp startup. Nếu lần trước onedex bị đóng giữa chừng\n * (connect/tx đang pending), check status server và TRẢ VỀ kết quả để DApp tự\n * hiển thị. Trả null nếu không có pending / chưa xong / lỗi tạm.\n *\n * KHÔNG emit event, KHÔNG tự chạy -> luồng cũ (không đóng) hoàn toàn không bị\n * ảnh hưởng; chỉ DApp chủ động gọi lúc mount.\n */\n async resume(): Promise<\n | { type: 'connect'; status: string; result?: ConnectResult }\n | { type: 'tx'; status: string; result: TxStatusResult }\n | null\n > {\n const pending = this.readPending();\n if (!pending) return null;\n\n // Bỏ qua pending quá cũ (tránh resume session/tx rác).\n if (!pending.ts || Date.now() - pending.ts > PENDING_TTL_MS) {\n this.clearPending();\n return null;\n }\n\n const baseUrl = String(pending.apiUrl || '').replace(/\\/$/, '');\n if (!baseUrl) {\n this.clearPending();\n return null;\n }\n\n try {\n if (pending.type === 'connect') {\n const res = await fetch(\n `${baseUrl}/connect/status?token=${pending.sessionToken}`,\n );\n if (!res.ok) return null; // lỗi tạm -> giữ pending, thử lại lần mở sau\n const data = await res.json();\n\n if (data.status === 'CONFIRMED') {\n const result: ConnectResult = {\n wallet: data.wallet ?? null,\n user: data.user ?? {},\n session: {\n sessionToken: pending.sessionToken,\n walletId: pending.walletId,\n apiUrl: pending.apiUrl,\n },\n };\n this.currentResult = result;\n this.storeSession(result);\n this.syncToWalletInstance(pending.walletId, result);\n this.clearPending();\n return { type: 'connect', status: 'CONFIRMED', result };\n }\n if (data.status === 'REJECTED' || data.status === 'EXPIRED') {\n this.clearPending();\n return { type: 'connect', status: data.status };\n }\n return null; // PENDING: chưa duyệt xong -> giữ pending.\n }\n\n if (pending.type === 'tx') {\n const res = await fetch(`${baseUrl}/dex/status/${pending.txId}`);\n if (!res.ok) return null;\n const data = await res.json();\n\n if (data.status !== 'PENDING') {\n this.clearPending();\n return {\n type: 'tx',\n status: data.status,\n result: {\n status: data.status,\n msgHash: data.msgHash,\n action: data.action,\n },\n };\n }\n return null; // còn pending -> giữ.\n }\n } catch {\n // mạng lỗi -> giữ pending để startup lần sau resume.\n }\n return null;\n }\n\n // ── Global window registration ──\n\n private registerGlobal(): void {\n if (typeof window === 'undefined') return;\n\n const walletId = this.config.walletId;\n const key = Array.isArray(walletId)\n ? walletId[0]\n : walletId || 'walletconnect';\n\n (window as any)[key] = this;\n }\n\n /** Sync connect result to the specific wallet instance on window (e.g. window.onedollar) */\n private syncToWalletInstance(walletId: string, result: ConnectResult): void {\n if (typeof window === 'undefined') return;\n\n const target = (window as any)[walletId];\n if (target && target !== this && target instanceof BaseWalletConnect) {\n (target as any).currentResult = result;\n (target as any).storeSession(result);\n }\n }\n\n /** Clear all related sessions from all window instances and localStorage */\n private clearAllRelatedSessions(): void {\n if (typeof window === 'undefined') return;\n\n // Find all BaseWalletConnect instances on window and clear them\n try {\n for (const key of Object.keys(window)) {\n const inst = (window as any)[key];\n if (inst && inst !== this && inst instanceof BaseWalletConnect) {\n (inst as any).currentResult = null;\n (inst as any).removeStoredSession();\n }\n }\n } catch {\n // ignore\n }\n\n // Clear all wc localStorage keys\n try {\n const toRemove: string[] = [];\n for (let i = 0; i < localStorage.length; i++) {\n const k = localStorage.key(i);\n if (k && k.startsWith(STORAGE_KEY_PREFIX)) {\n toRemove.push(k);\n }\n }\n toRemove.forEach((k) => localStorage.removeItem(k));\n } catch {\n // ignore\n }\n }\n}\n","import { BaseWalletConnect } from './client';\nimport type { WalletConnectConfig } from './types';\n\nexport class OneDollarConnect extends BaseWalletConnect {\n constructor(config: Omit<WalletConnectConfig, 'walletId'>) {\n super({ ...config, walletId: 'onedollar' });\n }\n}\n","import { BaseWalletConnect } from './client';\nimport type { WalletConnectConfig } from './types';\n\nexport class TonPassConnect extends BaseWalletConnect {\n constructor(config: Omit<WalletConnectConfig, 'walletId'>) {\n super({ ...config, walletId: 'tonpass' });\n }\n}\n"],"names":["WsTransport","constructor","apiUrl","sessionToken","onConfirmed","onRejected","onExpired","onError","this","ws","reconnectTimer","closed","connect","replace","connectSocketIO","baseUrl","fetch","then","res","text","body","jsonStr","sid","JSON","parse","wsUrl","WebSocket","onopen","send","onmessage","event","msg","data","startsWith","payload","slice","eventName","handleEvent","onclose","scheduleReconnect","onerror","catch","close","setTimeout","clearTimeout","PollingTransport","timer","stopped","start","poll","setInterval","ok","json","status","wallet","user","stop","clearInterval","MODAL_ID","Modal","theme","onClose","overlay","showWalletPicker","wallets","onSelect","html","length","map","w","walletId","icon","name","charAt","join","description","render","forEach","querySelector","addEventListener","showConnect","deeplink","showBack","onBack","qrUrl","size","encodeURIComponent","generateQRDataUrl","tabs","querySelectorAll","panels","tab","target","dataset","t","classList","remove","add","p","style","display","panel","showSuccess","address","onDone","displayName","username","initials","toUpperCase","avatar","updateStatus","showSpinner","el","innerHTML","innerHtml","destroy","document","createElement","id","isDark","bgCard","bgGlass","textSecondary","border","accent","gradientAccent","glowAccent","glowStrong","getStyles","appendChild","e","contains","getElementById","STORAGE_KEY_PREFIX","BaseWalletConnect","config","listeners","Map","modal","wsTransport","pollingTransport","currentResult","appId","registryUrl","siteTmaUrl","restoreSession","registerGlobal","fetchWallets","filtered","filterWallets","Error","Promise","resolve","reject","cleanup","startConnect","disconnect","session","window","key","Object","keys","inst","method","headers","stringify","removeStoredSession","clearAllRelatedSessions","emit","sendTransaction","params","source","storePending","type","txId","ts","Date","now","waitForResult","options","interval","timeout","result","msgHash","action","clearPending","sleep","isConnected","getWallet","on","cb","has","set","Set","get","off","delete","storageKey","suffix","Array","isArray","sort","url","ids","filter","includes","initSession","listenForConfirmation","storeSession","syncToWalletInstance","err","String","origin","location","platform","Telegram","WebApp","resolved","wrap","fn","args","wrappedConfirm","wrappedReject","wrappedExpire","ms","localStorage","setItem","stored","getItem","removeItem","pendingKey","readPending","raw","resume","pending","toRemove","i","k","push","super"],"mappings":"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"}
|