UNPKG

@gemini-wallet/core

Version:

Core SDK for Gemini Wallet integration with popup communication

835 lines (827 loc) 114 kB
// src/communicator.ts import { providerErrors, rpcErrors as rpcErrors2 } from "@metamask/rpc-errors"; // src/constants.ts import { arbitrum, arbitrumSepolia, base, baseSepolia, mainnet, optimism, optimismSepolia, polygon, polygonAmoy, sepolia } from "viem/chains"; var SDK_BACKEND_URL = "https://keys.gemini.com"; var ENS_API_URL = "https://horizon-api.gemini.com/api/ens"; var SDK_VERSION = "0.2.0"; var DEFAULT_CHAIN_ID = 42161; var MAINNET_CHAIN_IDS = { ARBITRUM_ONE: 42161, BASE: 8453, ETHEREUM: 1, OP_MAINNET: 10, POLYGON: 137 }; var TESTNET_CHAIN_IDS = { ARBITRUM_SEPOLIA: 421614, BASE_SEPOLIA: 84532, OP_SEPOLIA: 11155420, POLYGON_AMOY: 80002, SEPOLIA: 11155111 }; var SUPPORTED_CHAIN_IDS = [ ...Object.values(MAINNET_CHAIN_IDS), ...Object.values(TESTNET_CHAIN_IDS) ]; function getDefaultRpcUrl(chainId) { const chainMap = { [mainnet.id]: mainnet.rpcUrls.default.http[0], [arbitrum.id]: arbitrum.rpcUrls.default.http[0], [optimism.id]: optimism.rpcUrls.default.http[0], [base.id]: base.rpcUrls.default.http[0], [polygon.id]: polygon.rpcUrls.default.http[0], [sepolia.id]: sepolia.rpcUrls.default.http[0], [arbitrumSepolia.id]: arbitrumSepolia.rpcUrls.default.http[0], [optimismSepolia.id]: optimismSepolia.rpcUrls.default.http[0], [baseSepolia.id]: baseSepolia.rpcUrls.default.http[0], [polygonAmoy.id]: polygonAmoy.rpcUrls.default.http[0] }; return chainMap[chainId]; } var POPUP_WIDTH = 420; var POPUP_HEIGHT = 650; // src/types.ts import { EventEmitter } from "eventemitter3"; var GeminiSdkEvent; ((GeminiSdkEvent2) => { GeminiSdkEvent2["POPUP_LOADED"] = "POPUP_LOADED"; GeminiSdkEvent2["POPUP_UNLOADED"] = "POPUP_UNLOADED"; GeminiSdkEvent2["POPUP_APP_CONTEXT"] = "POPUP_APP_CONTEXT"; GeminiSdkEvent2["SDK_CONNECT"] = "SDK_CONNECT"; GeminiSdkEvent2["SDK_DISCONNECT"] = "SDK_DISCONNECT"; GeminiSdkEvent2["SDK_SEND_TRANSACTION"] = "SDK_SEND_TRANSACTION"; GeminiSdkEvent2["SDK_SIGN_DATA"] = "SDK_SIGN_DATA"; GeminiSdkEvent2["SDK_SIGN_TYPED_DATA"] = "SDK_SIGN_TYPED_DATA"; GeminiSdkEvent2["SDK_SWITCH_CHAIN"] = "SDK_SWITCH_CHAIN"; GeminiSdkEvent2["SDK_OPEN_SETTINGS"] = "SDK_OPEN_SETTINGS"; GeminiSdkEvent2["SDK_CURRENT_ACCOUNT"] = "SDK_CURRENT_ACCOUNT"; })(GeminiSdkEvent ||= {}); var PlatformType = { REACT_NATIVE: "REACT_NATIVE", WEB: "WEB" }; class ProviderEventEmitter extends EventEmitter { } // src/utils/base64.ts function encodeBase64(array) { let base64; if (typeof Buffer !== "undefined") { base64 = Buffer.from(array).toString("base64"); } else { base64 = btoa(Array.from(array).map((b) => String.fromCharCode(b)).join("")); } return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); } function decodeBase64(base64url) { let base64 = base64url.replace(/-/g, "+").replace(/_/g, "/"); while (base64.length % 4 !== 0) { base64 += "="; } if (typeof Buffer !== "undefined") { return new Uint8Array(Buffer.from(base64, "base64")); } else { const binaryString = atob(base64); const bytes = new Uint8Array(binaryString.length); for (let i = 0;i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return bytes; } } function bufferToBase64URLString(buffer) { const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer); return encodeBase64(bytes); } function utf8StringToBuffer(value) { if (typeof TextEncoder !== "undefined") { return new TextEncoder().encode(value); } else if (typeof Buffer !== "undefined") { return new Uint8Array(Buffer.from(value, "utf8")); } else { const bytes = new Uint8Array(value.length); for (let i = 0;i < value.length; i++) { bytes[i] = value.charCodeAt(i); } return bytes; } } function base64ToHex(base64) { const bytes = decodeBase64(base64); return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join(""); } // src/utils/calculateWalletAddress.ts import { encodeAbiParameters, encodeFunctionData, encodePacked, getCreate2Address, keccak256 } from "viem"; var CONTRACT_ADDRESSES = { ACCOUNT_IMPLEMENTATION: "0x0006050168DE255a8672ACaD4821e721CBA44337", ATTESTER: "0x000474392a9cd86a4687354f1Ce2964B52e97484", BOOTSTRAPPER: "0x00000000D3254452a909E4eeD47455Af7E27C289", FACTORY: "0x00E58DF70FaB983a324c4C068c82d20407579FaC", REGISTRY: "0x000000000069E2a187AEFFb852bF3cCdC95151B2", WEBAUTHN_VALIDATOR: "0xbA45a2BFb8De3D24cA9D7F1B551E14dFF5d690Fd" }; function calculateWalletAddress(params) { const { publicKey, credentialId, index = 0n } = params; if (!publicKey.startsWith("0x") || publicKey.length !== 130) { throw new Error("Invalid public key: must be 64-byte hex string (0x + 128 chars)"); } const pubKeyX = `0x${publicKey.slice(2, 66)}`; const pubKeyY = `0x${publicKey.slice(66, 130)}`; const webAuthnData = { pubKeyX: BigInt(pubKeyX), pubKeyY: BigInt(pubKeyY) }; if (!validateWebAuthnKey(webAuthnData)) { throw new Error("Invalid WebAuthn key: coordinates are not on secp256r1 curve"); } const authenticatorIdHash = generateAuthenticatorIdHash(credentialId); return calculateAddressInternal({ authenticatorIdHash, index, webAuthnData }); } function generateAuthenticatorIdHash(credentialId) { const padding = "=".repeat((4 - credentialId.length % 4) % 4); const base64 = credentialId.replace(/-/g, "+").replace(/_/g, "/") + padding; const binaryString = atob(base64); const bytes = new Uint8Array(binaryString.length); for (let i = 0;i < binaryString.length; i++) { bytes[i] = binaryString.charCodeAt(i); } return keccak256(bytes); } function validateWebAuthnKey(webAuthnData) { const SECP256R1_P = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffffn; const SECP256R1_B = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604bn; const { pubKeyX, pubKeyY } = webAuthnData; if (pubKeyX === 0n || pubKeyY === 0n || pubKeyX >= SECP256R1_P || pubKeyY >= SECP256R1_P) { return false; } const ySquared = pubKeyY * pubKeyY % SECP256R1_P; const xCubed = pubKeyX * pubKeyX * pubKeyX % SECP256R1_P; const threeX = 3n * pubKeyX % SECP256R1_P; const rightSide = (xCubed + SECP256R1_P - threeX + SECP256R1_B) % SECP256R1_P; return ySquared === rightSide; } function calculateAddressInternal(params) { const { webAuthnData, authenticatorIdHash, index } = params; const factoryAddress = CONTRACT_ADDRESSES.FACTORY; const accountImplementation = CONTRACT_ADDRESSES.ACCOUNT_IMPLEMENTATION; const webAuthnValidator = CONTRACT_ADDRESSES.WEBAUTHN_VALIDATOR; const attester = CONTRACT_ADDRESSES.ATTESTER; const bootstrapper = CONTRACT_ADDRESSES.BOOTSTRAPPER; const registry = CONTRACT_ADDRESSES.REGISTRY; const salt = keccak256(encodePacked(["uint256", "uint256", "bytes32", "uint256"], [webAuthnData.pubKeyX, webAuthnData.pubKeyY, authenticatorIdHash, index])); const validatorInitData = encodeAbiParameters([ { components: [ { name: "pubKeyX", type: "uint256" }, { name: "pubKeyY", type: "uint256" } ], type: "tuple" }, { type: "bytes32" } ], [webAuthnData, authenticatorIdHash]); const registryConfig = { attesters: [attester], registry, threshold: 1n }; const bootstrapCall = encodeFunctionData({ abi: [ { inputs: [ { name: "validator", type: "address" }, { name: "validatorInitData", type: "bytes" }, { components: [ { name: "registry", type: "address" }, { name: "attesters", type: "address[]" }, { name: "threshold", type: "uint8" } ], name: "registryConfig", type: "tuple" } ], name: "initNexusWithSingleValidator", type: "function" } ], args: [webAuthnValidator, validatorInitData, registryConfig], functionName: "initNexusWithSingleValidator" }); const initData = encodeAbiParameters([{ type: "address" }, { type: "bytes" }], [bootstrapper, bootstrapCall]); return predictProxyAddress(accountImplementation, salt, initData, factoryAddress); } function predictProxyAddress(implementation, salt, initData, deployer) { const initializeCall = encodeFunctionData({ abi: [ { inputs: [{ name: "data", type: "bytes" }], name: "initializeAccount", type: "function" } ], args: [initData], functionName: "initializeAccount" }); const constructorArgs = encodeAbiParameters([{ type: "address" }, { type: "bytes" }], [implementation, initializeCall]); const nexusProxyCreationCode = "0x60806040526102c8803803806100148161018c565b92833981016040828203126101885781516001600160a01b03811692909190838303610188576020810151906001600160401b03821161018857019281601f8501121561018857835161006e610069826101c5565b61018c565b9481865260208601936020838301011161018857815f926020809301865e8601015260017f90b772c2cb8a51aa7a8a65fc23543c6d022d5b3f8e2b92eed79fba7eef8293005d823b15610176577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b031916821790557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b5f80a282511561015e575f8091610146945190845af43d15610156573d91610137610069846101c5565b9283523d5f602085013e6101e0565b505b6040516089908161023f8239f35b6060916101e0565b50505034156101485763b398979f60e01b5f5260045ffd5b634c9c8ce360e01b5f5260045260245ffd5b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176101b157604052565b634e487b7160e01b5f52604160045260245ffd5b6001600160401b0381116101b157601f01601f191660200190565b9061020457508051156101f557805190602001fd5b63d6bda27560e01b5f5260045ffd5b81511580610235575b610215575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b1561020d56fe608060405236156051577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545f9081906001600160a01b0316368280378136915af43d5f803e15604d573d5ff35b3d5ffd5b00fea264697066735822122041b5f70a351952142223f22504ca7b4e6d975f3a302d114ff820442fcf815ac264736f6c634300081b0033"; const initCodeHash = keccak256(encodePacked(["bytes", "bytes"], [nexusProxyCreationCode, constructorArgs])); return getCreate2Address({ bytecodeHash: initCodeHash, from: deployer, salt }); } // src/utils/ens.ts async function reverseResolveEns(address) { try { const response = await fetch(`${ENS_API_URL}/reverse/${address}`); if (!response.ok) { throw new Error(`ENS API request failed: ${response.status} ${response.statusText}`); } const data = await response.json(); return { address: data.address, name: data.name || null }; } catch (error) { console.error("Failed to resolve ENS name:", error); return { address, name: null }; } } // src/utils/popup.ts import { rpcErrors } from "@metamask/rpc-errors"; var POPUP_WIDTH2 = 420; var POPUP_HEIGHT2 = 650; var openPopup = (url) => { const left = (window.innerWidth - POPUP_WIDTH2) / 2 + window.screenX; const top = (window.innerHeight - POPUP_HEIGHT2) / 2 + window.screenY; const popupId = `wallet_${window?.crypto?.randomUUID()}`; const popup = window.open(url, popupId, `width=${POPUP_WIDTH2}, height=${POPUP_HEIGHT2}, left=${left}, top=${top}`); popup?.focus(); if (!popup) { throw rpcErrors.internal("Pop up window failed to open"); } return popup; }; var closePopup = (popup) => { if (popup && !popup.closed) { popup.opener?.focus(); popup.close(); } }; // src/utils/strings.ts var hexStringFromNumber = (num) => { return `0x${BigInt(num).toString(16)}`; }; var safeJsonStringify = (obj) => JSON.stringify(obj, (_, value) => typeof value === "bigint" ? value.toString() + "n" : value, 2); // src/communicator.ts class Communicator { appMetadata; url; popup = null; listeners = new Map; onDisconnectCallback; constructor({ appMetadata, onDisconnectCallback }) { this.url = new URL(SDK_BACKEND_URL); this.appMetadata = appMetadata; this.onDisconnectCallback = onDisconnectCallback; } postMessage = async (message) => { const popup = await this.waitForPopupLoaded(); popup.postMessage(message, this.url.origin); }; postRequestAndWaitForResponse = async (request) => { const responsePromise = this.onMessage(({ requestId }) => requestId === request.requestId); this.postMessage(request); return await responsePromise; }; onMessage = async (predicate) => { return new Promise((resolve, reject) => { const listener = (event) => { if (event.origin !== this.url.origin) return; const message = event.data; if (predicate(message)) { resolve(message); window.removeEventListener("message", listener); this.listeners.delete(listener); } }; window.addEventListener("message", listener); this.listeners.set(listener, { reject }); }); }; onRequestCancelled = () => { closePopup(this.popup); this.popup = null; this.listeners.forEach(({ reject }, listener) => { reject(providerErrors.userRejectedRequest()); window.removeEventListener("message", listener); }); this.listeners.clear(); }; waitForPopupLoaded = async () => { if (this.popup && !this.popup.closed) { this.popup.focus(); return this.popup; } this.popup = openPopup(this.url); this.onMessage(({ event }) => event === "POPUP_UNLOADED" /* POPUP_UNLOADED */).then(this.onRequestCancelled).catch(() => {}); this.onMessage(({ event }) => event === "SDK_DISCONNECT" /* SDK_DISCONNECT */).then(() => { this.onDisconnectCallback?.(); this.onRequestCancelled(); }).catch(() => {}); return this.onMessage(({ event }) => event === "POPUP_LOADED" /* POPUP_LOADED */).then((message) => { this.postMessage({ chainId: DEFAULT_CHAIN_ID, data: { appMetadata: this.appMetadata, origin: window.location.origin, sdkVersion: SDK_VERSION }, event: "POPUP_APP_CONTEXT" /* POPUP_APP_CONTEXT */, origin: window.location.origin, requestId: message.requestId }); }).then(() => { if (!this.popup) throw rpcErrors2.internal(); return this.popup; }); }; } // src/provider/provider.ts import { errorCodes, providerErrors as providerErrors2, rpcErrors as rpcErrors4, serializeError } from "@metamask/rpc-errors"; // src/storage/storage.ts var memoryStorage = {}; class GeminiStorage { scope; module; constructor({ scope = "@gemini", module = "wallet" } = {}) { this.scope = scope; this.module = module; } scopedKey(key) { return `${this.scope}.${this.module}.${key}`; } async storeObject(key, item) { const json = safeJsonStringify(item); await this.setItem(key, json); } async loadObject(key, fallback) { const item = await this.getItem(key); if (!item) { await this.storeObject(key, fallback); return fallback; } try { return JSON.parse(item); } catch (error) { console.error(`Error parsing JSON for key ${key}:`, error); return fallback; } } async setItem(key, value) { const scoped = this.scopedKey(key); try { localStorage.setItem(scoped, value); } catch (e) { console.warn("localStorage not available, using memory storage", e); memoryStorage[scoped] = value; } } async getItem(key) { const scoped = this.scopedKey(key); try { return localStorage.getItem(scoped); } catch (e) { console.warn("localStorage not available, using memory storage", e); return memoryStorage[scoped] || null; } } async removeItem(key) { const scoped = this.scopedKey(key); try { localStorage.removeItem(scoped); } catch (e) { console.warn("localStorage not available, using memory storage", e); delete memoryStorage[scoped]; } } async removeItems(keys) { await Promise.all(keys.map((key) => this.removeItem(key))); } } // src/storage/storageInterface.ts var STORAGE_ETH_ACCOUNTS_KEY = "eth-accounts"; var STORAGE_ETH_ACTIVE_CHAIN_KEY = "eth-active-chain"; var STORAGE_PASSKEY_CREDENTIAL_KEY = "passkey-credential"; var STORAGE_SMART_ACCOUNT_KEY = "smart-account"; var STORAGE_SETTINGS_KEY = "settings"; // src/wallets/wallet.ts function isChainSupportedByGeminiSw(chainId) { return SUPPORTED_CHAIN_IDS.includes(chainId); } class GeminiWallet { communicator; storage; initPromise; accounts = []; chain = { id: DEFAULT_CHAIN_ID }; constructor({ appMetadata, chain, onDisconnectCallback, storage }) { this.communicator = new Communicator({ appMetadata, onDisconnectCallback }); this.storage = storage || new GeminiStorage; const initialChain = chain || { id: DEFAULT_CHAIN_ID }; this.initPromise = this.initializeFromStorage(initialChain); } async initializeFromStorage(defaultChain) { const fallbackChain = { id: defaultChain.id, rpcUrl: defaultChain.rpcUrl || getDefaultRpcUrl(defaultChain.id) }; const [storedChain, storedAccounts] = await Promise.all([ this.storage.loadObject(STORAGE_ETH_ACTIVE_CHAIN_KEY, fallbackChain), this.storage.loadObject(STORAGE_ETH_ACCOUNTS_KEY, this.accounts) ]); this.chain = { ...storedChain, rpcUrl: storedChain.rpcUrl || getDefaultRpcUrl(storedChain.id) }; this.accounts = storedAccounts; } async ensureInitialized() { await this.initPromise; } async connect() { await this.ensureInitialized(); const response = await this.sendMessageToPopup({ chainId: this.chain.id, event: "SDK_CONNECT" /* SDK_CONNECT */, origin: window.location.origin }); this.accounts = [response.data.address]; await this.storage.storeObject(STORAGE_ETH_ACCOUNTS_KEY, this.accounts); return this.accounts; } async disconnect() { await this.ensureInitialized(); this.accounts = []; await this.storage.storeObject(STORAGE_ETH_ACCOUNTS_KEY, this.accounts); } async switchChain({ id }) { await this.ensureInitialized(); if (isChainSupportedByGeminiSw(id)) { this.chain = { id, rpcUrl: getDefaultRpcUrl(id) }; await this.storage.storeObject(STORAGE_ETH_ACTIVE_CHAIN_KEY, this.chain); return; } const response = await this.sendMessageToPopup({ chainId: this.chain.id, data: id, event: "SDK_SWITCH_CHAIN" /* SDK_SWITCH_CHAIN */, origin: window.location.origin }); return response.data.error; } async sendTransaction(txData) { await this.ensureInitialized(); const response = await this.sendMessageToPopup({ chainId: this.chain.id, data: txData, event: "SDK_SEND_TRANSACTION" /* SDK_SEND_TRANSACTION */, origin: window.location.origin }); return response.data; } async signData({ message }) { await this.ensureInitialized(); const response = await this.sendMessageToPopup({ chainId: this.chain.id, data: { message }, event: "SDK_SIGN_DATA" /* SDK_SIGN_DATA */, origin: window.location.origin }); return response.data; } async signTypedData({ message, types, primaryType, domain }) { await this.ensureInitialized(); const response = await this.sendMessageToPopup({ chainId: this.chain.id, data: { domain, message, primaryType, types }, event: "SDK_SIGN_TYPED_DATA" /* SDK_SIGN_TYPED_DATA */, origin: window.location.origin }); return response.data; } async openSettings() { await this.ensureInitialized(); await this.sendMessageToPopup({ chainId: this.chain.id, data: {}, event: "SDK_OPEN_SETTINGS" /* SDK_OPEN_SETTINGS */, origin: window.location.origin }); } sendMessageToPopup(request) { return this.communicator.postRequestAndWaitForResponse({ ...request, requestId: window?.crypto?.randomUUID() }); } } // src/provider/provider.utils.ts import { rpcErrors as rpcErrors3 } from "@metamask/rpc-errors"; import { isHex } from "viem"; var fetchRpcRequest = async (request, rpcUrl) => { const requestBody = { ...request, id: window?.crypto?.randomUUID(), jsonrpc: "2.0" }; const res = await window.fetch(rpcUrl, { body: JSON.stringify(requestBody), headers: { "Content-Type": "application/json" }, method: "POST", mode: "cors" }); const { result, error } = await res.json(); if (error) throw error; return result; }; function validateRpcRequestArgs(args) { if (!args || typeof args !== "object" || Array.isArray(args)) { throw rpcErrors3.invalidParams({ message: "Expected a single, non-array, object argument." }); } const { method, params } = args; if (typeof method !== "string" || method.length === 0) { throw rpcErrors3.invalidParams({ message: "'args.method' must be a non-empty string." }); } if (params !== undefined && !Array.isArray(params) && (typeof params !== "object" || params === null)) { throw rpcErrors3.invalidParams({ message: "'args.params' must be an object or array if provided." }); } } function convertSendValuesToBigInt(tx) { const FIELDS_TO_NORMALIZE = ["value", "gas", "gasPrice", "maxPriorityFeePerGas", "maxFeePerGas"]; const normalized = { ...tx }; for (const field of FIELDS_TO_NORMALIZE) { if (!(field in tx)) continue; const value = tx[field]; if (typeof value === "bigint") continue; if (isHex(value)) { normalized[field] = BigInt(value); } } return normalized; } // src/provider/provider.ts class GeminiWalletProvider extends ProviderEventEmitter { config; wallet = null; constructor(providerConfig) { super(); this.config = providerConfig; const userDisconnectCallback = providerConfig.onDisconnectCallback; this.wallet = new GeminiWallet({ ...providerConfig, onDisconnectCallback: () => { userDisconnectCallback?.(); this.disconnect(); } }); } async request(args) { try { validateRpcRequestArgs(args); if (!this.wallet?.accounts?.length) { switch (args.method) { case "eth_requestAccounts": { if (!this.wallet) { const userDisconnectCallback = this.config.onDisconnectCallback; this.wallet = new GeminiWallet({ ...this.config, onDisconnectCallback: () => { userDisconnectCallback?.(); this.disconnect(); } }); } await this.wallet.connect(); this.emit("accountsChanged", this.wallet.accounts); break; } case "net_version": return DEFAULT_CHAIN_ID; case "eth_chainId": return hexStringFromNumber(DEFAULT_CHAIN_ID); default: { throw providerErrors2.unauthorized(); } } } let response; let requestParams; switch (args.method) { case "eth_requestAccounts": case "eth_accounts": response = this.wallet.accounts; break; case "net_version": response = this.wallet.chain.id; break; case "eth_chainId": response = hexStringFromNumber(this.wallet.chain.id); break; case "personal_sign": case "wallet_sign": requestParams = args.params; response = await this.wallet.signData({ account: requestParams[1], message: requestParams[0] }); if (response.error) { throw rpcErrors4.transactionRejected(response.error); } else { response = response.hash; } break; case "eth_sendTransaction": case "wallet_sendTransaction": requestParams = args.params; requestParams = convertSendValuesToBigInt(requestParams[0]); response = await this.wallet.sendTransaction(requestParams); if (response.error) { throw rpcErrors4.transactionRejected(response.error); } else { response = response.hash; } break; case "wallet_switchEthereumChain": { const rawParams = args.params; let chainId; if (Array.isArray(rawParams) && rawParams[0]?.chainId) { chainId = parseInt(rawParams[0].chainId, 16); } else if (rawParams && typeof rawParams === "object" && "id" in rawParams && Number.isInteger(rawParams.id)) { chainId = rawParams.id; } else { throw rpcErrors4.invalidParams("Invalid chain id argument. Expected [{ chainId: hex_string }] or { id: number }."); } response = await this.wallet.switchChain({ id: chainId }); if (response) { throw providerErrors2.custom({ code: 4902, message: response }); } await this.emit("chainChanged", hexStringFromNumber(chainId)); break; } case "eth_signTypedData_v1": case "eth_signTypedData_v2": case "eth_signTypedData_v3": case "eth_signTypedData_v4": case "eth_signTypedData": { requestParams = args.params; const signedTypedDataParams = JSON.parse(requestParams[1]); response = await this.wallet.signTypedData({ account: requestParams[0], domain: signedTypedDataParams.domain, message: signedTypedDataParams.message, primaryType: signedTypedDataParams.primaryType, types: signedTypedDataParams.types }); if (response.error) { throw rpcErrors4.transactionRejected(response.error); } else { response = response.hash; } break; } case "eth_ecRecover": case "eth_subscribe": case "eth_unsubscribe": case "personal_ecRecover": case "eth_signTransaction": case "wallet_watchAsset": case "wallet_sendCalls": case "wallet_getCallsStatus": case "wallet_getCapabilities": case "wallet_showCallsStatus": case "wallet_grantPermissions": throw rpcErrors4.methodNotSupported("Not yet implemented."); case "eth_sign": case "eth_coinbase": case "wallet_addEthereumChain": throw rpcErrors4.methodNotSupported(); default: if (!this.wallet.chain.rpcUrl) throw rpcErrors4.internal(`RPC URL missing for current chain (${this.wallet.chain.id})`); return fetchRpcRequest(args, this.wallet.chain.rpcUrl); } return response; } catch (error) { const { code } = error; if (code === errorCodes.provider.unauthorized) this.disconnect(); return Promise.reject(serializeError(error)); } } async openSettings() { await this.wallet?.openSettings(); } async disconnect() { if (this.wallet) { const storage = this.config.storage || new GeminiStorage; await storage.removeItem(STORAGE_ETH_ACCOUNTS_KEY); await storage.removeItem(STORAGE_ETH_ACTIVE_CHAIN_KEY); } this.wallet = null; this.config.onDisconnectCallback?.(); await this.emit("disconnect", "User initiated disconnection"); await this.emit("accountsChanged", []); } } export { validateWebAuthnKey, validateRpcRequestArgs, utf8StringToBuffer, safeJsonStringify, reverseResolveEns, openPopup, hexStringFromNumber, generateAuthenticatorIdHash, fetchRpcRequest, encodeBase64, decodeBase64, convertSendValuesToBigInt, closePopup, calculateWalletAddress, bufferToBase64URLString, base64ToHex, STORAGE_SMART_ACCOUNT_KEY, STORAGE_SETTINGS_KEY, STORAGE_PASSKEY_CREDENTIAL_KEY, STORAGE_ETH_ACTIVE_CHAIN_KEY, STORAGE_ETH_ACCOUNTS_KEY, SDK_VERSION, SDK_BACKEND_URL, ProviderEventEmitter, PlatformType, POPUP_WIDTH, POPUP_HEIGHT, GeminiWalletProvider, GeminiWallet, GeminiStorage, GeminiSdkEvent, DEFAULT_CHAIN_ID, Communicator }; //# debugId=AABF3419CA97199564756E2164756E21 //# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vc3JjL2NvbW11bmljYXRvci50cyIsICIuLi9zcmMvY29uc3RhbnRzLnRzIiwgIi4uL3NyYy90eXBlcy50cyIsICIuLi9zcmMvdXRpbHMvYmFzZTY0LnRzIiwgIi4uL3NyYy91dGlscy9jYWxjdWxhdGVXYWxsZXRBZGRyZXNzLnRzIiwgIi4uL3NyYy91dGlscy9lbnMudHMiLCAiLi4vc3JjL3V0aWxzL3BvcHVwLnRzIiwgIi4uL3NyYy91dGlscy9zdHJpbmdzLnRzIiwgIi4uL3NyYy9wcm92aWRlci9wcm92aWRlci50cyIsICIuLi9zcmMvc3RvcmFnZS9zdG9yYWdlLnRzIiwgIi4uL3NyYy9zdG9yYWdlL3N0b3JhZ2VJbnRlcmZhY2UudHMiLCAiLi4vc3JjL3dhbGxldHMvd2FsbGV0LnRzIiwgIi4uL3NyYy9wcm92aWRlci9wcm92aWRlci51dGlscy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsKICAgICJpbXBvcnQgeyBwcm92aWRlckVycm9ycywgcnBjRXJyb3JzIH0gZnJvbSBcIkBtZXRhbWFzay9ycGMtZXJyb3JzXCI7XG5cbmltcG9ydCB7IERFRkFVTFRfQ0hBSU5fSUQgfSBmcm9tIFwiLi9jb25zdGFudHNcIjtcblxuaW1wb3J0IHtcbiAgQXBwQ29udGV4dCxcbiAgdHlwZSBBcHBNZXRhZGF0YSxcbiAgR2VtaW5pU2RrRXZlbnQsXG4gIHR5cGUgR2VtaW5pU2RrTWVzc2FnZSxcbiAgdHlwZSBHZW1pbmlTZGtNZXNzYWdlUmVzcG9uc2UsXG59IGZyb20gXCIuL3R5cGVzXCI7XG5pbXBvcnQgeyBjbG9zZVBvcHVwLCBvcGVuUG9wdXAsIFNES19CQUNLRU5EX1VSTCwgU0RLX1ZFUlNJT04gfSBmcm9tIFwiLi91dGlsc1wiO1xuXG50eXBlIENvbW11bmljYXRvckNvbmZpZ1BhcmFtcyA9IHtcbiAgYXBwTWV0YWRhdGE6IEFwcE1ldGFkYXRhO1xuICBvbkRpc2Nvbm5lY3RDYWxsYmFjaz86ICgpID0+IHZvaWQ7XG59O1xuXG4vLyBjcmVhdGVzIGFuZCBjb21tdW5pY2F0ZXMgd2l0aCBhIHBvcHVwIHdpbmRvdyB0byBzZW5kIGFuZCByZWNlaXZlIG1lc3NhZ2VzXG5leHBvcnQgY2xhc3MgQ29tbXVuaWNhdG9yIHtcbiAgcHJpdmF0ZSByZWFkb25seSBhcHBNZXRhZGF0YTogQXBwTWV0YWRhdGE7XG4gIHByaXZhdGUgcmVhZG9ubHkgdXJsOiBVUkw7XG4gIHByaXZhdGUgcG9wdXA6IFdpbmRvdyB8IG51bGwgPSBudWxsO1xuICBwcml2YXRlIGxpc3RlbmVycyA9IG5ldyBNYXA8KF86IE1lc3NhZ2VFdmVudCkgPT4gdm9pZCwgeyByZWplY3Q6IChfOiBFcnJvcikgPT4gdm9pZCB9PigpO1xuICBwcml2YXRlIG9uRGlzY29ubmVjdENhbGxiYWNrPzogKCkgPT4gdm9pZDtcblxuICBjb25zdHJ1Y3Rvcih7IGFwcE1ldGFkYXRhLCBvbkRpc2Nvbm5lY3RDYWxsYmFjayB9OiBDb21tdW5pY2F0b3JDb25maWdQYXJhbXMpIHtcbiAgICB0aGlzLnVybCA9IG5ldyBVUkwoU0RLX0JBQ0tFTkRfVVJMKTtcbiAgICB0aGlzLmFwcE1ldGFkYXRhID0gYXBwTWV0YWRhdGE7XG4gICAgdGhpcy5vbkRpc2Nvbm5lY3RDYWxsYmFjayA9IG9uRGlzY29ubmVjdENhbGxiYWNrO1xuICB9XG5cbiAgLy8gcG9zdHMgYSBtZXNzYWdlIHRvIHRoZSBwb3B1cCB3aW5kb3dcbiAgcG9zdE1lc3NhZ2UgPSBhc3luYyAobWVzc2FnZTogR2VtaW5pU2RrTWVzc2FnZSkgPT4ge1xuICAgIGNvbnN0IHBvcHVwID0gYXdhaXQgdGhpcy53YWl0Rm9yUG9wdXBMb2FkZWQoKTtcbiAgICBwb3B1cC5wb3N0TWVzc2FnZShtZXNzYWdlLCB0aGlzLnVybC5vcmlnaW4pO1xuICB9O1xuXG4gIC8vIHBvc3RzIGEgcmVxdWVzdCB0byB0aGUgcG9wdXAgd2luZG93IGFuZCB3YWl0cyBmb3IgYSByZXNwb25zZVxuICBwb3N0UmVxdWVzdEFuZFdhaXRGb3JSZXNwb25zZSA9IGFzeW5jIDxNIGV4dGVuZHMgR2VtaW5pU2RrTWVzc2FnZSwgUiBleHRlbmRzIEdlbWluaVNka01lc3NhZ2VSZXNwb25zZT4oXG4gICAgcmVxdWVzdDogR2VtaW5pU2RrTWVzc2FnZSxcbiAgKTogUHJvbWlzZTxSPiA9PiB7XG4gICAgY29uc3QgcmVzcG9uc2VQcm9taXNlID0gdGhpcy5vbk1lc3NhZ2U8TSwgUj4oKHsgcmVxdWVzdElkIH0pID0+IHJlcXVlc3RJZCA9PT0gcmVxdWVzdC5yZXF1ZXN0SWQpO1xuICAgIHRoaXMucG9zdE1lc3NhZ2UocmVxdWVzdCk7XG4gICAgcmV0dXJuIGF3YWl0IHJlc3BvbnNlUHJvbWlzZTtcbiAgfTtcblxuICAvLyBsaXN0ZW5zIGZvciBtZXNzYWdlcyBmcm9tIHRoZSBwb3B1cCB3aW5kb3cgdGhhdCBtYXRjaCBhIGdpdmVuIHByZWRpY2F0ZVxuICBvbk1lc3NhZ2UgPSBhc3luYyA8TSBleHRlbmRzIEdlbWluaVNka01lc3NhZ2UsIFIgZXh0ZW5kcyBHZW1pbmlTZGtNZXNzYWdlUmVzcG9uc2U+KFxuICAgIHByZWRpY2F0ZTogKF86IFBhcnRpYWw8TT4pID0+IGJvb2xlYW4sXG4gICk6IFByb21pc2U8Uj4gPT4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XG4gICAgICBjb25zdCBsaXN0ZW5lciA9IChldmVudDogTWVzc2FnZUV2ZW50PE0+KSA9PiB7XG4gICAgICAgIC8vIGVuc3VyZSBvcmlnaW4gb2YgbWVzc2FnZVxuICAgICAgICBpZiAoZXZlbnQub3JpZ2luICE9PSB0aGlzLnVybC5vcmlnaW4pIHJldHVybjtcblxuICAgICAgICBjb25zdCBtZXNzYWdlID0gZXZlbnQuZGF0YTtcbiAgICAgICAgaWYgKHByZWRpY2F0ZShtZXNzYWdlKSkge1xuICAgICAgICAgIHJlc29sdmUobWVzc2FnZSBhcyB1bmtub3duIGFzIFIpO1xuICAgICAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKFwibWVzc2FnZVwiLCBsaXN0ZW5lcik7XG4gICAgICAgICAgdGhpcy5saXN0ZW5lcnMuZGVsZXRlKGxpc3RlbmVyKTtcbiAgICAgICAgfVxuICAgICAgfTtcblxuICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIGxpc3RlbmVyKTtcbiAgICAgIHRoaXMubGlzdGVuZXJzLnNldChsaXN0ZW5lciwgeyByZWplY3QgfSk7XG4gICAgfSk7XG4gIH07XG5cbiAgLy8gY2xvc2VzIHRoZSBwb3B1cCwgcmVqZWN0cyBhbGwgcmVxdWVzdHMgYW5kIGNsZWFycyBldmVudCBsaXN0ZW5lcnNcbiAgcHJpdmF0ZSBvblJlcXVlc3RDYW5jZWxsZWQgPSAoKSA9PiB7XG4gICAgY2xvc2VQb3B1cCh0aGlzLnBvcHVwKTtcbiAgICB0aGlzLnBvcHVwID0gbnVsbDtcblxuICAgIHRoaXMubGlzdGVuZXJzLmZvckVhY2goKHsgcmVqZWN0IH0sIGxpc3RlbmVyKSA9PiB7XG4gICAgICByZWplY3QocHJvdmlkZXJFcnJvcnMudXNlclJlamVjdGVkUmVxdWVzdCgpKTtcbiAgICAgIHdpbmRvdy5yZW1vdmVFdmVudExpc3RlbmVyKFwibWVzc2FnZVwiLCBsaXN0ZW5lcik7XG4gICAgfSk7XG4gICAgdGhpcy5saXN0ZW5lcnMuY2xlYXIoKTtcbiAgfTtcblxuICAvLyB3YWl0cyBmb3IgdGhlIHBvcHVwIHdpbmRvdyB0byBmdWxseSBsb2FkIGFuZCB0aGVuIHNlbmRzIGEgdmVyc2lvbiBtZXNzYWdlXG4gIHdhaXRGb3JQb3B1cExvYWRlZCA9IGFzeW5jICgpOiBQcm9taXNlPFdpbmRvdz4gPT4ge1xuICAgIGlmICh0aGlzLnBvcHVwICYmICF0aGlzLnBvcHVwLmNsb3NlZCkge1xuICAgICAgLy8gaW4gY2FzZSB0aGUgdXNlciB1bi1mb2N1c2VkIHRoZSBwb3B1cCBiZXR3ZWVuIHJlcXVlc3RzLCBmb2N1cyBpdCBhZ2FpblxuICAgICAgdGhpcy5wb3B1cC5mb2N1cygpO1xuICAgICAgcmV0dXJuIHRoaXMucG9wdXA7XG4gICAgfVxuXG4gICAgdGhpcy5wb3B1cCA9IG9wZW5Qb3B1cCh0aGlzLnVybCk7XG5cbiAgICAvLyBzZXR1cCBwb3B1cCBjbG9zZWQgbGlzdGVuZXIgaW4gY2FzZSB1c2VyIGNsb3NlcyB3aW5kb3cgd2l0aG91dCBleHBsaWNpdCByZXNwb25zZVxuICAgIHRoaXMub25NZXNzYWdlPEdlbWluaVNka01lc3NhZ2UsIEdlbWluaVNka01lc3NhZ2VSZXNwb25zZT4oKHsgZXZlbnQgfSkgPT4gZXZlbnQgPT09IEdlbWluaVNka0V2ZW50LlBPUFVQX1VOTE9BREVEKVxuICAgICAgLnRoZW4odGhpcy5vblJlcXVlc3RDYW5jZWxsZWQpXG4gICAgICAuY2F0Y2goKCkgPT4ge30pO1xuXG4gICAgLy8gc2V0dXAgYWNjb3VudCBkaXNjb25uZWN0IGxpc3RlbmVyIGluIGNhc2UgdXNlciByZXF1ZXN0cyBkaXNjb25uZWN0IGZyb20gd2l0aGluIHBvcHVwXG4gICAgdGhpcy5vbk1lc3NhZ2U8R2VtaW5pU2RrTWVzc2FnZSwgR2VtaW5pU2RrTWVzc2FnZVJlc3BvbnNlPigoeyBldmVudCB9KSA9PiBldmVudCA9PT0gR2VtaW5pU2RrRXZlbnQuU0RLX0RJU0NPTk5FQ1QpXG4gICAgICAudGhlbigoKSA9PiB7XG4gICAgICAgIC8vIGludm9rZSBkaXNjb25uZWN0IGNhbGxiYWNrIHBhc3NlZCBpbiBmcm9tIHdhbGxldFxuICAgICAgICB0aGlzLm9uRGlzY29ubmVjdENhbGxiYWNrPy4oKTtcbiAgICAgICAgLy8gY2xlYW51cCByZW1haW5pbmcgZXZlbnQgbGlzdGVuZXJzXG4gICAgICAgIHRoaXMub25SZXF1ZXN0Q2FuY2VsbGVkKCk7XG4gICAgICB9KVxuICAgICAgLmNhdGNoKCgpID0+IHt9KTtcblxuICAgIHJldHVybiB0aGlzLm9uTWVzc2FnZTxHZW1pbmlTZGtNZXNzYWdlLCBHZW1pbmlTZGtNZXNzYWdlUmVzcG9uc2U+KFxuICAgICAgKHsgZXZlbnQgfSkgPT4gZXZlbnQgPT09IEdlbWluaVNka0V2ZW50LlBPUFVQX0xPQURFRCxcbiAgICApXG4gICAgICAudGhlbihtZXNzYWdlID0+IHtcbiAgICAgICAgLy8gcmVwb3J0IGFwcCBtZXRhZGF0YSB0byBiYWNrZW5kIHVwb24gbG9hZCBjb21wbGV0ZVxuICAgICAgICB0aGlzLnBvc3RNZXNzYWdlKHtcbiAgICAgICAgICBjaGFpbklkOiBERUZBVUxUX0NIQUlOX0lELFxuICAgICAgICAgIGRhdGE6IHtcbiAgICAgICAgICAgIGFwcE1ldGFkYXRhOiB0aGlzLmFwcE1ldGFkYXRhLFxuICAgICAgICAgICAgb3JpZ2luOiB3aW5kb3cubG9jYXRpb24ub3JpZ2luLFxuICAgICAgICAgICAgc2RrVmVyc2lvbjogU0RLX1ZFUlNJT04sXG4gICAgICAgICAgfSBhcyBBcHBDb250ZXh0LFxuICAgICAgICAgIGV2ZW50OiBHZW1pbmlTZGtFdmVudC5QT1BVUF9BUFBfQ09OVEVYVCxcbiAgICAgICAgICBvcmlnaW46IHdpbmRvdy5sb2NhdGlvbi5vcmlnaW4sXG4gICAgICAgICAgcmVxdWVzdElkOiBtZXNzYWdlLnJlcXVlc3RJZCxcbiAgICAgICAgfSk7XG4gICAgICB9KVxuICAgICAgLnRoZW4oKCkgPT4ge1xuICAgICAgICBpZiAoIXRoaXMucG9wdXApIHRocm93IHJwY0Vycm9ycy5pbnRlcm5hbCgpO1xuICAgICAgICByZXR1cm4gdGhpcy5wb3B1cDtcbiAgICAgIH0pO1xuICB9O1xufVxuIiwKICAgICJpbXBvcnQge1xuICBhcmJpdHJ1bSxcbiAgYXJiaXRydW1TZXBvbGlhLFxuICBiYXNlLFxuICBiYXNlU2Vwb2xpYSxcbiAgbWFpbm5ldCxcbiAgb3B0aW1pc20sXG4gIG9wdGltaXNtU2Vwb2xpYSxcbiAgcG9seWdvbixcbiAgcG9seWdvbkFtb3ksXG4gIHNlcG9saWEsXG59IGZyb20gXCJ2aWVtL2NoYWluc1wiO1xuXG5leHBvcnQgY29uc3QgU0RLX0JBQ0tFTkRfVVJMID0gXCJodHRwczovL2tleXMuZ2VtaW5pLmNvbVwiO1xuZXhwb3J0IGNvbnN0IEVOU19BUElfVVJMID0gXCJodHRwczovL2hvcml6b24tYXBpLmdlbWluaS5jb20vYXBpL2Vuc1wiO1xuZXhwb3J0IGNvbnN0IFNES19WRVJTSU9OID0gXCIwLjIuMFwiO1xuZXhwb3J0IGNvbnN0IERFRkFVTFRfQ0hBSU5fSUQgPSA0MjE2MTsgLy8gQXJiaXRydW0gT25lXG5cbi8vIE1haW5uZXQgY2hhaW4gSURzXG5leHBvcnQgY29uc3QgTUFJTk5FVF9DSEFJTl9JRFMgPSB7XG4gIEFSQklUUlVNX09ORTogNDIxNjEsXG4gIEJBU0U6IDg0NTMsXG4gIEVUSEVSRVVNOiAxLFxuICBPUF9NQUlOTkVUOiAxMCxcbiAgUE9MWUdPTjogMTM3LFxufSBhcyBjb25zdDtcblxuLy8gVGVzdG5ldCBjaGFpbiBJRHNcbmV4cG9ydCBjb25zdCBURVNUTkVUX0NIQUlOX0lEUyA9IHtcbiAgQVJCSVRSVU1fU0VQT0xJQTogNDIxNjE0LFxuICBCQVNFX1NFUE9MSUE6IDg0NTMyLFxuICBPUF9TRVBPTElBOiAxMTE1NTQyMCxcbiAgUE9MWUdPTl9BTU9ZOiA4MDAwMixcbiAgU0VQT0xJQTogMTExNTUxMTEsXG59IGFzIGNvbnN0O1xuXG4vLyBBbGwgc3VwcG9ydGVkIGNoYWluIElEc1xuZXhwb3J0IGNvbnN0IFNVUFBPUlRFRF9DSEFJTl9JRFMgPSBbXG4gIC4uLk9iamVjdC52YWx1ZXMoTUFJTk5FVF9DSEFJTl9JRFMpLFxuICAuLi5PYmplY3QudmFsdWVzKFRFU1RORVRfQ0hBSU5fSURTKSxcbl07XG5cbi8vIEhlbHBlciBmdW5jdGlvbiB0byBnZXQgZGVmYXVsdCBSUEMgVVJMIGZvciBhIGNoYWluIHVzaW5nIHZpZW0gY2hhaW5zXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGVmYXVsdFJwY1VybChjaGFpbklkOiBudW1iZXIpOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBjb25zdCBjaGFpbk1hcDogUmVjb3JkPG51bWJlciwgc3RyaW5nPiA9IHtcbiAgICBbbWFpbm5ldC5pZF06IG1haW5uZXQucnBjVXJscy5kZWZhdWx0Lmh0dHBbMF0sXG4gICAgW2FyYml0cnVtLmlkXTogYXJiaXRydW0ucnBjVXJscy5kZWZhdWx0Lmh0dHBbMF0sXG4gICAgW29wdGltaXNtLmlkXTogb3B0aW1pc20ucnBjVXJscy5kZWZhdWx0Lmh0dHBbMF0sXG4gICAgW2Jhc2UuaWRdOiBiYXNlLnJwY1VybHMuZGVmYXVsdC5odHRwWzBdLFxuICAgIFtwb2x5Z29uLmlkXTogcG9seWdvbi5ycGNVcmxzLmRlZmF1bHQuaHR0cFswXSxcbiAgICBbc2Vwb2xpYS5pZF06IHNlcG9saWEucnBjVXJscy5kZWZhdWx0Lmh0dHBbMF0sXG4gICAgW2FyYml0cnVtU2Vwb2xpYS5pZF06IGFyYml0cnVtU2Vwb2xpYS5ycGNVcmxzLmRlZmF1bHQuaHR0cFswXSxcbiAgICBbb3B0aW1pc21TZXBvbGlhLmlkXTogb3B0aW1pc21TZXBvbGlhLnJwY1VybHMuZGVmYXVsdC5odHRwWzBdLFxuICAgIFtiYXNlU2Vwb2xpYS5pZF06IGJhc2VTZXBvbGlhLnJwY1VybHMuZGVmYXVsdC5odHRwWzBdLFxuICAgIFtwb2x5Z29uQW1veS5pZF06IHBvbHlnb25BbW95LnJwY1VybHMuZGVmYXVsdC5odHRwWzBdLFxuICB9O1xuXG4gIHJldHVybiBjaGFpbk1hcFtjaGFpbklkXTtcbn1cblxuLy8gUG9wdXAgd2luZG93IGRpbWVuc2lvbnNcbmV4cG9ydCBjb25zdCBQT1BVUF9XSURUSCA9IDQyMDtcbmV4cG9ydCBjb25zdCBQT1BVUF9IRUlHSFQgPSA2NTA7XG4iLAogICAgImltcG9ydCB7IEV2ZW50RW1pdHRlciB9IGZyb20gXCJldmVudGVtaXR0ZXIzXCI7XG5pbXBvcnQgdHlwZSB7IEFkZHJlc3MsIEhleCwgU2lnbk1lc3NhZ2VQYXJhbWV0ZXJzLCBTaWduVHlwZWREYXRhUGFyYW1ldGVycywgVHJhbnNhY3Rpb25SZXF1ZXN0IH0gZnJvbSBcInZpZW1cIjtcblxuaW1wb3J0IHsgdHlwZSBJU3RvcmFnZSB9IGZyb20gXCIuL3N0b3JhZ2Uvc3RvcmFnZUludGVyZmFjZVwiO1xuXG5leHBvcnQgZW51bSBHZW1pbmlTZGtFdmVudCB7XG4gIC8vIFBvcHVwIGV2ZW50c1xuICBQT1BVUF9MT0FERUQgPSBcIlBPUFVQX0xPQURFRFwiLFxuICBQT1BVUF9VTkxPQURFRCA9IFwiUE9QVVBfVU5MT0FERURcIixcbiAgUE9QVVBfQVBQX0NPTlRFWFQgPSBcIlBPUFVQX0FQUF9DT05URVhUXCIsXG5cbiAgLy8gU0RLIGV2ZW50c1xuICBTREtfQ09OTkVDVCA9IFwiU0RLX0NPTk5FQ1RcIixcbiAgU0RLX0RJU0NPTk5FQ1QgPSBcIlNES19ESVNDT05ORUNUXCIsXG4gIFNES19TRU5EX1RSQU5TQUNUSU9OID0gXCJTREtfU0VORF9UUkFOU0FDVElPTlwiLFxuICBTREtfU0lHTl9EQVRBID0gXCJTREtfU0lHTl9EQVRBXCIsXG4gIFNES19TSUdOX1RZUEVEX0RBVEEgPSBcIlNES19TSUdOX1RZUEVEX0RBVEFcIixcbiAgU0RLX1NXSVRDSF9DSEFJTiA9IFwiU0RLX1NXSVRDSF9DSEFJTlwiLFxuICBTREtfT1BFTl9TRVRUSU5HUyA9IFwiU0RLX09QRU5fU0VUVElOR1NcIixcbiAgU0RLX0NVUlJFTlRfQUNDT1VOVCA9IFwiU0RLX0NVUlJFTlRfQUNDT1VOVFwiLFxufVxuXG5leHBvcnQgaW50ZXJmYWNlIEFwcE1ldGFkYXRhIHtcbiAgYXBwTmFtZT86IHN0cmluZztcbiAgYXBwTG9nb1VybD86IHN0cmluZztcbiAgbmFtZT86IHN0cmluZztcbiAgZGVzY3JpcHRpb24/OiBzdHJpbmc7XG4gIHVybD86IHN0cmluZztcbiAgaWNvbnM/OiBzdHJpbmdbXTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBBcHBDb250ZXh0IHtcbiAgYXBwTWV0YWRhdGE6IEFwcE1ldGFkYXRhO1xuICBvcmlnaW46IHN0cmluZztcbiAgc2RrVmVyc2lvbjogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENoYWluIHtcbiAgaWQ6IG51bWJlcjtcbiAgcnBjVXJsPzogc3RyaW5nO1xufVxuXG4vLyBVc2luZyBjb25zdCBvYmplY3Qgd2l0aCAnYXMgY29uc3QnIGFzc2VydGlvbiBpbnN0ZWFkIG9mIGVudW1cbi8vIFRoaXMgYXZvaWRzIFR5cGVTY3JpcHQncyBpc29sYXRlZE1vZHVsZXMgcmUtZXhwb3J0IGxpbWl0YXRpb25zXG5leHBvcnQgY29uc3QgUGxhdGZvcm1UeXBlID0ge1xuICBSRUFDVF9OQVRJVkU6IFwiUkVBQ1RfTkFUSVZFXCIsXG4gIFdFQjogXCJXRUJcIixcbn0gYXMgY29uc3Q7XG5cbi8vIEV4dHJhY3QgdHlwZSBmcm9tIGNvbnN0IG9iamVjdCBmb3IgdHlwZSBzYWZldHlcbmV4cG9ydCB0eXBlIFBsYXRmb3JtVHlwZSA9ICh0eXBlb2YgUGxhdGZvcm1UeXBlKVtrZXlvZiB0eXBlb2YgUGxhdGZvcm1UeXBlXTtcblxuZXhwb3J0IHR5cGUgR2VtaW5pUHJvdmlkZXJDb25maWcgPSB7XG4gIGFwcE1ldGFkYXRhOiBBcHBNZXRhZGF0YTtcbiAgY2hhaW46IENoYWluO1xuICBwbGF0Zm9ybT86IFBsYXRmb3JtVHlwZTtcbiAgb25EaXNjb25uZWN0Q2FsbGJhY2s/OiAoKSA9PiB2b2lkO1xuICBzdG9yYWdlPzogSVN0b3JhZ2U7XG59O1xuXG5leHBvcnQgaW50ZXJmYWNlIFJwY1JlcXVlc3RBcmdzIHtcbiAgcmVhZG9ubHkgbWV0aG9kOiBzdHJpbmc7XG4gIHJlYWRvbmx5IHBhcmFtcz86IHJlYWRvbmx5IHVua25vd25bXSB8IG9iamVjdCB8IEhleFtdO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIFByb3ZpZGVyUnBjRXJyb3IgZXh0ZW5kcyBFcnJvciB7XG4gIGNvZGU6IG51bWJlcjtcbiAgZGF0YT86IHVua25vd247XG4gIG1lc3NhZ2U6IHN0cmluZztcbn1cblxuZXhwb3J0IHR5cGUgUHJvdmlkZXJFdmVudE1hcCA9IHtcbiAgYWNjb3VudHNDaGFuZ2VkOiBzdHJpbmdbXTtcbiAgY2hhaW5DaGFuZ2VkOiBzdHJpbmc7IC8vIGhleCBzdHJpbmdcbiAgY29ubmVjdDoge1xuICAgIHJlYWRvbmx5IGNoYWluSWQ6IHN0cmluZztcbiAgfTtcbiAgZGlzY29ubmVjdDogUHJvdmlkZXJScGNFcnJvcjtcbn07XG5cbmV4cG9ydCB0eXBlIFByb3ZpZGVyRXZlbnRDYWxsYmFjayA9IFByb3ZpZGVySW50ZXJmYWNlW1wiZW1pdFwiXTtcblxuZXhwb3J0IGNsYXNzIFByb3ZpZGVyRXZlbnRFbWl0dGVyIGV4dGVuZHMgRXZlbnRFbWl0dGVyPGtleW9mIFByb3ZpZGVyRXZlbnRNYXA+IHt9XG5cbmV4cG9ydCBpbnRlcmZhY2UgUHJvdmlkZXJJbnRlcmZhY2UgZXh0ZW5kcyBQcm92aWRlckV2ZW50RW1pdHRlciB7XG4gIGRpc2Nvbm5lY3QoKTogUHJvbWlzZTx2b2lkPjtcbiAgZW1pdDxLIGV4dGVuZHMga2V5b2YgUHJvdmlkZXJFdmVudE1hcD4oZXZlbnQ6IEssIC4uLmFyZ3M6IFtQcm92aWRlckV2ZW50TWFwW0tdXSk6IGJvb2xlYW47XG4gIG9uPEsgZXh0ZW5kcyBrZXlvZiBQcm92aWRlckV2ZW50TWFwPihldmVudDogSywgbGlzdGVuZXI6IChfOiBQcm92aWRlckV2ZW50TWFwW0tdKSA9PiB2b2lkKTogdGhpcztcbiAgcmVxdWVzdChhcmdzOiBScGNSZXF1ZXN0QXJncyk6IFByb21pc2U8YW55Pjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHZW1pbmlTZGtNZXNzYWdlIHtcbiAgY2hhaW5JZDogbnVtYmVyO1xuICBkYXRhPzogdW5rbm93bjtcbiAgZXZlbnQ6IEdlbWluaVNka0V2ZW50O1xuICBvcmlnaW46IHN0cmluZztcbiAgcmVxdWVzdElkPzogc3RyaW5nO1xuICB3Y0RhdGE/OiBhbnk7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgR2VtaW5pU2RrTWVzc2FnZVJlc3BvbnNlIHtcbiAgZGF0YT86IHVua25vd247XG4gIGV2ZW50OiBHZW1pbmlTZGtFdmVudDtcbiAgcmVxdWVzdElkPzogc3RyaW5nO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIENvbm5lY3RSZXNwb25zZSBleHRlbmRzIE9taXQ8R2VtaW5pU2RrTWVzc2FnZVJlc3BvbnNlLCBcImRhdGFcIj4ge1xuICBkYXRhOiB7IGFkZHJlc3M6IEFkZHJlc3MgfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTZW5kVHJhbnNhY3Rpb25SZXNwb25zZSBleHRlbmRzIE9taXQ8R2VtaW5pU2RrTWVzc2FnZVJlc3BvbnNlLCBcImRhdGFcIj4ge1xuICBkYXRhOiB7IGhhc2g/OiBIZXg7IGVycm9yPzogc3RyaW5nIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2lnbk1lc3NhZ2VSZXNwb25zZSBleHRlbmRzIE9taXQ8R2VtaW5pU2RrTWVzc2FnZVJlc3BvbnNlLCBcImRhdGFcIj4ge1xuICBkYXRhOiB7IGhhc2g/OiBIZXg7IGVycm9yPzogc3RyaW5nIH07XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgU2lnblR5cGVkRGF0YVJlc3BvbnNlIGV4dGVuZHMgT21pdDxHZW1pbmlTZGtNZXNzYWdlUmVzcG9uc2UsIFwiZGF0YVwiPiB7XG4gIGRhdGE6IHsgaGFzaD86IEhleDsgZXJyb3I/OiBzdHJpbmcgfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBTd2l0Y2hDaGFpblJlc3BvbnNlIGV4dGVuZHMgT21pdDxHZW1pbmlTZGtNZXNzYWdlUmVzcG9uc2UsIFwiZGF0YVwiPiB7XG4gIGRhdGE6IHsgY2hhaW5JZD86IG51bWJlcjsgZXJyb3I/OiBzdHJpbmcgfTtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHZW1pbmlTZGtTZW5kVHJhbnNhY3Rpb24gZXh0ZW5kcyBPbWl0PEdlbWluaVNka01lc3NhZ2UsIFwiZGF0YVwiPiB7XG4gIGRhdGE6IFRyYW5zYWN0aW9uUmVxdWVzdDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBHZW1pbmlTZGtTaWduTWVzc2FnZSBleHRlbmRzIE9taXQ8R2VtaW5pU2RrTWVzc2FnZSwgXCJkYXRhXCI+IHtcbiAgZGF0YTogU2lnbk1lc3NhZ2VQYXJhbWV0ZXJzO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEdlbWluaVNka1NpZ25UeXBlZERhdGEgZXh0ZW5kcyBPbWl0PEdlbWluaVNka01lc3NhZ2UsIFwiZGF0YVwiPiB7XG4gIGRhdGE6IFNpZ25UeXBlZERhdGFQYXJhbWV0ZXJzO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIEdlbWluaVNka1N3aXRjaENoYWluIGV4dGVuZHMgT21pdDxHZW1pbmlTZGtNZXNzYWdlLCBcImRhdGFcIj4ge1xuICBkYXRhOiBudW1iZXI7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgR2VtaW5pU2RrQXBwQ29udGV4dE1lc3NhZ2UgZXh0ZW5kcyBPbWl0PEdlbWluaVNka01lc3NhZ2UsIFwiZGF0YVwiPiB7XG4gIGRhdGE6IEFwcENvbnRleHQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgUmV2ZXJzZUVuc1Jlc3BvbnNlIHtcbiAgYWRkcmVzczogQWRkcmVzcztcbiAgbmFtZTogc3RyaW5nIHwgbnVsbDtcbn1cbiIsCiAgICAiLyoqXG4gKiBVdGlsaXR5IGZ1bmN0aW9ucyBmb3IgYmFzZTY0IGVuY29kaW5nIGFuZCBkZWNvZGluZ1xuICogQ29tcGF0aWJsZSB3aXRoIGJvdGggYnJvd3NlciBhbmQgTm9kZS5qcyBlbnZpcm9ubWVudHNcbiAqL1xuXG4vKipcbiAqIEVuY29kZXMgYSBVaW50OEFycmF5IHRvIGEgYmFzZTY0dXJsIHN0cmluZ1xuICogQHBhcmFtIGFycmF5IC0gVGhlIFVpbnQ4QXJyYXkgdG8gZW5jb2RlXG4gKiBAcmV0dXJucyBUaGUgYmFzZTY0dXJsIGVuY29kZWQgc3RyaW5nXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlbmNvZGVCYXNlNjQoYXJyYXk6IFVpbnQ4QXJyYXkpOiBzdHJpbmcge1xuICBsZXQgYmFzZTY0OiBzdHJpbmc7XG5cbiAgLy8gQ2hlY2sgaWYgd2UncmUgaW4gYSBOb2RlLmpzIGVudmlyb25tZW50IChCdWZmZXIgaXMgYXZhaWxhYmxlKVxuICBpZiAodHlwZW9mIEJ1ZmZlciAhPT0gXCJ1bmRlZmluZWRcIikge1xuICAgIC8vIE5vZGUuanMgZW52aXJvbm1lbnRcbiAgICBiYXNlNjQgPSBCdWZmZXIuZnJvbShhcnJheSkudG9TdHJpbmcoXCJiYXNlNjRcIik7XG4gIH0gZWxzZSB7XG4gICAgLy8gQnJvd3NlciBlbnZpcm9ubWVudFxuICAgIGJhc2U2NCA9IGJ0b2EoXG4gICAgICBBcnJheS5mcm9tKGFycmF5KVxuICAgICAgICAubWFwKGIgPT4gU3RyaW5nLmZyb21DaGFyQ29kZShiKSlcbiAgICAgICAgLmpvaW4oXCJcIiksXG4gICAgKTtcbiAgfVxuXG4gIC8vIENvbnZlcnQgdG8gYmFzZTY0dXJsIGZvcm1hdCBieSByZXBsYWNpbmcgY2hhcmFjdGVyc1xuICByZXR1cm4gYmFzZTY0LnJlcGxhY2UoL1xcKy9nLCBcIi1cIikucmVwbGFjZSgvXFwvL2csIFwiX1wiKS5yZXBsYWNlKC89KyQvLCBcIlwiKTtcbn1cblxuLyoqXG4gKiBEZWNvZGVzIGEgYmFzZTY0dXJsIHN0cmluZyB0byBhIFVpbnQ4QXJyYXlcbiAqIEBwYXJhbSBiYXNlNjR1cmwgLSBUaGUgYmFzZTY0dXJsIGVuY29kZWQgc3RyaW5nXG4gKiBAcmV0dXJucyBUaGUgZGVjb2RlZCBVaW50OEFycmF5XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBkZWNvZGVCYXNlNjQoYmFzZTY0dXJsOiBzdHJpbmcpOiBVaW50OEFycmF5IHtcbiAgLy8gQ29udmVydCBiYXNlNjR1cmwgdG8gc3RhbmRhcmQgYmFzZTY0IGJ5IHJlc3RvcmluZyBzcGVjaWFsIGNoYXJzXG4gIGxldCBiYXNlNjQgPSBiYXNlNjR1cmwucmVwbGFjZSgvLS9nLCBcIitcIikucmVwbGFjZSgvXy9nLCBcIi9cIik7XG5cbiAgLy8gQWRkIHBhZGRpbmcgaWYgbmVlZGVkXG4gIHdoaWxlIChiYXNlNjQubGVuZ3RoICUgNCAhPT0gMCkge1xuICAgIGJhc2U2NCArPSBcIj1cIjtcbiAgfVxuXG4gIC8vIENoZWNrIGlmIHdlJ3JlIGluIGEgTm9kZS5qcyBlbnZpcm9ubWVudCAoQnVmZmVyIGlzIGF2YWlsYWJsZSlcbiAgaWYgKHR5cGVvZiBCdWZmZXIgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICAvLyBOb2RlLmpzIGVudmlyb25tZW50XG4gICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KEJ1ZmZlci5mcm9tKGJhc2U2NCwgXCJiYXNlNjRcIikpO1xuICB9IGVsc2Uge1xuICAgIC8vIEJyb3dzZXIgZW52aXJvbm1lbnRcbiAgICBjb25zdCBiaW5hcnlTdHJpbmcgPSBhdG9iKGJhc2U2NCk7XG4gICAgY29uc3QgYnl0ZXMgPSBuZXcgVWludDhBcnJheShiaW5hcnlTdHJpbmcubGVuZ3RoKTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGJpbmFyeVN0cmluZy5sZW5ndGg7IGkrKykge1xuICAgICAgYnl0ZXNbaV0gPSBiaW5hcnlTdHJpbmcuY2hhckNvZGVBdChpKTtcbiAgICB9XG4gICAgcmV0dXJuIGJ5dGVzO1xuICB9XG59XG5cbi8qKlxuICogQ29udmVydCBhbiBBcnJheUJ1ZmZlciBvciBVaW50OEFycmF5IHRvIGEgYmFzZTY0dXJsIHN0cmluZ1xuICogQHBhcmFtIGJ1ZmZlciAtIFRoZSBidWZmZXIgdG8gY29udmVydFxuICogQHJldHVybnMgVGhlIGJhc2U2NHVybCBlbmNvZGVkIHN0cmluZ1xuICovXG5leHBvcnQgZnVuY3Rpb24gYnVmZmVyVG9CYXNlNjRVUkxTdHJpbmcoYnVmZmVyOiBBcnJheUJ1ZmZlciB8IFVpbnQ4QXJyYXkpOiBzdHJpbmcge1xuICBjb25zdCBieXRlcyA9IGJ1ZmZlciBpbnN0YW5jZW9mIFVpbnQ4QXJyYXkgPyBidWZmZXIgOiBuZXcgVWludDhBcnJheShidWZmZXIpO1xuICByZXR1cm4gZW5jb2RlQmFzZTY0KGJ5dGVzKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGEgc3RyaW5nIHRvIFVURi04IGVuY29kZWQgVWludDhBcnJheVxuICogQHBhcmFtIHZhbHVlIC0gVGhlIHN0cmluZyB0byBjb252ZXJ0XG4gKiBAcmV0dXJucyBUaGUgVVRGLTggZW5jb2RlZCBVaW50OEFycmF5XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB1dGY4U3RyaW5nVG9CdWZmZXIodmFsdWU6IHN0cmluZyk6IFVpbnQ4QXJyYXkge1xuICBpZiAodHlwZW9mIFRleHRFbmNvZGVyICE9PSBcInVuZGVmaW5lZFwiKSB7XG4gICAgLy8gTW9kZXJuIGJyb3dzZXJzIGFuZCBOb2RlLmpzIHdpdGggVGV4dEVuY29kZXIgc3VwcG9ydFxuICAgIHJldHVybiBuZXcgVGV4dEVuY29kZXIoKS5lbmNvZGUodmFsdWUpO1xuICB9IGVsc2UgaWYgKHR5cGVvZiBCdWZmZXIgIT09IFwidW5kZWZpbmVkXCIpIHtcbiAgICAvLyBOb2RlLmpzIGZhbGxiYWNrXG4gICAgcmV0dXJuIG5ldyBVaW50OEFycmF5KEJ1ZmZlci5mcm9tKHZhbHVlLCBcInV0ZjhcIikpO1xuICB9IGVsc2Uge1xuICAgIC8vIFZlcnkgb2xkIGJyb3dzZXJzIGZhbGxiYWNrIChub3QgcmVjb21tZW5kZWQpXG4gICAgY29uc3QgYnl0ZXMgPSBuZXcgVWludDhBcnJheSh2YWx1ZS5sZW5ndGgpO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWUubGVuZ3RoOyBpKyspIHtcbiAgICAgIGJ5dGVzW2ldID0gdmFsdWUuY2hhckNvZGVBdChpKTtcbiAgICB9XG4gICAgcmV0dXJuIGJ5dGVzO1xuICB9XG59XG5cbi8qKlxuICogQ29udmVydCBhIGJhc2U2NCBzdHJpbmcgdG8gaGV4IHN0cmluZ1xuICogQHBhcmFtIGJhc2U2NCAtIFRoZSBiYXNlNjQgc3RyaW5nIHRvIGNvbnZlcnRcbiAqIEByZXR1cm5zIFRoZSBoZXggc3RyaW5nXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBiYXNlNjRUb0hleChiYXNlNjQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IGJ5dGVzID0gZGVjb2RlQmFzZTY0KGJhc2U2NCk7XG4gIHJldHVybiBBcnJheS5mcm9tKGJ5dGVzKVxuICAgIC5tYXAoYiA9PiBiLnRvU3RyaW5nKDE2KS5wYWRTdGFydCgyLCBcIjBcIikpXG4gICAgLmpvaW4oXCJcIik7XG59XG4iLAogICAgImltcG9ydCB7XG4gIHR5cGUgQWRkcmVzcyxcbiAgZW5jb2RlQWJpUGFyYW1ldGVycyxcbiAgZW5jb2RlRnVuY3Rpb25EYXRhLFxuICBlbmNvZGVQYWNrZWQsXG4gIGdldENyZWF0ZTJBZGRyZXNzLFxuICB0eXBlIEhleCxcbiAga2VjY2FrMjU2LFxufSBmcm9tIFwidmllbVwiO1xuXG4vLyBXZWJBdXRobiB2YWxpZGF0b3IgZGF0YSBzdHJ1Y3R1cmVcbmV4cG9ydCBpbnRlcmZhY2UgV2ViQXV0aG5WYWxpZGF0b3JEYXRhIHtcbiAgcHViS2V5WDogYmlnaW50O1xuICBwdWJLZXlZOiBiaWdpbnQ7XG59XG5cbi8vIFBhcmFtZXRlcnMgZm9yIGNhbGN1bGF0aW5nIHdhbGxldCBhZGRyZXNzXG5leHBvcnQgaW50ZXJmYWNlIENhbGN1bGF0ZVdhbGxldEFkZHJlc3NQYXJhbXMge1xuICBwdWJsaWNLZXk6IEhleDsgLy8gQ29tYmluZWQgNjQtYnl0ZSBoZXggc3RyaW5nICgzMiBieXRlcyBYICsgMzIgYnl0ZXMgWSlcbiAgY3JlZGVudGlhbElkOiBzdHJpbmc7IC8vIEJhc2U2NFVSTCBlbmNvZGVkIGNyZWRlbnRpYWwgSURcbiAgaW5kZXg/OiBiaWdpbnQ7IC8vIE9wdGlvbmFsLCBkZWZhdWx0cyB0byAwXG59XG5cbi8vIEVtYmVkZGVkIGNvbnRyYWN0IGFkZHJlc3NlcyBmb3IgSG9yaXpvbiBkZXBsb3ltZW50XG5jb25zdCBDT05UUkFDVF9BRERSRVNTRVMgPSB7XG4gIEFDQ09VTlRfSU1QTEVNRU5UQVRJT046IFwiMHgwMDA2MDUwMTY4REUyNTVhODY3MkFDYUQ0ODIxZTcyMUNCQTQ0MzM3XCIgYXMgY29uc3QsXG4gIEFUVEVTVEVSOiBcIjB4MDAwNDc0MzkyYTljZDg2YTQ2ODczNTRmMUNlMjk2NEI1MmU5NzQ4NFwiIGFzIGNvbnN0LFxuICBCT09UU1RSQVBQRVI6IFwiMHgwMDAwMDAwMEQzMjU0NDUyYTkwOUU0ZWVENDc0NTVBZjdFMjdDMjg5XCIgYXMgY29uc3QsXG4gIEZBQ1RPUlk6IFwiMHgwMEU1OERGNzBGYUI5ODNhMzI0YzRDMDY4YzgyZDIwNDA3NTc5RmFDXCIgYXMgY29uc3QsXG4gIFJFR0lTVFJZOiBcIjB4MDAwMDAwMDAwMDY5RTJhMTg3QUVGRmI4NTJiRjNjQ2RDOTUxNTFCMlwiIGFzIGNvbnN0LFxuICBXRUJBVVRITl9WQUxJREFUT1I6IFwiMHhiQTQ1YTJCRmI4RGUzRDI0Y0E5RDdGMUI1NTFFMTRkRkY1ZDY5MEZkXCIgYXMgY29uc3QsXG59O1xuXG4vKipcbiAqIENhbGN1bGF0ZSBzbWFydCB3YWxsZXQgYWRkcmVzcyBmcm9tIHB1YmxpYyBrZXkgYW5kIGNyZWRlbnRpYWwgSURcbiAqIFRoaXMgaGFuZGxlcyBhbGwgdmFsaWRhdGlvbiBhbmQgc2V0dXAgaW50ZXJuYWxseVxuICovXG5leHBvcnQgZnVuY3Rpb24gY2FsY3VsYXRlV2FsbGV0QWRkcmVzcyhwYXJhbXM6IENhbGN1bGF0ZVdhbGxldEFkZHJlc3NQYXJhbXMpOiBBZGRyZXNzIHtcbiAgY29uc3QgeyBwdWJsaWNLZXksIGNyZWRlbnRpYWxJZCwgaW5kZXggPSAwbiB9ID0gcGFyYW1zO1xuXG4gIC8vIFZhbGlkYXRlIGlucHV0XG4gIGlmICghcHVibGljS2V5LnN0YXJ0c1dpdGgoXCIweFwiKSB8fCBwdWJsaWNLZXkubGVuZ3RoICE9PSAxMzApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXCJJbnZhbGlkIHB1YmxpYyBrZXk6IG11c3QgYmUgNjQtYnl0ZSBoZXggc3RyaW5nICgweCArIDEyOCBjaGFycylcIik7XG4gIH1cblxuICAvLyBFeHRyYWN0IFggYW5kIFkgY29vcmRpbmF0ZXNcbiAgY29uc3QgcHViS2V5WCA9IGAweCR7cHVibGljS2V5LnNsaWNlKDIsIDY2KX1gIGFzIEhleDtcbiAgY29uc3QgcHViS2V5WSA9IGAweCR7cHVibGljS2V5LnNsaWNlKDY2LCAxMzApfWAgYXMgSGV4O1xuXG4gIC8vIENvbnZlcnQgdG8gV2ViQXV0aG5WYWxpZGF0b3JEYXRhXG4gIGNvbnN0IHdlYkF1dGhuRGF0YTogV2ViQXV0aG5WYWxpZGF0b3JEYXRhID0ge1xuICAgIHB1YktleVg6IEJpZ0ludChwdWJLZXlYKS