@gemini-wallet/core
Version:
Core SDK for Gemini Wallet integration with popup communication
835 lines (827 loc) • 114 kB
JavaScript
// 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