UNPKG

@linenext/dapp-portal-sdk

Version:

Dapp Portal SDK

1 lines 119 kB
import{liff as e}from"@line/liff";import{OKXConnectError as t,OKXUniversalConnectUI as n,THEME as i}from"@okxconnect/ui";import s from"axios";import a from"decimal.js";import{EventEmitter as o}from"eventemitter3";import{jwtDecode as r}from"jwt-decode";import{v4 as c}from"uuid";import l from"async-lock";import{createAppKit as d}from"@reown/appkit";import{createAppKitWalletButton as p}from"@reown/appkit-wallet-button";import{kaia as h,kairos as u}from"viem/chains";function m(e){if(!e||"string"!=typeof e)return!1;return/(?:^|\s|\/)Line(?:\/|$|\s)/i.test(e)}function y(e){if(!e||"string"!=typeof e)return!1;return/\((iPhone|iPad|iPod)(?:[0-9,.]*)?(?:\s|;)|CPU iPhone OS|CPU OS/i.test(e)&&!/IPhoneSimulator|iPhone look-alike/i.test(e)}function g(e){if(!e||"string"!=typeof e)return!1;return/iPhone|iPad|iPod|Android|webOS|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(e)}function w(e){if(!e||"string"!=typeof e)return!1;return/Klutch|Kaikas|Kaia/i.test(e)}var f,_,v;function T(e){return e===f.Google||e===f.Line||e===f.Naver||e===f.Kakao||e===f.Apple||e===f.Wechat}function P(e){switch(e){case f.Google:case f.Kakao:case f.Line:case f.Apple:case f.Naver:case f.Wechat:return m(navigator.userAgent)?_.Liff:_.Web;case f.OKX:return _.OKX;case f.Kaia:return g(navigator.userAgent)?_.Mobile:_.Extension;case f.BITGET:return _.BITGET}}!function(e){e.Google="GOOGLE",e.Line="LINE",e.Naver="NAVER",e.Kakao="KAKAO",e.Apple="APPLE",e.Wechat="WECHAT",e.Kaia="KAIA",e.OKX="OKX",e.BITGET="BITGET"}(f||(f={})),function(e){e.Web="Web",e.Liff="Liff",e.Extension="Extension",e.KaiaMobileIab="KaiaMobileIab",e.Mobile="Mobile",e.OKX="OKX",e.BITGET="BITGET"}(_||(_={}));class b extends Error{constructor(e){super(e.message),this.name="RpcException",this.code=e.code,this.data=e.data,Object.setPrototypeOf(this,b.prototype)}}!function(e){e.invalidRequest="-32600",e.methodNotFound="-32601",e.invalidParams="-32602",e.internal="-32603",e.parse="-32700"}(v||(v={}));const E={"-32600":{standard:"JSON RPC 2.0",message:"The JSON sent is not a valid Request object."},"-32601":{standard:"JSON RPC 2.0",message:"The method does not exist / is not available."},"-32602":{standard:"JSON RPC 2.0",message:"Invalid method parameter(s)."},"-32603":{standard:"JSON RPC 2.0",message:"Internal JSON-RPC error."},"-32700":{standard:"JSON RPC 2.0",message:"Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text."}},I=-32001;class k extends Error{constructor(e,t,n){const i=Number.parseInt(e);if(isNaN(i))throw new Error('"code" must be an integer.');super(t??E[v.invalidRequest].message),this.code=i,n&&(this.data=n),Object.setPrototypeOf(this,k.prototype)}}class C extends k{constructor(e,t){super(v.internal,e,t)}}function M(e){return e instanceof t&&300===e.code&&e.message.includes("user rejects error")?{code:I,message:"User canceled"}:e instanceof t&&(e.message.includes("Request params address error")||e.message.includes("must provide an Ethereum address"))?{code:-32004,message:"Invalid from address"}:"string"==typeof e?{code:Number.parseInt(v.internal),message:e}:e instanceof k||e instanceof b?{code:e.code,message:e.message,data:e.data}:{code:Number.parseInt(v.internal),message:E[v.internal].message}}function x(e){return"object"==typeof e&&null!==e&&"number"==typeof e.code&&"string"==typeof e.message}function S(){return null!=globalThis.klaytn}function N(){const e=globalThis.klaytn;if(!e)throw new C("Extension is not installed");return e}function L(e,t){return"klay_signTransaction"!==e?t:function(e){const t=e;return{raw:t.rawTransaction}}(t)}var W,j;!function(e){e.WEB="WEB",e.KAIA_WALLET="KAIA_WALLET",e.OKX="OKX",e.BITGET="BITGET"}(W||(W={})),function(e){e.START="START",e.CONNECT="CONNECT",e.MISSION="MISSION"}(j||(j={}));const R="walletType",A="selectedWalletAddress",U="deviceId",D="providerType",z="eventRead",O="eventMissionCompleted",X="noticeRead";class G{constructor(e){this.chainId=e}clear(){this.removeItem(R),this.removeItem(A)}setWalletType(e){this.setItem(R,e)}getWalletType(){return this.getItem(R)}setProviderType(e){this.setItem(D,e)}getProviderType(){return this.getItem(D)}setSelectedWallet(e){this.setItem(A,e)}getConnectedWallet(){return this.getItem(A)}getDeviceId(){return this.getItem(U)}setDeviceId(e){this.setItem(U,e)}getEventRead(e,t){const n=t.toString().toLowerCase(),i=`${z}:${n}:${e}`;return"true"===this.getItem(i)}setEventRead(e,t,n){const i=n.toString().toLowerCase(),s=`${z}:${i}:${e}`;this.setItem(s,t?"true":"false")}getNoticeRead(e){const t=`${X}:${e}`;return"true"===this.getItem(t)}setNoticeRead(e,t){const n=`${X}:${e}`;this.setItem(n,t?"true":"false")}getMissionCompleted(e){const t=`${O}:${e}`,n=this.getItem(t);if(n){const[e,t]=n.split(":");return[e,"true"===t]}return["",!1]}setMissionCompleted(e,t,n){const i=`${O}:${n}`;this.setItem(i,`${e}:`+(t?"true":"false"))}getKeyWithPrefix(e){return`sdk.dappportal.io:${this.chainId}:${e}`}setItem(e,t){localStorage.setItem(this.getKeyWithPrefix(e),t)}getItem(e){return localStorage.getItem(this.getKeyWithPrefix(e))}removeItem(e){localStorage.removeItem(this.getKeyWithPrefix(e))}}class K{constructor(e){this.getEvents=async e=>{const t=`${this.baseUrl}/api/v1/events`;try{return(await s.get(t,{params:{bannerTiming:e},headers:{"x-client-id":this.clientId,"Content-Type":"application/json"}})).data}catch(n){throw console.error("Fail to get event - ",n),n}},this.setEventUserConnection=async(e,t,n)=>{const i=`${this.baseUrl}/api/v1/events/${e}/connect`;try{await s.post(i,{connection_token:t,wallet_address:n},{headers:{"x-client-id":this.clientId,"Content-Type":"application/json"}})}catch(a){throw console.error("Fail to set event user connection - ",a),a}},this.validateSubMissionCompletion=async(e,t,n)=>{const i=`${this.baseUrl}/api/v1/events/${e}/validate`;try{await s.post(i,{sub_mission_index:t,wallet_address:n},{headers:{"x-client-id":this.clientId,"Content-Type":"application/json"}})}catch(a){throw console.warn("Fail to validate submission - ",a),a}},this.clientId=e.clientId,this.baseUrl=e.relayServerConfig.baseUrl}}class F{constructor(e){this.eventClient=new K(e)}async getEventWhenStart(){return await this.eventClient.getEvents("START")}async getEventWhenConnect(){return await this.eventClient.getEvents("CONNECT")}async setEventUserConnection(e,t,n){try{await this.eventClient.setEventUserConnection(e,t,n)}catch(i){console.warn(i)}}async validateSubMissionCompletion(e,t,n){try{return await this.eventClient.validateSubMissionCompletion(e,t,n),!0}catch(i){return console.warn(i),!1}}}class Z{constructor(e,t,n){this.config=e,this.walletProvider=t,this.internalEventProvider=n,this.sdkLocalStorage=new G(e.chainId),this.handler=new F(e)}async callback(e,t){const n=await this.walletProvider.request({method:"kaia_accounts"});n.length>0&&await this.handler.validateSubMissionCompletion(e,t,n[0])&&(this.sdkLocalStorage.setEventRead(e,!1,j.MISSION),this.sdkLocalStorage.setMissionCompleted(e,!0,n[0]),await this.internalEventProvider.openEventBannerWhenSubMissionCompleted(e,n[0]))}}var V;!function(e){e.POPUP_BLOCKED="POPUP_BLOCKED",e.PAYMENT_HISTORY_SIGN="PAYMENT_HISTORY_SIGN",e.PAYMENT_HISTORY_OPEN="PAYMENT_HISTORY_OPEN",e.ERC20_APPROVE="ERC20_APPROVE",e.ERC20_APPROVE_RESULT="ERC20_APPROVE_RESULT"}(V||(V={}));const Y={ko:{payment_reqeust_verification_title:"본인 인증",payment_reqeust_verification_desc:"거래내역을 확인하려면 본인 인증이 필요합니다. 아래 버튼을 눌러 서명을 진행해주세요.",payment_reqeust_verification_cancel:"취소",payment_reqeust_verification_submit:"진행하기"},en:{payment_reqeust_verification_title:"Identity verification",payment_reqeust_verification_desc:"Identity verification is required to view your transaction history. Click the button below to sign.",payment_reqeust_verification_cancel:"Cancel",payment_reqeust_verification_submit:"Proceed"},ja:{payment_reqeust_verification_title:"本人認証",payment_reqeust_verification_desc:"取引履歴を確認するには本人認証が必要です。以下のボタンを押して署名を行ってください。",payment_reqeust_verification_cancel:"キャンセル",payment_reqeust_verification_submit:"次に進む"},zh:{payment_reqeust_verification_title:"本人驗證",payment_reqeust_verification_desc:"若要查看交易記錄,需進行本人驗證。請點擊下方按鍵進行簽名。",payment_reqeust_verification_cancel:"取消",payment_reqeust_verification_submit:"進行"},th:{payment_reqeust_verification_title:"ยืนยันตัวตน",payment_reqeust_verification_desc:"จำเป็นต้องยืนยันตัวตนเพื่อดูประวัติธุรกรรมของคุณ คลิกปุ่มด้านล่างเพื่อลงลายมือชื่อ",payment_reqeust_verification_cancel:"ยกเลิก",payment_reqeust_verification_submit:"ดำเนินการ"}},B={ko:{payment_complete_verification_title:"인증 완료",payment_complete_verification_desc:"본인 인증이 완료되었습니다. 아래 버튼을 눌러 거래내역을 확인하세요.",payment_complete_verification_cancel:"취소",payment_complete_verification_submit:"진행하기"},en:{payment_complete_verification_title:"Verification complete",payment_complete_verification_desc:"Identity verification complete. Click the button below to view your transaction history.",payment_complete_verification_cancel:"Cancel",payment_complete_verification_submit:"Proceed"},ja:{payment_complete_verification_title:"認証完了",payment_complete_verification_desc:"本人認証が完了しました。以下のボタンを押して取引履歴をご確認ください。",payment_complete_verification_cancel:"キャンセル",payment_complete_verification_submit:"次に進む"},zh:{payment_complete_verification_title:"驗證完成",payment_complete_verification_desc:"已完成本人驗證。請點擊下方按鍵查看交易記錄。",payment_complete_verification_cancel:"取消",payment_complete_verification_submit:"進行"},th:{payment_complete_verification_title:"ยืนยันสำเร็จ",payment_complete_verification_desc:"ยืนยันตัวตนสำเร็จ คลิกที่ปุ่มด้านล่างเพื่อดูประวัติธุรกรรมของคุณ",payment_complete_verification_cancel:"ยกเลิก",payment_complete_verification_submit:"ดำเนินการ"}},H={ko:{wallet_block_popup_title:"팝업 차단",wallet_block_popup_desc:"팝업이 차단되었습니다. 계속 진행하시겠습니까?",wallet_block_popup_cancel:"취소",wallet_block_popup_submit:"진행하기"},en:{wallet_block_popup_title:"Block pop-ups",wallet_block_popup_desc:"Pop-ups have been blocked. Do you want to continue?",wallet_block_popup_cancel:"Cancel",wallet_block_popup_submit:"Proceed"},ja:{wallet_block_popup_title:"ポップアップブロック",wallet_block_popup_desc:"ポップアップがブロックされました。続行しますか?",wallet_block_popup_cancel:"キャンセル",wallet_block_popup_submit:"次に進む"},zh:{wallet_block_popup_title:"封鎖彈出視窗",wallet_block_popup_desc:"已封鎖彈出視窗。要繼續進行嗎?",wallet_block_popup_cancel:"取消",wallet_block_popup_submit:"進行"},th:{wallet_block_popup_title:"บล็อกป็อบอัป",wallet_block_popup_desc:"ป็อบอัปถูกบล็อกแล้ว ต้องการดำเนินการต่อหรือไม่?",wallet_block_popup_cancel:"ยกเลิก",wallet_block_popup_submit:"ดำเนินการ"}};class J{constructor(e){this.chainRpcUrl=e.chainNodeRpcEndpoint}async requestRpc(e){const t={id:0,jsonrpc:"2.0",...e},n=await fetch(this.chainRpcUrl,{method:"POST",body:JSON.stringify(t),headers:{"Content-Type":"application/json"}}),{result:i,error:s}=await n.json();if(s)throw console.error("Error in RPC request",s),s;return i}}const q="VISIT",Q="8217",$="reownVerified",ee="bitgetExtensionInstalled",te=(e,t,n,i,s,a)=>{if(e!==V.ERC20_APPROVE&&e!==V.ERC20_APPROVE_RESULT)throw new Error(`[Internal Error] - ${e} is not supported in this context.`);const o=e===V.ERC20_APPROVE?ne:ie,r=e===V.ERC20_APPROVE?"payment_approve_permission_popup_title":"payment_proceed_payment_popup_title",c=e===V.ERC20_APPROVE?"payment_approve_permission_popup_desc":"payment_proceed_payment_popup_desc",l=e===V.ERC20_APPROVE?"payment_approve_permission_popup_submit":"payment_proceed_payment_popup_submit",d=e===V.ERC20_APPROVE?"payment_approve_permission_popup_cancel":"payment_proceed_payment_popup_cancel",p=`\n <div class="row">\n <div class="row1" id="dappportalsdk_tokenApproveTokenNameTitle"></div>\n <div class="row2Wrapper">\n <img class="imageToken" src="${s}" alt="img"/>\n <div class="row2">\n ${i}\n </div>\n </div>\n </div> \n <div class="sizedBox"></div>\n <div class="row">\n <div class="row1" id="dappportalsdk_tokenApproveScopeTitle"></div>\n <div class="row2">\n ${a}\n </div>\n </div> \n `;return`\n <!DOCTYPE html>\n <html lang="en">\n <head>\n <meta charset="UTF-8">\n <style>\n body {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\n sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n }\n .bottomSheetContainer {\n position: absolute;\n display: flex;\n flex-direction: column;\n align-items: center;\n bottom: 0;\n left: 0;\n right: 0;\n width: 100%;\n height: 480px;\n z-index: 999999999999999;\n border-top-left-radius: 16px;\n border-top-right-radius: 16px;\n overflow-y: auto;\n background-color: #ffffff;\n box-shadow: 0px -8px 12px 0px rgba(16, 24, 64, 0.1);\n }\n .dialogContainer {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n background-color: #ffffff;\n z-index: 999999999999999;\n border-radius: 26px;\n width: 420px;\n height: ${e===V.ERC20_APPROVE?"437px":"355px"};\n position: fixed;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n }\n .headerContainer {\n width: 100%;\n height: ${t?"50px":"24px"};\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n }\n .closeBtn {\n width: 24px;\n height: 24px;\n margin: 16px;\n cursor: pointer;\n }\n .imageCheck {\n width: 72px;\n height: 72px;\n margin-top: 16px;\n }\n .title {\n font-size: 28px;\n font-weight: 700;\n color: #000000;\n margin-top: 18px;\n }\n .desc {\n font-size: ${t?"14px":"16px"};\n font-weight: 400;\n color: #666666;\n margin-top: 8px;\n padding: 0 24px;\n text-align: center;\n }\n .flexBox {\n flex: 1; \n }\n .sizedBox {\n height: 12px;\n }\n .buttonWrapper {\n box-sizing: border-box;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n padding: ${t?"36px 24px":"40px 40px"};\n }\n .cancelBtn {\n background-color: #FFFFFF;\n color: #000000;\n font-size: 16px;\n font-weight: 700;\n border-radius: 8px;\n border: 1px solid #EFEFEF;\n padding: 12px 0;\n width: 100%;\n text-align: center;\n cursor: pointer;\n }\n .approveBtn {\n margin-left: 7px;\n background-color: #06C755;\n color: #FFFFFF;\n font-size: 16px;\n font-weight: 700;\n border-radius: 8px;\n padding: 12px 0;\n width: 100%;\n text-align: center;\n cursor: pointer;\n }\n .row {\n width: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n padding: 0 ${t?"24px":"40px"}; \n }\n .row1 {\n font-size: ${t?"14px":"16px"};\n font-weight: 400;\n color: #666666;\n }\n .row2 {\n font-size: ${t?"14px":"16px"};\n font-weight: 400;\n color: #000000;\n }\n .imageToken {\n width: 16px;\n height: 16px;\n margin-right: 2px; \n }\n .row2Wrapper {\n display: flex;\n flex-direction: row;\n align-items: center;\n }\n </style>\n </head>\n <body>\n <script>\n const browserLang = window.navigator.language.slice(0, 2);\n const xlt = ${JSON.stringify(o)};\n const translations = xlt[browserLang] || xlt['en'];\n console.log(translations);\n \n window.onload = function() {\n const tokenNameElement = document.getElementById('dappportalsdk_tokenApproveTokenNameTitle');\n if (tokenNameElement) {\n tokenNameElement.innerHTML = translations.payment_approve_permission_popup_token_name;\n }\n const scopeElement = document.getElementById('dappportalsdk_tokenApproveScopeTitle');\n if (scopeElement) {\n scopeElement.innerHTML = translations.payment_approve_permission_popup_scope;\n }\n document.getElementById('dappportalsdk_title').innerHTML = translations.${r};\n document.getElementById('dappportalsdk_desc').innerHTML = translations.${c};\n document.getElementById('dappportalsdk_cancelBtn').innerHTML = translations.${d};\n document.getElementById('dappportalsdk_approveBtn').innerHTML = translations.${l};\n \n document.getElementById('dappportalsdk_closeBtn').addEventListener('click', function() {\n window.parent.postMessage({event: 'Canceled'}, '*');\n });\n document.getElementById('dappportalsdk_cancelBtn').addEventListener('click', function() {\n window.parent.postMessage({event: 'Canceled'}, '*');\n });\n document.getElementById('dappportalsdk_approveBtn').addEventListener('click', function() {\n window.parent.postMessage({event: 'Clicked'}, '*');\n });\n }\n <\/script>\n <div class=${t?"bottomSheetContainer":"dialogContainer"}>\n <div class="headerContainer">\n <img id="dappportalsdk_closeBtn" class="closeBtn" src="https://static.kaiawallet.io/assets/sdk/ic_close.svg" alt="img"/>\n </div>\n <img class="imageCheck" src=${n} alt="tokenImage"/>\n <div class="title" id="dappportalsdk_title"/></div>\n <div class="desc" id="dappportalsdk_desc"/></div>\n <div class="flexBox"></div>\n ${e===V.ERC20_APPROVE?p:"\n <div>\n </div>\n "}\n <div class="buttonWrapper">\n <div id="dappportalsdk_cancelBtn" class="cancelBtn"></div>\n <div id="dappportalsdk_approveBtn" class="approveBtn"></div>\n </div>\n </div>\n </body>\n </html>\n `},ne={ko:{payment_approve_permission_popup_title:"접근권한 승인",payment_approve_permission_popup_desc:"결제를 위해 최초 1회의<br/>접근 권한 승인이 필요합니다.",payment_approve_permission_popup_token_name:"토큰 이름",payment_approve_permission_popup_token_usdt:"USDT",payment_approve_permission_popup_scope:"권한 범위",payment_approve_permission_popup_scope_desc:"approve",payment_approve_permission_popup_cancel:"취소",payment_approve_permission_popup_submit:"승인"},en:{payment_approve_permission_popup_title:"Grant access permission",payment_approve_permission_popup_desc:"Access permission must be<br/>granted once for the payment.",payment_approve_permission_popup_token_name:"Token Name",payment_approve_permission_popup_token_usdt:"USDT",payment_approve_permission_popup_scope:"Scope of permission",payment_approve_permission_popup_scope_desc:"approve",payment_approve_permission_popup_cancel:"Cancel",payment_approve_permission_popup_submit:"Grant"},ja:{payment_approve_permission_popup_title:"アクセス権限の許可",payment_approve_permission_popup_desc:"決済のために、初回のみ<br/>アクセス権限の許可が必要です。",payment_approve_permission_popup_token_name:"トークン名",payment_approve_permission_popup_token_usdt:"USDT",payment_approve_permission_popup_scope:"アクセス範囲",payment_approve_permission_popup_scope_desc:"approve",payment_approve_permission_popup_cancel:"キャンセル",payment_approve_permission_popup_submit:"許可"},zh:{payment_approve_permission_popup_title:"同意存取權限",payment_approve_permission_popup_desc:"首次付款需取得<br/>存取權限同意。",payment_approve_permission_popup_token_name:"代幣名稱",payment_approve_permission_popup_token_usdt:"USDT",payment_approve_permission_popup_scope:"權限範圍",payment_approve_permission_popup_scope_desc:"approve",payment_approve_permission_popup_cancel:"取消",payment_approve_permission_popup_submit:"同意"},th:{payment_approve_permission_popup_title:"อนุญาตสิทธิ์การเข้าถึง",payment_approve_permission_popup_desc:"จำเป็นต้องให้สิทธิ์การเข้าถึง<br/>เพียงครั้งเดียวสำหรับการชำระเงิน",payment_approve_permission_popup_token_name:"ชื่อโทเค็น",payment_approve_permission_popup_token_usdt:"USDT",payment_approve_permission_popup_scope:"ขอบเขตการอนุญาต",payment_approve_permission_popup_scope_desc:"approve",payment_approve_permission_popup_cancel:"ยกเลิก",payment_approve_permission_popup_submit:"ให้สิทธิ์"}},ie={ko:{payment_proceed_payment_popup_title:"접근권한 승인 완료",payment_proceed_payment_popup_desc:"접근권한 승인이 완료되었습니다. 아래 버튼을 눌러 결제를 진행하세요.",payment_proceed_payment_popup_cancel:"취소",payment_proceed_payment_popup_submit:"결제"},en:{payment_proceed_payment_popup_title:"Access permission granted",payment_proceed_payment_popup_desc:"Access permission has been granted. Press the button below to proceed with payment.",payment_proceed_payment_popup_cancel:"Cancel",payment_proceed_payment_popup_submit:"Pay"},ja:{payment_proceed_payment_popup_title:"アクセス許可済み",payment_proceed_payment_popup_desc:"アクセス権限が許可されました。下のボタンを押して、決済へお進みください。",payment_proceed_payment_popup_cancel:"キャンセル",payment_proceed_payment_popup_submit:"決済"},zh:{payment_proceed_payment_popup_title:"已同意存取權限",payment_proceed_payment_popup_desc:"已同意存取權限,請點選下方按鈕進行付款。",payment_proceed_payment_popup_cancel:"取消",payment_proceed_payment_popup_submit:"付款"},th:{payment_proceed_payment_popup_title:"ได้รับสิทธิ์การเข้าถึงแล้ว",payment_proceed_payment_popup_desc:"สิทธิ์การเข้าถึงได้ถูกอนุมัติแล้ว กดปุ่มด้านล่างเพื่อดำเนินการชำระเงิน",payment_proceed_payment_popup_cancel:"ยกเลิก",payment_proceed_payment_popup_submit:"ชำระเงิน"}};function se(e,t,n){const i=document.createElement("div");i.style.display="flex",i.style.justifyContent="center",i.style.alignItems="center",i.style.position="fixed",i.style.top="0",i.style.left="0",i.style.width="100vw",i.style.height="100dvh",i.style.backgroundColor="rgba(0, 0, 0, 0.35)",i.style.zIndex="9999999",i.id="iframeDiv";const s=document.createElement("iframe");s.srcdoc=e,s.width=t,s.height=n,s.style.border="none",s.style.zIndex="99999999";const a=i.appendChild(s);return document.body.appendChild(i),a}function ae(){const e=document.getElementById("iframeDiv");e?.remove()}function oe(e,t){return`iframeDiv-${t.toString().toLowerCase()}-${e}`}function re(e){return`iframeDiv-${e}`}class ce{constructor(e){this.resolvePromise=()=>{},this.rejectPromise=()=>{},this.config=e}setupListeners(e){let t,n;switch(e){case V.POPUP_BLOCKED:case V.PAYMENT_HISTORY_SIGN:case V.PAYMENT_HISTORY_OPEN:case V.ERC20_APPROVE:case V.ERC20_APPROVE_RESULT:t="Clicked",n="Canceled";break;default:throw new Error("[Internal Error] Unsupported ClickEventEmitterUiType: "+e)}const i=i=>{const s=this.iframe;if(!s)return;if(i.source!==s.contentWindow)return;const a=i.data;if(!a||"object"!=typeof a)return;const o=a.event;if(o)switch(o){case t:this.resolvePromise&&this.resolvePromise();break;case n:this.rejectPromise&&(this.rejectPromise(new Error("User canceled")),this.reset());break;default:throw new Error("[Internal Error] Unsupported ClickEventEmitterUiType: "+e)}};window.addEventListener("message",i),this.eventListener=i}clearListeners(){const e=this.eventListener;e&&(window.removeEventListener("message",e),this.eventListener=void 0)}reset(){this.resolvePromise=void 0,this.rejectPromise=void 0,ae(),this.clearListeners()}async awaitUserClick(e,t={}){const n=g(navigator.userAgent),i=new Promise(((e,t)=>{this.resolvePromise=e,this.rejectPromise=t}));switch(this.setupListeners(e),e){case V.POPUP_BLOCKED:case V.PAYMENT_HISTORY_SIGN:case V.PAYMENT_HISTORY_OPEN:this.iframe=se((e=>{if(e===V.ERC20_APPROVE||e===V.ERC20_APPROVE_RESULT)throw new Error(`[Internal Error] - ${e} is not supported in this context.`);const t=e===V.POPUP_BLOCKED?H:e===V.PAYMENT_HISTORY_SIGN?Y:B,n=e===V.PAYMENT_HISTORY_SIGN?"payment_reqeust_verification_title":e===V.PAYMENT_HISTORY_OPEN?"payment_complete_verification_title":"wallet_block_popup_title",i=e===V.PAYMENT_HISTORY_SIGN?"payment_reqeust_verification_desc":e===V.PAYMENT_HISTORY_OPEN?"payment_complete_verification_desc":"wallet_block_popup_desc",s=e===V.PAYMENT_HISTORY_SIGN?"payment_reqeust_verification_submit":e===V.PAYMENT_HISTORY_OPEN?"payment_complete_verification_submit":"wallet_block_popup_submit",a=e===V.PAYMENT_HISTORY_SIGN?"payment_reqeust_verification_cancel":e===V.PAYMENT_HISTORY_OPEN?"payment_complete_verification_cancel":"wallet_block_popup_cancel";return`\n <!DOCTYPE html>\n <html lang="en">\n <head>\n <meta charset="UTF-8">\n <style>\n .popupContainer {\n background-color: white;\n display: flex;\n flex-direction: column;\n border-radius: 20px;\n padding: 30px 24px;\n align-items: center;\n }\n .title {\n font-size: 18px;\n font-weight: 700;\n text-align: center;\n }\n .desc {\n margin-top: 12px;\n font-size: 16px;\n font-weight: 400;\n text-align: center;\n }\n .btnContainer {\n display: flex;\n flex-direction: column;\n width: 100%;\n }\n .okBtn {\n background-color: #000000;\n color: #ffffff;\n font-size: 16px;\n font-weight: 700;\n padding: 15px 24px;\n cursor: pointer;\n border-radius: 100px;\n margin-top: 24px;\n border: none;\n width: 100%;\n }\n .cancelBtn {\n background-color: #FFFFFF;\n color: #7B7F81;\n font-size: 15px;\n font-weight: 700;\n cursor: pointer;\n border: none;\n margin-top: 15px;\n }\n </style>\n </head>\n <body>\n <script>\n const browserLang = window.navigator.language.slice(0, 2);\n const xlt = ${JSON.stringify(t)};\n const translations = xlt[browserLang] || xlt['en'];\n console.log(translations);\n \n window.onload = function() {\n document.getElementById('dappportalsdk_popup_title').innerHTML = translations.${n};\n document.getElementById('dappportalsdk_popup_desc').innerHTML = translations.${i};\n document.getElementById('dappportalsdk_popup_cancel').textContent = translations.${a};\n document.getElementById('dappportalsdk_popup_proceed').textContent = translations.${s};\n \n document.getElementById('dappportalsdk_popup_cancel').addEventListener('click', function() {\n window.parent.postMessage({event: 'Canceled'}, '*');\n });\n document.getElementById('dappportalsdk_popup_proceed').addEventListener('click', function() {\n window.parent.postMessage({event: 'Clicked'}, '*');\n });\n }\n <\/script>\n <div class="popupContainer">\n <div id="dappportalsdk_popup_title" class="title"></div>\n <div id="dappportalsdk_popup_desc" class="desc"></div>\n <div class="btnContainer">\n <button id="dappportalsdk_popup_proceed" class="okBtn"></button>\n <button id="dappportalsdk_popup_cancel" class="cancelBtn"></button>\n </div>\n </div>\n </body>\n </html>\n `})(e),"300px","400px");break;case V.ERC20_APPROVE:{const e=t.contractAddress,i=t.scope;if(!e||!i)throw new Error("[Internal Error] - Failed to get erc20 token info");const s=this.config.erc20Tokens[e];if(!s)throw new Error("[Internal Error] - Failed to get erc20 token info");const a=s.name,o=s.imageUrl;this.iframe=se(te(V.ERC20_APPROVE,n,"https://static.kaiawallet.io/assets/sdk/3d_circle_check_mint.png",a,o,i),n?"100%":"420px","100%");break}case V.ERC20_APPROVE_RESULT:this.iframe=se(te(V.ERC20_APPROVE_RESULT,n,"https://static.kaiawallet.io/assets/sdk/3d_check_blue.png",null,null,null),n?"100%":"420px","100%")}await i.catch((()=>{throw new b({code:I,message:"User canceled popup"})})),this.reset(),window.focus()}}function le(e){const t=(new TextEncoder).encode(e);let n="";for(const i of t)n+=i.toString(16).padStart(2,"0");return"0x"+n}function de(e,t){const n=new a(10).pow(t),i=new a(e).mul(n);return BigInt(i.toFixed(0))}class pe{constructor(e){this.getPayment=async e=>{const t=`${this.baseUrl}/api/payment-v1/payment/info`;try{return(await s.get(t,{params:{id:e},headers:{"Content-Type":"application/json"}})).data}catch(n){throw console.error("Fail to get payment info - ",n),n}},this.getCryptoApproveTxInfo=async e=>{const t=`${this.baseUrl}/api/payment-v1/payment/crypto/approveTx/${e}`;try{return(await s.get(t,{headers:{"Content-Type":"application/json"}})).data}catch(n){throw console.error("Fail to get crypto approve tx info - ",n),n}},this.waitForConfirm=async e=>{let t=1;for(;t<this.maxRetryCount;)try{switch((await this.getPaymentStatus(e)).status){case"CONFIRMED":case"FINALIZED":return;case"CANCELED":throw new b({code:-31001,message:"Payment is canceled"});case"CONFIRM_FAILED":throw new b({code:-31002,message:"Payment is failed"})}await this.delayPolling(1e3)}catch(n){if(n instanceof b)throw console.error(n),n}finally{t++}throw new Error("Fail to wait for response")},this.delayPolling=async e=>{await new Promise((t=>setTimeout(t,e)))},this.getPaymentStatus=async e=>{const t=`${this.baseUrl}/api/payment-v1/payment/status`;try{return(await s.get(t,{params:{id:e},headers:{"Content-Type":"application/json"}})).data}catch(n){throw console.error("Fail to get payment info - ",n),n}},this.register=async(e,t)=>{const n=`${this.baseUrl}/api/payment-v1/payment/register`,i={id:e,txHash:t};try{await s.post(n,i,{headers:{"Content-Type":"application/json"}})}catch(a){throw console.error("Fail to register - ",a),a}},this.cancelCryptoPayment=async e=>{const t=`${this.baseUrl}/api/payment-v1/payment/cancel/crypto/${e}`;try{return(await s.post(t,null,{headers:{"Content-Type":"application/json","x-client-id":this.clientId}})).data}catch(n){throw console.error("Fail to cancel crypto payment - ",n),n}},this.startPayment=async(e,t,n)=>{const i=`${this.baseUrl}/api/payment-v1/payment/start`;try{return(await s.post(i,{id:e,userAgent:t,isLiff:n},{headers:{"Content-Type":"application/json","X-Sdk-Version":"1.5.2"}})).data}catch(a){throw console.error("Fail to startPayment - ",a),a}},this.getNonceForPaymentHistory=async e=>{const t=`${this.baseUrl}/api/payment-v1/auth/nonce`;try{return(await s.get(t,{params:{walletAddress:e},headers:{"Content-Type":"application/json"}})).data}catch(n){throw console.error("Fail to get nonce for payment history - ",n),n}},this.getSessionTokenForPaymentHistory=async(e,t)=>{const n=`${this.baseUrl}/api/payment-v1/auth/verify`,i={walletAddress:e,signature:t,isLegacy:!1};try{return(await s.post(n,i,{headers:{"Content-Type":"application/json","x-client-id":this.clientId}})).data}catch(a){throw console.error("Fail to get session token for payment history - ",a),a}},this.signTransactionAsFeePayer=async(e,t)=>{const n=`${this.baseUrl}/api/payment-v1/payment/signTransactionAsFeePayer`,i={paymentId:e,userSignedRawTransaction:t};try{return(await s.post(n,i,{headers:{"Content-Type":"application/json"}})).data}catch(a){throw console.error("Fail to sign transaction as fee payer - ",a),a}},this.baseUrl=e.paymentServerConfig.baseUrl,this.maxRetryCount=e.relayServerConfig.maxRetryCount,this.clientId=e.clientId}}class he{constructor(e){this.paymentClient=new pe(e)}async getPayment(e){return await this.paymentClient.getPayment(e)}async getCryptoApproveTxInfo(e){return await this.paymentClient.getCryptoApproveTxInfo(e)}async cancelCryptoPayment(e){return await this.paymentClient.cancelCryptoPayment(e)}async startPayment(t){const n=navigator.userAgent,i=e.isInClient();return await this.paymentClient.startPayment(t,n,i)}async waitForConfirm(e){try{await this.paymentClient.waitForConfirm(e)}catch(t){throw M(t)}}async registerTxHash(e,t){await this.paymentClient.register(e,t)}openStripePaymentPage(e,t,n){return window.open(e,"stripePayment",`width=${t},height=${n}`)}async getNonceForPaymentHistory(e){return await this.paymentClient.getNonceForPaymentHistory(e)}async getSessionTokenForPaymentHistory(e,t){return await this.paymentClient.getSessionTokenForPaymentHistory(e,t)}async signTransactionAsFeePayer(e,t){return this.paymentClient.signTransactionAsFeePayer(e,t)}}class ue{constructor(e,t,n){this.config=e,this.walletProvider=t,this.handler=new he(e),this.clickEventListener=new ce(e),this.trackingService=n,this.chainNodeRpcClient=new J(e)}async startPayment(e){this.trackingService.sendWalletActivity("START_PAYMENT",{paymentId:e});const t=(await this.walletProvider.request({method:"kaia_accounts"}))[0];if(!t)throw new Error("Wallet is not connected");const n=await this.handler.getPayment(e),i=this.config.chainId,s=n.testMode;if(i===Q&&s)throw console.error("The payment is set to test mode, but DappPortalSDK is initialized with mainnet(8217)."),new Error("The payment is set to test mode, but DappPortalSDK is initialized with mainnet(8217).");if("1001"===i&&!s)throw console.error("The payment is set to non-test mode, but DappPortalSDK is initialized with testnet(1001)."),new Error("The payment is set to non-test mode, but DappPortalSDK is initialized with testnet(1001).");"CRYPTO"===n.pgType?await this.handleCryptoPayment(e,n,t):await this.handleStripePayment(e),await this.handler.waitForConfirm(e)}async handleCryptoPayment(e,t,n){const i=await this.walletProvider.request({method:"kaia_gasPrice",params:[]}),s="KAIA"!==t.currencyCode;let a,o,r=!1;const c=this.walletProvider.getWalletType();if(!c)throw new Error("Wallet type is not initialized");const l=(c===_.Web||c===_.Liff)&&"USDT"===t.currencyCode&&this.config.isUnifiDeployed;if(s){const s=await this.handler.getCryptoApproveTxInfo(e);await this.isApproveRequired(s.allowanceTxDTO,t.price,t.decimal)&&(await this.requestApprove(s.enableCryptoFeeDelegation,e,n,i,s.approveTxDTO),r=!0),l&&(o=s.approveTxDTO.to,a=de(t.price,t.decimal).toString())}const d=await this.handler.startPayment(e),p=d.paymentTxDTO,h=await this.requestPayment(d.enableCryptoFeeDelegation,s&&r,e,n,i,p,l,o,a);await this.handler.registerTxHash(e,h)}async handleStripePayment(t){const n=(await this.handler.startPayment(t)).redirectUrl;this.handler.openStripePaymentPage(n,this.config.stripePopupSize.width,this.config.stripePopupSize.height)||e.isInClient()||(await this.clickEventListener.awaitUserClick(V.POPUP_BLOCKED),this.handler.openStripePaymentPage(n,this.config.stripePopupSize.width,this.config.stripePopupSize.height))}async openPaymentHistory(){const e=(await this.walletProvider.request({method:"kaia_accounts"}))[0];if(!e)throw new Error("Wallet is not connected");const t=await this.walletProvider.getWalletType();if(!t)throw new Error("Wallet type is not initialized");const n=await this.handler.getNonceForPaymentHistory(e);await this.clickEventListener.awaitUserClick(V.PAYMENT_HISTORY_SIGN);const i=await this.issueSessionToken(n,e);await this.clickEventListener.awaitUserClick(V.PAYMENT_HISTORY_OPEN),await this.openPaymentHistoryPage(t,i),this.clickEventListener.reset()}async issueSessionToken(e,t){const n=le(e),i=await this.walletProvider.request({method:"personal_sign",params:[n,t]});return await this.handler.getSessionTokenForPaymentHistory(t,i)}async openPaymentHistoryPage(t,n){const i=`${this.getPaymentPageBaseUrl()}?sessionToken=${n}`;if(e.isInClient())return void await e.subWindow.open({url:i});const s=navigator.userAgent;if(m(s)&&y(s))return void(window.location.href=i);const a=this.config.webWalletConfig.popupSize;window.open(i,"payment",`width=${a.width},height=${a.height}`)}getPaymentPageBaseUrl(){return m(navigator.userAgent)?`${this.config.paymentHistoryPopupConfig.liffUrl}/payment-history`:`${this.config.paymentHistoryPopupConfig.webUrl}/payment-history`}async isApproveRequired(e,t,n){const i=await this.chainNodeRpcClient.requestRpc({method:"eth_call",params:[{to:e.to,input:e.input},"latest"]});return BigInt(i)<de(t,n)}async requestApprove(e,t,n,i,s){const a={typeInt:e?49:48,from:n,to:s.to,gasPrice:i,value:s.value,input:s.input},o=e?await this.estimateGasWithoutBalanceCheck(n,a):await this.estimateGas(a);try{return await this.clickEventListener.awaitUserClick(V.ERC20_APPROVE,{contractAddress:s.to,scope:"approve"}),e?await this.requestFeeDelegatedApprove(t,a,o):await this.requestNonFeeDelegatedApprove(a,o)}catch(r){throw r.code===I&&await this.handler.cancelCryptoPayment(t),r}}async requestNonFeeDelegatedApprove(e,t){return this.walletProvider.request({method:"kaia_sendTransaction",params:[{...e,gas:t}]})}async requestFeeDelegatedApprove(e,t,n){const i=await this.walletProvider.request({method:"kaia_signTransaction",params:[{...t,gas:`0x${(BigInt(n)*BigInt(2)).toString(16)}`}]});return this.handler.signTransactionAsFeePayer(e,i.raw)}async requestPayment(e,t,n,i,s,a,o,r,c){return o&&this.isAllowedByUnifi()?e?this.requestUnifiFeeDelegatedTransaction(t,i,s,a,r,c):this.requestUnifiNonFeeDelegatedTransaction(t,i,s,a,r,c):e?this.requestFeeDelegatedTransaction(t,n,i,s,a):this.requestNonFeeDelegatedTransaction(t,i,s,a)}async requestNonFeeDelegatedTransaction(e,t,n,i){const s={typeInt:48,from:t,to:i.to,gasPrice:n,value:i.value,input:i.input},a=await this.estimateGas(s);return e&&await this.clickEventListener.awaitUserClick(V.ERC20_APPROVE_RESULT),this.walletProvider.request({method:"kaia_sendTransaction",params:[{...s,gas:a}]})}async requestFeeDelegatedTransaction(e,t,n,i,s){const a={typeInt:49,from:n,to:s.to,gasPrice:i,value:s.value,input:s.input},o=await this.estimateGasWithoutBalanceCheck(n,a);e&&await this.clickEventListener.awaitUserClick(V.ERC20_APPROVE_RESULT);const r=await this.walletProvider.request({method:"kaia_signTransaction",params:[{...a,gas:`0x${(BigInt(o)*BigInt(2)).toString(16)}`}]});return(await this.handler.signTransactionAsFeePayer(t,r.raw)).txHash}async requestUnifiFeeDelegatedTransaction(e,t,n,i,s,a){const o={typeInt:49,from:t,to:i.to,gasPrice:n,value:i.value,input:i.input};return e&&await this.clickEventListener.awaitUserClick(V.ERC20_APPROVE_RESULT),await this.walletProvider.request({method:"kaia_sendTransaction",params:[{...o,gas:`0x${BigInt(this.config.unifiPaymentDefaultGasLimit).toString(16)}`,depositTokenAddress:s,depositAmount:a}]})}async requestUnifiNonFeeDelegatedTransaction(e,t,n,i,s,a){const o={typeInt:48,from:t,to:i.to,gasPrice:n,value:i.value,input:i.input};return e&&await this.clickEventListener.awaitUserClick(V.ERC20_APPROVE_RESULT),this.walletProvider.request({method:"kaia_sendTransaction",params:[{...o,gas:`0x${BigInt(this.config.unifiPaymentDefaultGasLimit).toString(16)}`,depositTokenAddress:s,depositAmount:a}]})}async estimateGas(e){return this.walletProvider.request({method:"kaia_estimateGas",params:[e]})}async estimateGasWithoutBalanceCheck(e,t){const n=[t,"latest",{[e]:{balance:"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}}];return this.walletProvider.request({method:"kaia_estimateGas",params:n})}isAllowedByUnifi(){return this.config.chainId===Q||!1}}class me{constructor(e){this.getErc20BalanceWithDeposited=async(e,t,n,i)=>{const a=`${this.baseUrl}/api/v1/balance/erc20/with-deposited-balance`,o={walletType:e,walletAddress:t,contractAddress:n,chainId:i};try{return(await s.get(a,{params:o,headers:{"Content-Type":"application/json"}})).data}catch(r){throw console.error("Fail to get ERC20 balance with deposited balance - ",r),r}},this.baseUrl=e}}class ye{constructor(e){this.fetchClientInfo=async()=>{const e=`${this.baseUrl}/api/v1/client-info`;try{return(await s.get(e,{headers:{"x-client-id":this.clientId}})).data}catch(t){throw console.error("Fail to get client info - ",t),t}},this.clientId=e.clientId,this.baseUrl=e.relayServerConfig.baseUrl}}function ge(e,t,n){var i=function(e){return atob(e)}(e),s=i.indexOf("\n",10)+1,a=i.substring(s)+"",o=new Blob([a],{type:"application/javascript"});return URL.createObjectURL(o)}function we(e,t,n){var i;return function(t){return i=i||ge(e),new Worker(i,t)}}var fe=we("Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwohZnVuY3Rpb24oKXsidXNlIHN0cmljdCI7ZnVuY3Rpb24gdCh0KXtjb25zdCBpPW5ldyBBcnJheUJ1ZmZlcig4KTtyZXR1cm4gbmV3IERhdGFWaWV3KGkpLnNldEJpZ1VpbnQ2NCgwLHQpLG5ldyBVaW50OEFycmF5KGkpfWZ1bmN0aW9uIGkodCxpKXtpZih0Lmxlbmd0aCE9PWkubGVuZ3RoKXJldHVybiExO2ZvcihsZXQgcj0wO3I8dC5sZW5ndGg7cisrKWlmKHRbcl0hPT1pW3JdKXJldHVybiExO3JldHVybiEwfWZ1bmN0aW9uIHIodCl7Y29uc3QgaT1hdG9iKHQpLHI9aS5sZW5ndGgsZT1uZXcgVWludDhBcnJheShyKTtmb3IobGV0IGg9MDtoPHI7aCsrKWVbaF09aS5jaGFyQ29kZUF0KGgpO3JldHVybiBlfXNlbGYub25tZXNzYWdlPWFzeW5jIGZ1bmN0aW9uKGUpe2NvbnN0e2NoYWxsZW5nZXM6aH09ZS5kYXRhLHM9QmlnSW50KDB4MTAwMDAwMDAwMDAwMDAwMDApLG49W10sbz1EYXRlLm5vdygpO2Zvcihjb25zdCBmIG9mIGgpe2lmKCFmLnNhbHR8fCFmLnRhcmdldCl0aHJvdyBuZXcgRXJyb3IoIkludmFsaWQgY2hhbGxlbmdlIHN0cnVjdHVyZSIpO2NvbnN0IGU9cihmLnNhbHQpLGg9cihmLnRhcmdldCk7Zm9yKGxldCByPUJpZ0ludCgwKTtyPHM7cisrKXtjb25zdCBzPW5ldyBVaW50OEFycmF5KFsuLi5lLC4uLnQocildKTtpZihpKHNoYTI1Ni5jcmVhdGUoKS51cGRhdGUocykuYXJyYXkoKSxoKSl7bi5wdXNoKHtpbmRleDpmLl9pbmRleCxhbnN3ZXI6cn0pO2JyZWFrfX19Y29uc3QgYT1EYXRlLm5vdygpLW87c2VsZi5wb3N0TWVzc2FnZSh7cmVzdWx0czpuLHRvdGFsRXhlY3V0aW9uVGltZTphfSl9LGZ1bmN0aW9uKCl7dmFyIHQ9ImlucHV0IGlzIGludmFsaWQgdHlwZSIsaT0ib2JqZWN0Ij09dHlwZW9mIHdpbmRvdyxyPWk/d2luZG93Ont9O3IuSlNfU0hBMjU2X05PX1dJTkRPVyYmKGk9ITEpO3ZhciBlPSFpJiYib2JqZWN0Ij09dHlwZW9mIHNlbGYsaD0hci5KU19TSEEyNTZfTk9fTk9ERV9KUyYmIm9iamVjdCI9PXR5cGVvZiBwcm9jZXNzJiZwcm9jZXNzLnZlcnNpb25zJiZwcm9jZXNzLnZlcnNpb25zLm5vZGU7aD9yPWdsb2JhbDplJiYocj1zZWxmKTt2YXIgcz0hci5KU19TSEEyNTZfTk9fQ09NTU9OX0pTJiYib2JqZWN0Ij09dHlwZW9mIG1vZHVsZSYmbW9kdWxlLmV4cG9ydHMsbj0iZnVuY3Rpb24iPT10eXBlb2YgZGVmaW5lJiZkZWZpbmUuYW1kLG89IXIuSlNfU0hBMjU2X05PX0FSUkFZX0JVRkZFUiYmInVuZGVmaW5lZCIhPXR5cGVvZiBBcnJheUJ1ZmZlcixhPSIwMTIzNDU2Nzg5YWJjZGVmIi5zcGxpdCgiIiksZj1bLTIxNDc0ODM2NDgsODM4ODYwOCwzMjc2OCwxMjhdLHU9WzI0LDE2LDgsMF0sYz1bMTExNjM1MjQwOCwxODk5NDQ3NDQxLDMwNDkzMjM0NzEsMzkyMTAwOTU3Myw5NjE5ODcxNjMsMTUwODk3MDk5MywyNDUzNjM1NzQ4LDI4NzA3NjMyMjEsMzYyNDM4MTA4MCwzMTA1OTg0MDEsNjA3MjI1Mjc4LDE0MjY4ODE5ODcsMTkyNTA3ODM4OCwyMTYyMDc4MjA2LDI2MTQ4ODgxMDMsMzI0ODIyMjU4MCwzODM1MzkwNDAxLDQwMjIyMjQ3NzQsMjY0MzQ3MDc4LDYwNDgwNzYyOCw3NzAyNTU5ODMsMTI0OTE1MDEyMiwxNTU1MDgxNjkyLDE5OTYwNjQ5ODYsMjU1NDIyMDg4MiwyODIxODM0MzQ5LDI5NTI5OTY4MDgsMzIxMDMxMzY3MSwzMzM2NTcxODkxLDM1ODQ1Mjg3MTEsMTEzOTI2OTkzLDMzODI0MTg5NSw2NjYzMDcyMDUsNzczNTI5OTEyLDEyOTQ3NTczNzIsMTM5NjE4MjI5MSwxNjk1MTgzNzAwLDE5ODY2NjEwNTEsMjE3NzAyNjM1MCwyNDU2OTU2MDM3LDI3MzA0ODU5MjEsMjgyMDMwMjQxMSwzMjU5NzMwODAwLDMzNDU3NjQ3NzEsMzUxNjA2NTgxNywzNjAwMzUyODA0LDQwOTQ1NzE5MDksMjc1NDIzMzQ0LDQzMDIyNzczNCw1MDY5NDg2MTYsNjU5MDYwNTU2LDg4Mzk5Nzg3Nyw5NTgxMzk1NzEsMTMyMjgyMjIxOCwxNTM3MDAyMDYzLDE3NDc4NzM3NzksMTk1NTU2MjIyMiwyMDI0MTA0ODE1LDIyMjc3MzA0NTIsMjM2MTg1MjQyNCwyNDI4NDM2NDc0LDI3NTY3MzQxODcsMzIwNDAzMTQ3OSwzMzI5MzI1Mjk4XSx5PVsiaGV4IiwiYXJyYXkiLCJkaWdlc3QiLCJhcnJheUJ1ZmZlciJdLGw9W107IXIuSlNfU0hBMjU2X05PX05PREVfSlMmJkFycmF5LmlzQXJyYXl8fChBcnJheS5pc0FycmF5PWZ1bmN0aW9uKHQpe3JldHVybiJbb2JqZWN0IEFycmF5XSI9PT1PYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwodCl9KSwhb3x8IXIuSlNfU0hBMjU2X05PX0FSUkFZX0JVRkZFUl9JU19WSUVXJiZBcnJheUJ1ZmZlci5pc1ZpZXd8fChBcnJheUJ1ZmZlci5pc1ZpZXc9ZnVuY3Rpb24odCl7cmV0dXJuIm9iamVjdCI9PXR5cGVvZiB0JiZ0LmJ1ZmZlciYmdC5idWZmZXIuY29uc3RydWN0b3I9PT1BcnJheUJ1ZmZlcn0pO3ZhciBwPWZ1bmN0aW9uKHQsaSl7cmV0dXJuIGZ1bmN0aW9uKHIpe3JldHVybiBuZXcgZyhpLCEwKS51cGRhdGUocilbdF0oKX19LGQ9ZnVuY3Rpb24odCl7dmFyIGk9cCgiaGV4Iix0KTtoJiYoaT13KGksdCkpLGkuY3JlYXRlPWZ1bmN0aW9uKCl7cmV0dXJuIG5ldyBnKHQpfSxpLnVwZGF0ZT1mdW5jdGlvbih0KXtyZXR1cm4gaS5jcmVhdGUoKS51cGRhdGUodCl9O2Zvcih2YXIgcj0wO3I8eS5sZW5ndGg7KytyKXt2YXIgZT15W3JdO2lbZV09cChlLHQpfXJldHVybiBpfSx3PWZ1bmN0aW9uKGkscil7dmFyIGU9cmVxdWlyZSgiY3J5cHRvIiksaD1yPyJzaGEyMjQiOiJzaGEyNTYiO3JldHVybiBmdW5jdGlvbihyKXtpZigic3RyaW5nIj09dHlwZW9mIHIpcmV0dXJuIGUuY3JlYXRlSGFzaChoKS51cGRhdGUociwidXRmOCIpLmRpZ2VzdCgiaGV4Iik7aWYobnVsbD09cil0aHJvdyBuZXcgRXJyb3IodCk7cmV0dXJuIHIuY29uc3RydWN0b3I9PT1BcnJheUJ1ZmZlciYmKHI9bmV3IFVpbnQ4QXJyYXkocikpLEFycmF5LmlzQXJyYXkocil8fEFycmF5QnVmZmVyLmlzVmlldyhyKXx8ci5jb25zdHJ1Y3Rvcj09PUJ1ZmZlcj9lLmNyZWF0ZUhhc2goaCkudXBkYXRlKChuZXcgVGV4dEVuY29kZXIpLmVuY29kZShyKSkuZGlnZXN0KCJoZXgiKTppKHIpfX0sQT1mdW5jdGlvbih0LGkpe3JldHVybiBmdW5jdGlvbihyLGUpe3JldHVybiBuZXcgdihyLGksITApLnVwZGF0ZShlKVt0XSgpfX0sYj1mdW5jdGlvbih0KXt2YXIgaT1BKCJoZXgiLHQpO2kuY3JlYXRlPWZ1bmN0aW9uKGkpe3JldHVybiBuZXcgdihpLHQpfSxpLnVwZGF0ZT1mdW5jdGlvbih0LHIpe3JldHVybiBpLmNyZWF0ZSh0KS51cGRhdGUocil9O2Zvcih2YXIgcj0wO3I8eS5sZW5ndGg7KytyKXt2YXIgZT15W3JdO2lbZV09QShlLHQpfXJldHVybiBpfTtmdW5jdGlvbiBnKHQsaSl7aT8obFswXT1sWzE2XT1sWzFdPWxbMl09bFszXT1sWzRdPWxbNV09bFs2XT1sWzddPWxbOF09bFs5XT1sWzEwXT1sWzExXT1sWzEyXT1sWzEzXT1sWzE0XT1sWzE1XT0wLHRoaXMuYmxvY2tzPWwpOnRoaXMuYmxvY2tzPVswLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDAsMCwwLDBdLHQ/KHRoaXMuaDA9MzIzODM3MTAzMix0aGlzLmgxPTkxNDE1MDY2Myx0aGlzLmgyPTgxMjcwMjk5OSx0aGlzLmgzPTQxNDQ5MTI2OTcsdGhpcy5oND00MjkwNzc1ODU3LHRoaXMuaDU9MTc1MDYwMzAyNSx0aGlzLmg2PTE2OTQwNzY4MzksdGhpcy5oNz0zMjA0MDc1NDI4KToodGhpcy5oMD0xNzc5MDMzNzAzLHRoaXMuaDE9MzE0NDEzNDI3Nyx0aGlzLmgyPTEwMTM5MDQyNDIsdGhpcy5oMz0yNzczNDgwNzYyLHRoaXMuaDQ9MTM1OTg5MzExOSx0aGlzLmg1PTI2MDA4MjI5MjQsdGhpcy5oNj01Mjg3MzQ2MzUsdGhpcy5oNz0xNTQxNDU5MjI1KSx0aGlzLmJsb2NrPXRoaXMuc3RhcnQ9dGhpcy5ieXRlcz10aGlzLmhCeXRlcz0wLHRoaXMuZmluYWxpemVkPXRoaXMuaGFzaGVkPSExLHRoaXMuZmlyc3Q9ITAsdGhpcy5pczIyND10fWZ1bmN0aW9uIHYoaSxyLGUpe3ZhciBoLHM9dHlwZW9mIGk7aWYoInN0cmluZyI9PT1zKXt2YXIgbixhPVtdLGY9aS5sZW5ndGgsdT0wO2ZvcihoPTA7aDxmOysraCkobj1pLmNoYXJDb2RlQXQoaCkpPDEyOD9hW3UrK109bjpuPDIwNDg/KGFbdSsrXT0xOTJ8bj4+PjYsYVt1KytdPTEyOHw2MyZuKTpuPDU1Mjk2fHxuPj01NzM0ND8oYVt1KytdPTIyNHxuPj4+MTIsYVt1KytdPTEyOHxuPj4+NiY2MyxhW3UrK109MTI4fDYzJm4pOihuPTY1NTM2KygoMTAyMyZuKTw8MTB8MTAyMyZpLmNoYXJDb2RlQXQoKytoKSksYVt1KytdPTI0MHxuPj4+MTgsYVt1KytdPTEyOHxuPj4+MTImNjMsYVt1KytdPTEyOHxuPj4+NiY2MyxhW3UrK109MTI4fDYzJm4pO2k9YX1lbHNle2lmKCJvYmplY3QiIT09cyl0aHJvdyBuZXcgRXJyb3IodCk7aWYobnVsbD09PWkpdGhyb3cgbmV3IEVycm9yKHQpO2lmKG8mJmkuY29uc3RydWN0b3I9PT1BcnJheUJ1ZmZlcilpPW5ldyBVaW50OEFycmF5KGkpO2Vsc2UgaWYoIShBcnJheS5pc0FycmF5KGkpfHxvJiZBcnJheUJ1ZmZlci5pc1ZpZXcoaSkpKXRocm93IG5ldyBFcnJvcih0KX1pLmxlbmd0aD42NCYmKGk9bmV3IGcociwhMCkudXBkYXRlKGkpLmFycmF5KCkpO3ZhciBjPVtdLHk9W107Zm9yKGg9MDtoPDY0OysraCl7dmFyIGw9aVtoXXx8MDtjW2hdPTkyXmwseVtoXT01NF5sfWcuY2FsbCh0aGlzLHIsZSksdGhpcy51cGRhdGUoeSksdGhpcy5vS2V5UGFkPWMsdGhpcy5pbm5lcj0hMCx0aGlzLnNoYXJlZE1lbW9yeT1lfWcucHJvdG90eXBlLnVwZGF0ZT1mdW5jdGlvbihpKXtpZighdGhpcy5maW5hbGl6ZWQpe3ZhciByLGU9dHlwZW9mIGk7aWYoInN0cmluZyIhPT1lKXtpZigib2JqZWN0IiE9PWUpdGhyb3cgbmV3IEVycm9yKHQpO2lmKG51bGw9PT1pKXRocm93IG5ldyBFcnJvcih0KTtpZihvJiZpLmNvbnN0cnVjdG9yPT09QXJyYXlCdWZmZXIpaT1uZXcgVWludDhBcnJheShpKTtlbHNlIGlmKCEoQXJyYXkuaXNBcnJheShpKXx8byYmQXJyYXlCdWZmZXIuaXNWaWV3KGkpKSl0aHJvdyBuZXcgRXJyb3IodCk7cj0hMH1mb3IodmFyIGgscyxuPTAsYT1pLmxlbmd0aCxmPXRoaXMuYmxvY2tzO248YTspe2lmKHRoaXMuaGFzaGVkJiYodGhpcy5oYXNoZWQ9ITEsZlswXT10aGlzLmJsb2NrLHRoaXMuYmxvY2s9ZlsxNl09ZlsxXT1mWzJdPWZbM109Zls0XT1mWzVdPWZbNl09Zls3XT1mWzhdPWZbOV09ZlsxMF09ZlsxMV09ZlsxMl09ZlsxM109ZlsxNF09ZlsxNV09MCkscilmb3Iocz10aGlzLnN0YXJ0O248YSYmczw2NDsrK24pZltzPj4+Ml18PWlbbl08PHVbMyZzKytdO2Vsc2UgZm9yKHM9dGhpcy5zdGFydDtuPGEmJnM8NjQ7KytuKShoPWkuY2hhckNvZGVBdChuKSk8MTI4P2Zbcz4+PjJdfD1oPDx1WzMmcysrXTpoPDIwNDg/KGZbcz4+PjJdfD0oMTkyfGg+Pj42KTw8dVszJnMrK10sZltzPj4+Ml18PSgxMjh8NjMmaCk8PHVbMyZzKytdKTpoPDU1Mjk2fHxoPj01NzM0ND8oZltzPj4+Ml18PSgyMjR8aD4+PjEyKTw8dVszJnMrK10sZltzPj4+Ml18PSgxMjh8aD4+PjYmNjMpPDx1WzMmcysrXSxmW3M+Pj4yXXw9KDEyOHw2MyZoKTw8dVszJnMrK10pOihoPTY1NTM2KygoMTAyMyZoKTw8MTB8MTAyMyZpLmNoYXJDb2RlQXQoKytuKSksZltzPj4+Ml18PSgyNDB8aD4+PjE4KTw8dVszJnMrK10sZltzPj4+Ml18PSgxMjh8aD4+PjEyJjYzKTw8dVszJnMrK10sZltzPj4+Ml18PSgxMjh8aD4+PjYmNjMpPDx1WzMmcysrXSxmW3M+Pj4yXXw9KDEyOHw2MyZoKTw8dVszJnMrK10pO3RoaXMubGFzdEJ5dGVJbmRleD1zLHRoaXMuYnl0ZXMrPXMtdGhpcy5zdGFydCxzPj02ND8odGhpcy5ibG9jaz1mWzE2XSx0aGlzLnN0YXJ0PXMtNjQsdGhpcy5oYXNoKCksdGhpcy5oYXNoZWQ9ITApOnRoaXMuc3RhcnQ9c31yZXR1cm4gdGhpcy5ieXRlcz40Mjk0OTY3Mjk1JiYodGhpcy5oQnl0ZXMrPXRoaXMuYnl0ZXMvNDI5NDk2NzI5NnwwLHRoaXMuYnl0ZXM9dGhpcy5ieXRlcyU0Mjk0OTY3Mjk2KSx0aGlzfX0sZy5wcm90b3R5cGUuZmluYWxpemU9ZnVuY3Rpb24oKXtpZighdGhpcy5maW5hbGl6ZWQpe3RoaXMuZmluYWxpemVkPSEwO3ZhciB0PXRoaXMuYmxvY2tzLGk9dGhpcy5sYXN0Qnl0ZUluZGV4O3RbMTZdPXRoaXMuYmxvY2ssdFtpPj4+Ml18PWZbMyZpXSx0aGlzLmJsb2NrPXRbMTZdLGk+PTU2JiYodGhpcy5oYXNoZWR8fHRoaXMuaGFzaCgpLHRbMF09dGhpcy5ibG9jayx0WzE2XT10WzFdPXRbMl09dFszXT10WzRdPXRbNV09dFs2XT10WzddPXRbOF09dFs5XT10WzEwXT10WzExXT10WzEyXT10WzEzXT10WzE0XT10WzE1XT0wKSx0WzE0XT10aGlzLmhCeXRlczw8M3x0aGlzLmJ5dGVzPj4+MjksdFsxNV09dGhpcy5ieXRlczw8Myx0aGlzLmhhc2goKX19LGcucHJvdG90eXBlLmhhc2g9ZnVuY3Rpb24oKXt2YXIgdCxpLHIsZSxoLHMsbixvLGEsZj10aGlzLmgwLHU9dGhpcy5oMSx5PXRoaXMuaDIsbD10aGlzLmgzLHA9dGhpcy5oNCxkPXRoaXMuaDUsdz10aGlzLmg2LEE9dGhpcy5oNyxiPXRoaXMuYmxvY2tzO2Zvcih0PTE2O3Q8NjQ7Kyt0KWk9KChoPWJbdC0xNV0pPj4+N3xoPDwyNSleKGg+Pj4xOHxoPDwxNCleaD4+PjMscj0oKGg9Ylt0LTJdKT4+PjE3fGg8PDE1KV4oaD4+PjE5fGg8PDEzKV5oPj4+MTAsYlt0XT1iW3QtMTZdK2krYlt0LTddK3J8MDtmb3IoYT11JnksdD0wO3Q8NjQ7dCs9NCl0aGlzLmZpcnN0Pyh0aGlzLmlzMjI0PyhzPTMwMDAzMixBPShoPWJbMF0tMTQxMzI1NzgxOSktMTUwMDU0NTk5fDAsbD1oKzI0MTc3MDc3fDApOihzPTcwNDc1MTEwOSxBPShoPWJbMF0tMjEwMjQ0MjQ4KS0xNTIxNDg2NTM0fDAsbD1oKzE0MzY5NDU2NXwwKSx0aGlzLmZpcnN0PSExKTooaT0oZj4+PjJ8Zjw8MzApXihmPj4+MTN8Zjw8MTkpXihmPj4+MjJ8Zjw8MTApLGU9KHM9ZiZ1KV5mJnleYSxBPWwrKGg9QSsocj0ocD4+PjZ8cDw8MjYpXihwPj4+MTF8cDw8MjEpXihwPj4+MjV8cDw8NykpKyhwJmRefnAmdykrY1t0XStiW3RdKXwwLGw9aCsoaStlKXwwKSxpPShsPj4+MnxsPDwzMCleKGw+Pj4xM3xsPDwxOSleKGw+Pj4yMnxsPDwxMCksZT0obj1sJmYpXmwmdV5zLHc9eSsoaD13KyhyPShBPj4+NnxBPDwyNileKEE+Pj4xMXxBPDwyMSleKEE+Pj4yNXxBPDw3KSkrKEEmcF5+QSZkKStjW3QrMV0rYlt0KzFdKXwwLGk9KCh5PWgrKGkrZSl8MCk+Pj4yfHk8PDMwKV4oeT4+PjEzfHk8PDE5KV4oeT4+PjIyfHk8PDEwKSxlPShvPXkmbCleeSZmXm4sZD11KyhoPWQrKHI9KHc+Pj42fHc8PDI2KV4odz4+PjExfHc8PDIxKV4odz4+PjI1fHc8PDcpKSsodyZBXn53JnApK2NbdCsyXStiW3QrMl0pfDAsaT0oKHU9aCsoaStlKXwwKT4+PjJ8dTw8MzApXih1Pj4+MTN8dTw8MTkpXih1Pj4+MjJ8dTw8MTApLGU9KGE9dSZ5KV51JmxebyxwPWYrKGg9cCsocj0oZD4+PjZ8ZDw8MjYpXihkPj4+MTF8ZDw8MjEpXihkPj4+MjV8ZDw8NykpKyhkJndefmQmQSkrY1t0KzNdK2JbdCszXSl8MCxmPWgrKGkrZSl8MCx0aGlzLmNocm9tZUJ1Z1dvcmtBcm91bmQ9ITA7dGhpcy5oMD10aGlzLmgwK2Z8MCx0aGlzLmgxPXRoaXMuaDErdXwwLHRoaX