UNPKG

@linenext/dapp-portal-sdk

Version:

Dapp Portal SDK

1 lines 121 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 I={"-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."}},E=-32001,k=-31001;class C extends Error{constructor(e,t,n){const i=Number.parseInt(e);if(isNaN(i))throw new Error('"code" must be an integer.');super(t??I[v.invalidRequest].message),this.code=i,n&&(this.data=n),Object.setPrototypeOf(this,C.prototype)}}class S extends C{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:E,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 C||e instanceof b?{code:e.code,message:e.message,data:e.data}:{code:Number.parseInt(v.internal),message:I[v.internal].message}}function x(e){return"object"==typeof e&&null!==e&&"number"==typeof e.code&&"string"==typeof e.message}function N(){return null!=globalThis.klaytn}function L(){const e=globalThis.klaytn;if(!e)throw new S("Extension is not installed");return e}function j(e,t){return"klay_signTransaction"!==e?t:function(e){const t=e;return{raw:t.rawTransaction}}(t)}var W,R;!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"}(R||(R={}));const A="walletType",U="selectedWalletAddress",D="deviceId",z="providerType",O="eventRead",X="eventMissionCompleted",G="noticeRead";class K{constructor(e){this.chainId=e}clear(){this.removeItem(A),this.removeItem(U)}setWalletType(e){this.setItem(A,e)}getWalletType(){return this.getItem(A)}setProviderType(e){this.setItem(z,e)}getProviderType(){return this.getItem(z)}setSelectedWallet(e){this.setItem(U,e)}getConnectedWallet(){return this.getItem(U)}getDeviceId(){return this.getItem(D)}setDeviceId(e){this.setItem(D,e)}getEventRead(e,t){const n=t.toString().toLowerCase(),i=`${O}:${n}:${e}`;return"true"===this.getItem(i)}setEventRead(e,t,n){const i=n.toString().toLowerCase(),s=`${O}:${i}:${e}`;this.setItem(s,t?"true":"false")}getNoticeRead(e){const t=`${G}:${e}`;return"true"===this.getItem(t)}setNoticeRead(e,t){const n=`${G}:${e}`;this.setItem(n,t?"true":"false")}getMissionCompleted(e){const t=`${X}:${e}`,n=this.getItem(t);if(n){const[e,t]=n.split(":");return[e,"true"===t]}return["",!1]}setMissionCompleted(e,t,n){const i=`${X}:${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 F{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 Z{constructor(e){this.eventClient=new F(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 V{constructor(e,t,n){this.config=e,this.walletProvider=t,this.internalEventProvider=n,this.sdkLocalStorage=new K(e.chainId),this.handler=new Z(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,R.MISSION),this.sdkLocalStorage.setMissionCompleted(e,!0,n[0]),await this.internalEventProvider.openEventBannerWhenSubMissionCompleted(e,n[0]))}}var Y;!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"}(Y||(Y={}));const B={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:"ดำเนินการ"}},H={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:"ดำเนินการ"}},q={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",$="8217",ee="reownVerified",te="bitgetExtensionInstalled",ne=(e,t,n,i,s,a)=>{if(e!==Y.ERC20_APPROVE&&e!==Y.ERC20_APPROVE_RESULT)throw new Error(`[Internal Error] - ${e} is not supported in this context.`);const o=e===Y.ERC20_APPROVE?ie:se,r=e===Y.ERC20_APPROVE?"payment_approve_permission_popup_title":"payment_proceed_payment_popup_title",c=e===Y.ERC20_APPROVE?"payment_approve_permission_popup_desc":"payment_proceed_payment_popup_desc",l=e===Y.ERC20_APPROVE?"payment_approve_permission_popup_submit":"payment_proceed_payment_popup_submit",d=e===Y.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===Y.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===Y.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 `},ie={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:"ให้สิทธิ์"}},se={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 ae(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 oe(){const e=document.getElementById("iframeDiv");e?.remove()}function re(e,t){return`iframeDiv-${t.toString().toLowerCase()}-${e}`}function ce(e){return`iframeDiv-${e}`}class le{constructor(e){this.resolvePromise=()=>{},this.rejectPromise=()=>{},this.config=e}setupListeners(e){let t,n;switch(e){case Y.POPUP_BLOCKED:case Y.PAYMENT_HISTORY_SIGN:case Y.PAYMENT_HISTORY_OPEN:case Y.ERC20_APPROVE:case Y.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,oe(),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 Y.POPUP_BLOCKED:case Y.PAYMENT_HISTORY_SIGN:case Y.PAYMENT_HISTORY_OPEN:this.iframe=ae((e=>{if(e===Y.ERC20_APPROVE||e===Y.ERC20_APPROVE_RESULT)throw new Error(`[Internal Error] - ${e} is not supported in this context.`);const t=e===Y.POPUP_BLOCKED?q:e===Y.PAYMENT_HISTORY_SIGN?B:H,n=e===Y.PAYMENT_HISTORY_SIGN?"payment_reqeust_verification_title":e===Y.PAYMENT_HISTORY_OPEN?"payment_complete_verification_title":"wallet_block_popup_title",i=e===Y.PAYMENT_HISTORY_SIGN?"payment_reqeust_verification_desc":e===Y.PAYMENT_HISTORY_OPEN?"payment_complete_verification_desc":"wallet_block_popup_desc",s=e===Y.PAYMENT_HISTORY_SIGN?"payment_reqeust_verification_submit":e===Y.PAYMENT_HISTORY_OPEN?"payment_complete_verification_submit":"wallet_block_popup_submit",a=e===Y.PAYMENT_HISTORY_SIGN?"payment_reqeust_verification_cancel":e===Y.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 body {\n font-family: 'SF Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n }\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 Y.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=ae(ne(Y.ERC20_APPROVE,n,"https://static.kaiawallet.io/assets/sdk/3d_circle_check_mint.png",a,o,i),n?"100%":"420px","100%");break}case Y.ERC20_APPROVE_RESULT:this.iframe=ae(ne(Y.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:E,message:"User canceled popup"})})),this.reset(),window.focus()}}function de(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 pe(e,t){const n=new a(10).pow(t),i=new a(e).mul(n);return BigInt(i.toFixed(0))}class he{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:k,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.cancelLineIapPayment=async e=>{const t=`${this.baseUrl}/api/payment-v1/payment/cancel/lineiap/${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 iap payment - ",n),n}},this.startPayment=async(e,t,n,i=null,a=null,o=null,r=null,c=null,l=null)=>{const d=`${this.baseUrl}/api/payment-v1/payment/start`;try{return(await s.post(d,{id:e,userAgent:t,isLiff:n,lineChannelId:i,lineUserAccessToken:a,lineIapProductId:o,lineIapPaymentPrice:r,lineIapPaymentCurrency:c,liffOs:l},{headers:{"Content-Type":"application/json","X-Sdk-Version":"1.6.0"}})).data}catch(p){throw console.error("Fail to startPayment - ",p),p}},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){if(s.isAxiosError(a)&&400===a.response?.status){const e=a.response.data;if(1012===e?.code)throw{code:-32009,message:"nonce too low"}}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 ue{constructor(e){this.paymentClient=new he(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 cancelLineIapPayment(e){return await this.paymentClient.cancelLineIapPayment(e)}async startPayment(t,n=null,i=null,s=null,a=null,o=null,r=null){const c=navigator.userAgent,l=e.isInClient();return await this.paymentClient.startPayment(t,c,l,n,i,s,a,o,r)}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 me{constructor(e,t,n){this.config=e,this.walletProvider=t,this.handler=new ue(e),this.clickEventListener=new le(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=this.walletProvider.getWalletType(),i=await this.handler.getPayment(e),s=this.config.chainId,a=i.testMode;if(s===$&&a)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"===s&&!a)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).");if(n===_.OKX&&a)throw console.error("The payment is set to test mode, but OKX wallet does not support testmode payment"),new Error("The payment is set to test mode, but OKX wallet does not support testmode payment");if("CRYPTO"===i.pgType)await this.handleCryptoPayment(e,i,t);else if("STRIPE"===i.pgType)await this.handleStripePayment(e);else{if("LINE_IAP"!==i.pgType)throw new Error(`Invalid pg_type : ${i.pgType}`);await this.handleLineIapPayment(e,i)}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=pe(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(Y.POPUP_BLOCKED),this.handler.openStripePaymentPage(n,this.config.stripePopupSize.width,this.config.stripePopupSize.height))}async handleLineIapPayment(t,n){if(!e.isApiAvailable("iap"))throw new b({code:-31003,message:"Can not process LINE_IAP payment"});const i=n.lineIapProductId;if(!i)throw new Error("lineIapProductId is not defined for LINE IAP payment");const s=(await e.iap.getPlatformProducts({productIds:[i]}))[i];if(!s)throw new Error(`Invalid productId for LINE IAP payment : ${i}`);const a=s.price.toString(),o=s.currency,r=e.getDecodedIDToken(),c=e.getAccessToken(),l=r?.aud,d=e.getOS();if(!l)throw new Error("Failed to get channelid from liff");if(!c)throw new Error("Failed to get accessToken from liff");if(!d)throw new Error("Failed to get os from liff");if("web"===d)throw new Error("InAppPurchase is not available on web environment");await e.iap.requestConsentAgreement();const p=(await this.handler.startPayment(t,l,c,i,a,o,d)).paymentIapInfoDTO;try{await e.iap.createPayment({productId:p?.productId,orderId:p?.orderId})}catch(h){if(function(e){return e&&"object"==typeof e&&"code"in e}(h)&&"CANCELED"===h.code)throw await this.handler.cancelLineIapPayment(t),new b({code:k,message:"Payment is canceled"});throw h}}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(Y.PAYMENT_HISTORY_SIGN);const i=await this.issueSessionToken(n,e);await this.clickEventListener.awaitUserClick(Y.PAYMENT_HISTORY_OPEN),await this.openPaymentHistoryPage(t,i),this.clickEventListener.reset()}async issueSessionToken(e,t){const n=de(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)<pe(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(Y.ERC20_APPROVE,{contractAddress:s.to,scope:"approve"}),e?await this.requestFeeDelegatedApprove(t,a,o):await this.requestNonFeeDelegatedApprove(a,o)}catch(r){throw r.code===E&&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(Y.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(Y.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(Y.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(Y.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===$||!1}}class ye{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 ge{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 we(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 fe(e,t,n){var i;return function(t){return i=i||we(e),new Worker(i,t)}}var _e=fe("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+Ml18PSgxMjh8