UNPKG

eulith-web3js-core

Version:

Eulith core web3js SDK (code to access Eulith services via web3js)

518 lines 70.8 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Uniswap = void 0; const web3_1 = __importDefault(require("web3")); const Eulith = __importStar(require("../src/index")); const IUniswapV3Pool_json_1 = __importDefault(require("../known_contracts_abis/IUniswapV3Pool.json")); // UNTESTED BUT MAYBE USEFUL ESMODULE WAY (cannot use without swtiching tsconfig to ES2020) // import { createRequire } from "module"; // const xrequire = createRequire(import.meta.url); // const kUniswapV3PoolABI = xrequire("../known_contracts_abis/IUniswapV3Pool.json"); // # this is based on the uniswap fee standard, which the server adheres to for other protocols // # 500 is 5 bips = 0.05% const INT_FEE_TO_FLOAT_DIVISOR = 1000000; /** * The Uniswap module wraps access to a bunch of UNISWAP APIs (which are mediated by control * structures within the Eulith Toolkit), as well as raw direct access to UNISWAP APIs. */ var Uniswap; (function (Uniswap) { /** * Uniswap.V3 wraps the V3 version of the Uniswap API */ let V3; (function (V3) { /** * @typedef Eulith.Uniswap.V3.PoolFee / @typedef Eulith.Uniswap.PoolFee * * Uniswap pools are organized by Fee, and have 3 categories of fee. * * Fee's are applied to swaps as rate * sellAmount (in units of the sell token). * * Selecting a fee is an advanced topic - for experts; obviously buyers want * the lowest fee possible, but depending on the size of the pool, you may see lower liquidity * on the lower feed pools, and so worse pricing. * * When in doubt, use Eulith.Uniswap.getBestPriceQuote. * * Historical Notes: Based on class UniswapV3PoolFee(int, Enum): */ let PoolFee; (function (PoolFee) { PoolFee[PoolFee["FiveBips"] = 500] = "FiveBips"; PoolFee[PoolFee["ThirtyBips"] = 3000] = "ThirtyBips"; PoolFee[PoolFee["OneHundredBips"] = 10000] = "OneHundredBips"; })(PoolFee = V3.PoolFee || (V3.PoolFee = {})); /** * @typedef Eulith.Uniswap.V3.LoanRequest / @typedef Eulith.Uniswap.LoanRequest * * A LoanRequest implements IFlashLoanable - so its designed to work with the 'Flash' mechanism, to create * instant, temporary liquidity. * * In the context of Uniswap, this just means using Uniswap pools to create that instant, temporary liquidity. * * This object rquires a borrowTokenA (and borrowAmountA), and an optional borrowTokenB (and borrowAmountB). * * payTransferFrom designates who PAYS the loan or sell side of the swap. * * @todo better explain this - but wait til I review the Flash code... */ class LoanRequest { /** * @constructor */ constructor(p) { Object.assign(this, p); } generateLoanRequest() { let wireFormatParams = { borrow_token_a: this.borrowTokenA.address, borrow_amount_a: this.borrowAmountA }; if (this.borrowTokenB) { wireFormatParams["borrow_token_b"] = this.borrowTokenB.address; wireFormatParams["borrow_amount_b"] = this.borrowAmountB; } if (this.payTransferFrom) { wireFormatParams["pay_transfer_from"] = this.payTransferFrom; } if (this.recipient) { wireFormatParams["recipient"] = this.recipient; } return { method: "eulith_start_uniswapv3_loan", params: [wireFormatParams] }; } get tokenValues() { let v = [this.borrowTokenA.asTokenValue(this.borrowAmountA)]; if (this.borrowTokenB) { v.push(this.borrowTokenB.asTokenValue(this.borrowAmountB)); } return v; } } V3.LoanRequest = LoanRequest; /** * @typedef Eulith.Uniswap.V3.SwapRequest / @typedef Eulith.Uniswap.SwapRequest * * This class is may generally be thought of as opaque, and is generally not used directly. * * Instead, these 'swap requests' are generally created by * Eulith.Uniswap.Pool instance method getQuote OR * Eulith.Uniswap.getBestPriceQuote, which both produce a Eulith.Uniswap.PriceQuote (which aggregates this object in the swapRequest member). * * This is then typically USED via a call to Eulith.Uniswap.startSwap() * * Historical Note: Based on python class EulithUniV3StartSwapRequest(TypedDict): */ class SwapRequest { /** * @constructor */ constructor(p) { Object.assign(this, p); } } V3.SwapRequest = SwapRequest; /** * @typedef Eulith.Uniswap.V3.PriceQuote / @typedef Eulith.Uniswap.PriceQuote * * Eulith.Uniswap.PriceQuote is generally not created directly, but as a result of calls * to Eulith.Uniswap.getBestPriceQuote, or 'a uniswap pool'.getQuote */ class PriceQuote { /** * @constructor */ constructor(p) { var _a; this.price = p === null || p === void 0 ? void 0 : p.price; this.swapRequest = p === null || p === void 0 ? void 0 : p.swapRequest; this.feePct_ = (_a = p.feePct) !== null && _a !== void 0 ? _a : 0; } /** * The fee is in units of of the 'sellToken', and a percent of the swapRequest.sellAmount - used to create the quote. */ get feeAmt() { return this.swapRequest.sellToken.asTokenValue(this.swapRequest.sellAmount * this.feePct_); } /** * The fee is in units of of the 'sellToken', and a percent of the swapRequest.sellAmount - used to create the quote. */ get feePct() { return this.feePct_; } } V3.PriceQuote = PriceQuote; /** * @typedef Eulith.Uniswap.Pool / @typedef Eulith.Uniswap.V3.Pool * * A UNISWAP pool contains exactly two different token contracts, and allows the user * to exchange currency in one, for the other. * * This cannot be constructed directly, but rather by Eulith.Uniswap.getSwapPool () * * HISTORICAL NOTE: BASED ON PYTHON EulithUniswapV3Pool */ class Pool { constructor({ web3, poolAddress, fee }) { this.fee_ = fee; this.poolAddress = poolAddress; this.web3 = web3; this.logger_ = web3.logger; this.contract_ = new web3.eth.Contract(IUniswapV3Pool_json_1.default, poolAddress); } /** * @constructor */ static AsyncConstructor({ provider, poolAddress, fee, token0Address, token1Address }) { return __awaiter(this, void 0, void 0, function* () { const useProvider = Eulith.Provider.ProviderOrWeb3(provider); const useWeb3 = new Eulith.Web3({ provider: useProvider }); const thisPool = new Pool({ web3: useWeb3, poolAddress, fee }); thisPool.token0_ = yield Eulith.Contracts.ERC20TokenContract.mk({ provider: useProvider, contractAddress: token0Address !== null && token0Address !== void 0 ? token0Address : (yield thisPool.contract_.methods.token0().call()), decimals: 18 }); thisPool.token1_ = yield Eulith.Contracts.ERC20TokenContract.mk({ provider: useProvider, contractAddress: token1Address !== null && token1Address !== void 0 ? token1Address : (yield thisPool.contract_.methods.token1().call()), decimals: 18 }); return thisPool; }); } /** * This return the fee (percentage) associated with this pool - Eulith.Uniswap.PoolFee * * This number is used to derive the actual fee that appears in specific quotes. * * There is an enumeration of possible fees. */ get fee() { return this.fee_; } /** * pool.contract returns the web3js native IUniswapV3Pool contract interface, with typescript typings for that interface. * * This extension point allows for calling of any of the IUniswapV3Pool apis, via the standard web3js contract syntax */ get contract() { return this.contract_; } /** * @todo consider redoing this as 'tokens' - a set. */ get token0() { return this.token0_; } /** * @todo consider redoing this as 'tokens' - a set. */ get token1() { return this.token1_; } /** * Given a uniswap pool, request a quote for the given sellToken and amount. * * @param payTransferFrom is not truely used in forming the 'quote'. But the getQuote method returns both the amount of money * it will cost, and an object - the swapRequest object. This requires the payTransferFrom to be embedded in it, and must * contain the 'source account' the sellToken will be transfering funds from, if that differs from the sender of the actual * request. * * @param fillOrKill -- either fill the whole order, or kill it. Don't allow partial fills * (i.e. if you requested to sell 10 WETH @ $1,700 per, but we can only fill 5 at that price, kill the order. * * @param recipient -- the opposite of payTransferFrom. Designates who gets the bought or loaned * (in this case bought) funds. Defaults to the calling wallet or authorized address of the toolkit contract. * * Example Usage: * * ```javascript * const swapPool = await Eulith.Uniswap.getSwapPool({ * request: { * tokenContracts: [usdcContract, wethContract], * fee: Eulith.Uniswap.PoolFee.ThirtyBips, * }, * provider * }); * const quote = await swapPool.getQuote({ sellToken: usdcContract, sellAmount: sellAmount, payTransferFrom: acct.address }); * ``` * * HISTORICAL NOTE: Based on Python EulithUniswapV3Pool.get_quote */ getQuote({ sellToken, sellAmount, fillOrKill, recipient, payTransferFrom }) { return __awaiter(this, void 0, void 0, function* () { if (sellToken.address != this.token0_.address && sellToken.address != this.token1_.address) { Eulith.Exceptions.API.Throw(this.logger_, { message: "cannot start swap on pool with no matching sell token, please make sure you're requesting to swap with one of the pool's tokens" }); } const buyToken = sellToken.options.address == this.token0_.options.address ? this.token1_ : this.token0_; const fee = this.fee; return yield Eulith.Uniswap.V3.getBestPriceQuote({ swapQuoteRequest: { sellToken, buyToken, amount: sellAmount, fee }, fillOrKill, recipient, payTransferFrom, provider: this.web3.provider }); }); } } V3.Pool = Pool; /** * Eulith.Uniswap.getBestPriceQuote / Eulith.Uniswap.V3.getBestPriceQuote * * Return the best available price (object Eulith.Uniswap.PriceQuote) which contains * the price, the fee, and actual swapRequest to use to perform this operation. * * Example Usage: * * ```javascript * const quote = await Eulith.Uniswap.getBestPriceQuote({ * swapQuoteRequest: { sellToken: tokenA, buyToken: tokenB, amount: sellAmount }, * payTransferFrom: acct.address, * provider * }); * ``` * * Timing note - if the price changes materially from when the quote happens, to when the actual transaction happens, the * actual transaction may fail (@todo details here might be interesting) * * For info about the @param swapQuoteRequest.fee argument - see Eulith.Uniswap.PoolFee, but mostly - recommended callers ignore/omit. * * The @param payTransferFrom is used to specify the account that will be charged for the sellAmount on the sellToken contract. If * not specified, the sender will be used by the sellToken contract. * * @param fillOrKill -- either fill the whole order, or kill it. Don't allow partial fills * (i.e. if you requested to sell 10 WETH @ $1,700 per, but we can only fill 5 at that price, kill the order. * * @param recipient -- the opposite of payTransferFrom. Designates who gets the bought or loaned * (in this case bought) funds. Defaults to the calling wallet or authorized address of the toolkit contract. * * Historical Note: Based on Python get_univ3_best_price_quote API */ function getBestPriceQuote({ swapQuoteRequest, fillOrKill, recipient, payTransferFrom, provider }) { return __awaiter(this, void 0, void 0, function* () { if (fillOrKill == null) { fillOrKill = true; // default } const wireFormatParams = { sell_token: swapQuoteRequest.sellToken.options.address, buy_token: swapQuoteRequest.buyToken.options.address, amount: swapQuoteRequest.amount, true_for_amount_in: swapQuoteRequest.trueForAmountIn == null ? true : swapQuoteRequest.trueForAmountIn }; if (swapQuoteRequest.fee) { wireFormatParams["fee"] = swapQuoteRequest.fee; } const wireFormattedResult = yield provider.request({ method: "eulith_uniswapv3_quote", params: [wireFormatParams] }); const sellToken = yield Eulith.Contracts.ERC20TokenContract.mk({ provider: provider, contractAddress: wireFormattedResult["sell_token"] }); let amount = wireFormattedResult.amount; if (!wireFormattedResult["true_for_amount_in"]) { amount *= -1.0; // make negative if we want exact amount out } const actualSwapRequest = new SwapRequest({ sellToken: sellToken, sellAmount: amount, poolAddress: web3_1.default.utils.toChecksumAddress(wireFormattedResult["pool_address"]), fillOrKill: fillOrKill, sqrtLimitPrice: wireFormattedResult["sqrt_limit_price"], recipient: recipient, payTransferFrom }); return new PriceQuote({ price: wireFormattedResult.price, feePct: wireFormattedResult.fee / INT_FEE_TO_FLOAT_DIVISOR, swapRequest: actualSwapRequest }); }); } V3.getBestPriceQuote = getBestPriceQuote; /** * Eulith.Uniswap.V3.startSwap / Eulith.Uniswap.startSwap * * Example Usage: * * ```javascript * const swapAtomicTx: Eulith.AtomicTx.NestedTransaction = await Eulith.Uniswap.startSwap({ request: quote.swapRequest, parentTx: atomicTx }) * await swapAtomicTx.commit(); * ``` * * \note mark this object as 'completed' calling swapAtomicTx.commit() * \req parentTx != null * * Historical Note: Analogous to Python start_uniswap_v3_swap or start_uni_swap */ function startSwap({ request, parentTx }) { return __awaiter(this, void 0, void 0, function* () { const wireFormatParams = { sell_token: request.sellToken.options.address, amount: request.sellAmount, pool_address: request.poolAddress, fill_or_kill: request.fillOrKill, sqrt_limit_price: request.sqrtLimitPrice }; if (request.recipient) { wireFormatParams["recipient"] = request.recipient; } if (request.payTransferFrom) { wireFormatParams["pay_transfer_from"] = request.payTransferFrom; } const useProvider = parentTx.provider; const res = yield useProvider.request({ method: "eulith_start_uniswapv3_swap", params: [wireFormatParams] }); return new Eulith.AtomicTx.NestedTransaction({ parentTx }); }); } V3.startSwap = startSwap; /** * Eulith.Uniswap.getSwapPool / Eulith.Uniswap.V3.getSwapPool * * A UNISWAP pool contains exactly two different token Contracts, and allows the user * to exchange currency in one, for the other. Each pool also has exactly one fee associated (as a percent of exchange amount). * * getSwapPool fetches the UNISWAP pool which contains both these two argument Contracts. * * NOTE - the order of the request.tokenContracts is meaningless (not using Set only because JS makes this syntactically more awkward) * * The fee is a required parameter for looking up a pool - see Eulith.Uniswap.PoolFee for details. * If you don't know what fee to use, don't use getSwapPool, but use Eulith.Uniswap.getBestPriceQuote instead * * Note that the returned Eulith.Uniswap.Pool object can generally be used directly (as the examples below), * but also can be used to access the underlying contract: IUniswapV3Pool accessor. * * Example Usage: * * ```javascript * const swapPool = await Eulith.Uniswap.getSwapPool({ * request: { * tokenContracts: [usdcContract, wethContract], * fee: Eulith.Uniswap.PoolFee.ThirtyBips, * }, * provider * }); * const quote = await swapPool.getQuote({ sellToken: usdcContract, sellAmount: sellAmount, payTransferFrom: acct.address }); * ``` */ function getSwapPool({ request, provider }) { return __awaiter(this, void 0, void 0, function* () { const useProvider = Eulith.Provider.ProviderOrWeb3(provider); let tokenContracts = request.tokenContracts; if (tokenContracts.length != 2 || tokenContracts[0] == tokenContracts[1]) { throw new Error("UNISWAP.getSwapPool requires a request precisely two different tokenContracts"); } const wireFormatParams = { token_a: tokenContracts[0].address, token_b: tokenContracts[1].address, fee: request.fee }; const wireFormattedResult = yield useProvider.request({ method: "eulith_uniswapv3_pool_lookup", params: [wireFormatParams] }); if (!Array.isArray(wireFormattedResult) || wireFormattedResult.length != 1) { Eulith.Exceptions.API.Throw(useProvider.logger, { message: `uniswap v3 pool lookup came back with an unexpected response: ${JSON.stringify(wireFormattedResult)}` }); } const token_zero = wireFormattedResult[0].token_zero; const token_one = wireFormattedResult[0].token_one; const fee = wireFormattedResult[0].fee; const poolAddress = wireFormattedResult[0].pool_address; return yield Pool.AsyncConstructor({ provider: useProvider, poolAddress: web3_1.default.utils.toChecksumAddress(poolAddress), fee: fee, token0Address: token_zero, token1Address: token_one }); }); } V3.getSwapPool = getSwapPool; })(V3 = Uniswap.V3 || (Uniswap.V3 = {})); /** * Eulith.Uniswap.PoolFee: see Eulith.Uniswap.V3.PoolFee */ Uniswap.PoolFee = V3.PoolFee; /** * Eulith.Uniswap.Pool: see Eulith.Uniswap.V3.Pool */ Uniswap.Pool = V3.Pool; /** * Eulith.Uniswap.PriceQuote: See Eulith.Uniswap.V3.PriceQuote */ Uniswap.PriceQuote = V3.PriceQuote; /** * Eulith.Uniswap.LoanRequest: see Eulith.Uniswap.V3.LoanRequest */ Uniswap.LoanRequest = V3.LoanRequest; /** * Eulith.Uniswap.SwapRequest: see Eulith.Uniswap.V3.SwapRequest */ Uniswap.SwapRequest = V3.SwapRequest; /** * Eulith.Uniswap.getBestPriceQuote: see Eulith.Uniswap.V3.getBestPriceQuote */ Uniswap.getBestPriceQuote = V3.getBestPriceQuote; /** * Eulith.Uniswap.getSwapPool: see Eulith.Uniswap.V3.getSwapPool */ Uniswap.getSwapPool = V3.getSwapPool; /** * Eulith.Uniswap.startSwap: see Eulith.Uniswap.V3.startSwap */ Uniswap.startSwap = V3.startSwap; })(Uniswap = exports.Uniswap || (exports.Uniswap = {})); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidW5pc3dhcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91bmlzd2FwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBQUEsZ0RBQXdCO0FBSXhCLHFEQUF1QztBQUl2QyxzR0FBNEU7QUFFNUUsMkZBQTJGO0FBQzNGLDBDQUEwQztBQUMxQyxtREFBbUQ7QUFDbkQscUZBQXFGO0FBRXJGLCtGQUErRjtBQUMvRiwwQkFBMEI7QUFDMUIsTUFBTSx3QkFBd0IsR0FBRyxPQUFPLENBQUM7QUFFekM7OztHQUdHO0FBQ0gsSUFBYyxPQUFPLENBaWtCcEI7QUFqa0JELFdBQWMsT0FBTztJQUNqQjs7T0FFRztJQUNILElBQWMsRUFBRSxDQStnQmY7SUEvZ0JELFdBQWMsRUFBRTtRQUNaOzs7Ozs7Ozs7Ozs7OztXQWNHO1FBQ0gsSUFBWSxPQUlYO1FBSkQsV0FBWSxPQUFPO1lBQ2YsK0NBQWMsQ0FBQTtZQUNkLG9EQUFpQixDQUFBO1lBQ2pCLDZEQUFzQixDQUFBO1FBQzFCLENBQUMsRUFKVyxPQUFPLEdBQVAsVUFBTyxLQUFQLFVBQU8sUUFJbEI7UUFFRDs7Ozs7Ozs7Ozs7OztXQWFHO1FBQ0gsTUFBYSxXQUFXO1lBUXBCOztlQUVHO1lBQ0gsWUFBbUIsQ0FBdUI7Z0JBQ3RDLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQzNCLENBQUM7WUFFTSxtQkFBbUI7Z0JBQ3RCLElBQUksZ0JBQWdCLEdBQUc7b0JBQ25CLGNBQWMsRUFBRSxJQUFJLENBQUMsWUFBWSxDQUFDLE9BQU87b0JBQ3pDLGVBQWUsRUFBRSxJQUFJLENBQUMsYUFBYTtpQkFDdEMsQ0FBQztnQkFDRixJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7b0JBQ25CLGdCQUFnQixDQUFDLGdCQUFnQixDQUFDLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUM7b0JB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QUdsQzs7T0FFRztJQUNVLFlBQUksR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDO0lBRzVCOztPQUVHO0lBQ1Usa0JBQVUsR0FBRyxFQUFFLENBQUMsVUFBVSxDQUFDO0lBR3hDOztPQUVHO0lBQ1UsbUJBQVcsR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDO0lBRzFDOztPQUVHO0lBQ1UsbUJBQVcsR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDO0lBRzFDOztPQUVHO0lBQ1UseUJBQWlCLEdBQUcsRUFBRSxDQUFDLGlCQUFpQixDQUFDO0lBRXREOztPQUVHO0lBQ1UsbUJBQVcsR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDO0lBRTFDOztPQUVHO0lBQ1UsaUJBQVMsR0FBRyxFQUFFLENBQUMsU0FBUyxDQUFDO0FBQzFDLENBQUMsRUFqa0JhLE9BQU8sR0FBUCxlQUFPLEtBQVAsZUFBTyxRQWlrQnBCIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IFdlYjMgZnJvbSBcIndlYjNcIjtcbmltcG9ydCB7IEFiaUl0ZW0gfSBmcm9tIFwid2ViMy11dGlsc1wiO1xuaW1wb3J0IHsgUmVxdWVzdEFyZ3VtZW50cyB9IGZyb20gXCJ3ZWIzLWNvcmVcIjtcblxuaW1wb3J0ICogYXMgRXVsaXRoIGZyb20gXCIuLi9zcmMvaW5kZXhcIjtcblxuLy8gUHJpdmF0ZVxuaW1wb3J0IHsgSVVuaXN3YXBWM1Bvb2wgfSBmcm9tIFwiLi4va25vd25fY29udHJhY3RzX3R5cGluZ3NcIjtcbmltcG9ydCBrVW5pc3dhcFYzUG9vbEFCSSBmcm9tIFwiLi4va25vd25fY29udHJhY3RzX2FiaXMvSVVuaXN3YXBWM1Bvb2wuanNvblwiO1xuXG4vLyBVTlRFU1RFRCBCVVQgTUFZQkUgVVNFRlVMIEVTTU9EVUxFIFdBWSAoY2Fubm90IHVzZSB3aXRob3V0IHN3dGljaGluZyB0c2NvbmZpZyB0byBFUzIwMjApXG4vLyBpbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSBcIm1vZHVsZVwiO1xuLy8gY29uc3QgeHJlcXVpcmUgPSBjcmVhdGVSZXF1aXJlKGltcG9ydC5tZXRhLnVybCk7XG4vLyBjb25zdCBrVW5pc3dhcFYzUG9vbEFCSSA9IHhyZXF1aXJlKFwiLi4va25vd25fY29udHJhY3RzX2FiaXMvSVVuaXN3YXBWM1Bvb2wuanNvblwiKTtcblxuLy8gIyB0aGlzIGlzIGJhc2VkIG9uIHRoZSB1bmlzd2FwIGZlZSBzdGFuZGFyZCwgd2hpY2ggdGhlIHNlcnZlciBhZGhlcmVzIHRvIGZvciBvdGhlciBwcm90b2NvbHNcbi8vICMgNTAwIGlzIDUgYmlwcyA9IDAuMDUlXG5jb25zdCBJTlRfRkVFX1RPX0ZMT0FUX0RJVklTT1IgPSAxMDAwMDAwO1xuXG4vKipcbiAqICBUaGUgVW5pc3dhcCBtb2R1bGUgd3JhcHMgYWNjZXNzIHRvIGEgYnVuY2ggb2YgVU5JU1dBUCBBUElzICh3aGljaCBhcmUgbWVkaWF0ZWQgYnkgY29udHJvbFxuICogIHN0cnVjdHVyZXMgd2l0aGluIHRoZSBFdWxpdGggVG9vbGtpdCksIGFzIHdlbGwgYXMgcmF3IGRpcmVjdCBhY2Nlc3MgdG8gVU5JU1dBUCBBUElzLlxuICovXG5leHBvcnQgbW9kdWxlIFVuaXN3YXAge1xuICAgIC8qKlxuICAgICAqICBVbmlzd2FwLlYzIHdyYXBzIHRoZSBWMyB2ZXJzaW9uIG9mIHRoZSBVbmlzd2FwIEFQSVxuICAgICAqL1xuICAgIGV4cG9ydCBtb2R1bGUgVjMge1xuICAgICAgICAvKipcbiAgICAgICAgICogIEB0eXBlZGVmIEV1bGl0aC5Vbmlzd2FwLlYzLlBvb2xGZWUgLyBAdHlwZWRlZiBFdWxpdGguVW5pc3dhcC5Qb29sRmVlXG4gICAgICAgICAqXG4gICAgICAgICAqICBVbmlzd2FwIHBvb2xzIGFyZSBvcmdhbml6ZWQgYnkgRmVlLCBhbmQgaGF2ZSAzIGNhdGVnb3JpZXMgb2YgZmVlLlxuICAgICAgICAgKlxuICAgICAgICAgKiAgRmVlJ3MgYXJlIGFwcGxpZWQgdG8gc3dhcHMgYXMgcmF0ZSAqIHNlbGxBbW91bnQgKGluIHVuaXRzIG9mIHRoZSBzZWxsIHRva2VuKS5cbiAgICAgICAgICpcbiAgICAgICAgICogIFNlbGVjdGluZyBhIGZlZSBpcyBhbiBhZHZhbmNlZCB0b3BpYyAtIGZvciBleHBlcnRzOyBvYnZpb3VzbHkgYnV5ZXJzIHdhbnRcbiAgICAgICAgICogIHRoZSBsb3dlc3QgZmVlIHBvc3NpYmxlLCBidXQgZGVwZW5kaW5nIG9uIHRoZSBzaXplIG9mIHRoZSBwb29sLCB5b3UgbWF5IHNlZSBsb3dlciBsaXF1aWRpdHlcbiAgICAgICAgICogIG9uIHRoZSBsb3dlciBmZWVkIHBvb2xzLCBhbmQgc28gd29yc2UgcHJpY2luZy5cbiAgICAgICAgICpcbiAgICAgICAgICogIFdoZW4gaW4gZG91YnQsIHVzZSBFdWxpdGguVW5pc3dhcC5nZXRCZXN0UHJpY2VRdW90ZS5cbiAgICAgICAgICpcbiAgICAgICAgICogIEhpc3RvcmljYWwgTm90ZXM6IEJhc2VkIG9uIGNsYXNzIFVuaXN3YXBWM1Bvb2xGZWUoaW50LCBFbnVtKTpcbiAgICAgICAgICovXG4gICAgICAgIGV4cG9ydCBlbnVtIFBvb2xGZWUge1xuICAgICAgICAgICAgRml2ZUJpcHMgPSA1MDAsXG4gICAgICAgICAgICBUaGlydHlCaXBzID0gMzAwMCxcbiAgICAgICAgICAgIE9uZUh1bmRyZWRCaXBzID0gMTAwMDBcbiAgICAgICAgfVxuXG4gICAgICAgIC8qKlxuICAgICAgICAgKiAgQHR5cGVkZWYgRXVsaXRoLlVuaXN3YXAuVjMuTG9hblJlcXVlc3QgLyBAdHlwZWRlZiBFdWxpdGguVW5pc3dhcC5Mb2FuUmVxdWVzdFxuICAgICAgICAgKlxuICAgICAgICAgKiAgQSBMb2FuUmVxdWVzdCBpbXBsZW1lbnRzIElGbGFzaExvYW5hYmxlIC0gc28gaXRzIGRlc2lnbmVkIHRvIHdvcmsgd2l0aCB0aGUgJ0ZsYXNoJyBtZWNoYW5pc20sIHRvIGNyZWF0ZVxuICAgICAgICAgKiAgaW5zdGFudCwgdGVtcG9yYXJ5IGxpcXVpZGl0eS5cbiAgICAgICAgICpcbiAgICAgICAgICogIEluIHRoZSBjb250ZXh0IG9mIFVuaXN3YXAsIHRoaXMganVzdCBtZWFucyB1c2luZyBVbmlzd2FwIHBvb2xzIHRvIGNyZWF0ZSB0aGF0IGluc3RhbnQsIHRlbXBvcmFyeSBsaXF1aWRpdHkuXG4gICAgICAgICAqXG4gICAgICAgICAqICBUaGlzIG9iamVjdCBycXVpcmVzIGEgYm9ycm93VG9rZW5BIChhbmQgYm9ycm93QW1vdW50QSksIGFuZCBhbiBvcHRpb25hbCBib3Jyb3dUb2tlbkIgKGFuZCBib3Jyb3dBbW91bnRCKS5cbiAgICAgICAgICpcbiAgICAgICAgICogIHBheVRyYW5zZmVyRnJvbSBkZXNpZ25hdGVzIHdobyBQQVlTIHRoZSBsb2FuIG9yIHNlbGwgc2lkZSBvZiB0aGUgc3dhcC5cbiAgICAgICAgICpcbiAgICAgICAgICogIEB0b2RvIGJldHRlciBleHBsYWluIHRoaXMgLSBidXQgd2FpdCB0aWwgSSByZXZpZXcgdGhlIEZsYXNoIGNvZGUuLi5cbiAgICAgICAgICovXG4gICAgICAgIGV4cG9ydCBjbGFzcyBMb2FuUmVxdWVzdCBpbXBsZW1lbnRzIEV1bGl0aC5GbGFzaExpcXVpZGl0eS5JRmxhc2hMb2FuYWJsZSB7XG4gICAgICAgICAgICBib3Jyb3dUb2tlbkE6IEV1bGl0aC5Db250cmFjdHMuRVJDMjBUb2tlbkNvbnRyYWN0O1xuICAgICAgICAgICAgYm9ycm93QW1vdW50QTogbnVtYmVyO1xuICAgICAgICAgICAgYm9ycm93VG9rZW5CPzogRXVsaXRoLkNvbnRyYWN0cy5FUkMyMFRva2VuQ29udHJhY3Q7XG4gICAgICAgICAgICBib3Jyb3dBbW91bnRCPzogbnVtYmVyO1xuICAgICAgICAgICAgcGF5VHJhbnNmZXJGcm9tPzogc3RyaW5nO1xuICAgICAgICAgICAgcmVjaXBpZW50Pzogc3RyaW5nO1xuXG4gICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAqICBAY29uc3RydWN0b3JcbiAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgcHVibGljIGNvbnN0cnVjdG9yKHA6IFBhcnRpYWw8TG9hblJlcXVlc3Q+KSB7XG4gICAgICAgICAgICAgICAgT2JqZWN0LmFzc2lnbih0aGlzLCBwKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgcHVibGljIGdlbmVyYXRlTG9hblJlcXVlc3QoKTogUmVxdWVzdEFyZ3VtZW50cyB7XG4gICAgICAgICAgICAgICAgbGV0IHdpcmVGb3JtYXRQYXJhbXMgPSB7XG4gICAgICAgICAgICAgICAgICAgIGJvcnJvd190b2tlbl9hOiB0aGlzLmJvcnJvd1Rva2VuQS5hZGRyZXNzLFxuICAgICAgICAgICAgICAgICAgICBib3Jyb3dfYW1vdW50X2E6IHRoaXMuYm9ycm93QW1vdW50QVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuYm9ycm93VG9rZW5CKSB7XG4gICAgICAgICAgICAgICAgICAgIHdpcmVGb3JtYXRQYXJhbXNbXCJib3Jyb3dfdG9rZW5fYlwiXSA9IHRoaXMuYm9ycm93VG9rZW5CLmFkZHJlc3M7XG4gICAgICAgICAgICAgICAgICAgIHdpcmVGb3JtYXRQYXJhbXNbXCJib3Jyb3dfYW1vdW50X2JcIl0gPSB0aGlzLmJvcnJvd0Ftb3VudEI7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmICh0aGlzLnBheVRyYW5zZmVyRnJvbSkge1xuICAgICAgICAgICAgICAgICAgICB3aXJlRm9ybWF0UGFyYW1zW1wicGF5X3RyYW5zZmVyX2Zyb21cIl0gPSB0aGlzLnBheVRyYW5zZmVyRnJvbTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMucmVjaXBpZW50KSB7XG4gICAgICAgICAgICAgICAgICAgIHdpcmVGb3JtYXRQYXJhbXNbXCJyZWNpcGllbnRcIl0gPSB0aGlzLnJlY2lwaWVudDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgICAgICAgICAgbWV0aG9kOiBcImV1bGl0aF9zdGFydF91bmlzd2FwdjNfbG9hblwiLFxuICAgICAgICAgICAgICAgICAgICBwYXJhbXM6IFt3aXJlRm9ybWF0UGFyYW1zXVxuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBnZXQgdG9rZW5WYWx1ZXMoKTogRXVsaXRoLlRva2Vucy5WYWx1ZS5JVG9rZW5WYWx1ZVtdIHtcbiAgICAgICAgICAgICAgICBsZXQgdiA9IFt0aGlzLmJvcnJvd1Rva2VuQS5hc1Rva2VuVmFsdWUodGhpcy5ib3Jyb3dBbW91bnRBKV07XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuYm9ycm93VG9rZW5CKSB7XG4gICAgICAgICAgICAgICAgICAgIHYucHVzaCh0aGlzLmJvcnJvd1Rva2VuQi5hc1Rva2VuVmFsdWUodGhpcy5ib3Jyb3dBbW91bnRCKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJldHVybiB2O1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLyoqXG4gICAgICAgICAqICBAdHlwZWRlZiBFdWxpdGguVW5pc3dhcC5WMy5Td2FwUmVxdWVzdCAvIEB0eXBlZGVmIEV1bGl0aC5Vbmlzd2FwLlN3YXBSZXF1ZXN0XG4gICAgICAgICAqXG4gICAgICAgICAqICBUaGlzIGNsYXNzIGlzIG1heSBnZW5lcmFsbHkgYmUgdGhvdWdodCBvZiBhcyBvcGFxdWUsIGFuZCBpcyBnZW5lcmFsbHkgbm90IHVzZWQgZGlyZWN0bHkuXG4gICAgICAgICAqXG4gICAgICAgICAqICBJbnN0ZWFkLCB0aGVzZSAnc3dhcCByZXF1ZXN0cycgYXJlIGdlbmVyYWxseSBjcmVhdGVkIGJ5XG4gICAgICAgICAqICAgICAgRXVsaXRoLlVuaXN3YXAuUG9vbCBpbnN0YW5jZSBtZXRob2QgZ2V0UXVvdGUgT1JcbiAgICAgICAgICogICAgICBFdWxpdGguVW5pc3dhcC5nZXRCZXN0UHJpY2VRdW90ZSwgd2hpY2ggYm90aCBwcm9kdWNlIGEgRXVsaXRoLlVuaXN3YXAuUHJpY2VRdW90ZSAod2hpY2ggYWdncmVnYXRlcyB0aGlzIG9iamVjdCBpbiB0aGUgc3dhcFJlcXVlc3QgbWVtYmVyKS5cbiAgICAgICAgICpcbiAgICAgICAgICogIFRoaXMgaXMgdGhlbiB0eXBpY2FsbHkgVVNFRCB2aWEgYSBjYWxsIHRvIEV1bGl0aC5Vbmlzd2FwLnN0YXJ0U3dhcCgpXG4gICAgICAgICAqXG4gICAgICAgICAqICBIaXN0b3JpY2FsIE5vdGU6IEJhc2VkIG9uIHB5dGhvbiBjbGFzcyBFdWxpdGhVbmlWM1N0YXJ0U3dhcFJlcXVlc3QoVHlwZWREaWN0KTpcbiAgICAgICAgICovXG4gICAgICAgIGV4cG9ydCBjbGFzcyBTd2FwUmVxdWVzdCB7XG4gICAgICAgICAgICBwdWJsaWMgc2VsbFRva2VuOiBFdWxpdGguQ29udHJhY3RzLkVSQzIwVG9rZW5Db250cmFjdDtcbiAgICAgICAgICAgIHB1YmxpYyBzZWxsQW1vdW50OiBudW1iZXI7XG4gICAgICAgICAgICBwdWJsaWMgcG9vbEFkZHJlc3M6IHN0cmluZztcbiAgICAgICAgICAgIHB1YmxpYyBmaWxsT3JLaWxsOiBib29sZWFuO1xuICAgICAgICAgICAgcHVibGljIHNxcnRMaW1pdFByaWNlOiBzdHJpbmc7XG4gICAgICAgICAgICBwdWJsaWMgcmVjaXBpZW50Pzogc3RyaW5nO1xuICAgICAgICAgICAgcHVibGljIHBheVRyYW5zZmVyRnJvbT86IHN0cmluZztcblxuICAgICAgICAgICAgLyoqXG4gICAgICAgICAgICAgKiAgQGNvbnN0cnVjdG9yXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihwOiBQYXJ0aWFsPFN3YXBSZXF1ZXN0Pikge1xuICAgICAgICAgICAgICAgIE9iamVjdC5hc3NpZ24odGhpcywgcCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogIEB0eXBlZGVmIEV1bGl0aC5Vbmlzd2FwLlYzLlByaWNlUXVvdGUgLyBAdHlwZWRlZiBFdWxpdGguVW5pc3dhcC5QcmljZVF1b3RlXG4gICAgICAgICAqXG4gICAgICAgICAqICBFdWxpdGguVW5pc3dhcC5QcmljZVF1b3RlIGlzIGdlbmVyYWxseSBub3QgY3JlYXRlZCBkaXJlY3RseSwgYnV0IGFzIGEgcmVzdWx0IG9mIGNhbGxzXG4gICAgICAgICAqICB0byBFdWxpdGguVW5pc3dhcC5nZXRCZXN0UHJpY2VRdW90ZSwgb3IgJ2EgdW5pc3dhcCBwb29sJy5nZXRRdW90ZVxuICAgICAgICAgKi9cbiAgICAgICAgZXhwb3J0IGNsYXNzIFByaWNlUXVvdGUge1xuICAgICAgICAgICAgcHJpdmF0ZSBmZWVQY3RfOiBudW1iZXI7XG5cbiAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICogIEBjb25zdHJ1Y3RvclxuICAgICAgICAgICAgICovXG4gICAgICAgICAgICBwdWJsaWMgY29uc3RydWN0b3IocD86IFBhcnRpYWw8UHJpY2VRdW90ZT4pIHtcbiAgICAgICAgICAgICAgICB0aGlzLnByaWNlID0gcD8ucHJpY2U7XG4gICAgICAgICAgICAgICAgdGhpcy5zd2FwUmVxdWVzdCA9IHA/LnN3YXBSZXF1ZXN0O1xuICAgICAgICAgICAgICAgIHRoaXMuZmVlUGN0XyA9IHAuZmVlUGN0ID8/IDA7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICogIFRoZSBwcmljZSBpcyBpbiB1bml0cyBvZiBvZiB0aGUgJ3NlbGxUb2tlbicgLSB1c2VkIHRvIGNyZWF0ZSB0aGUgcXVvdGUuXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgIHB1YmxpYyBwcmljZTogbnVtYmVyO1xuXG4gICAgICAgICAgICAvKipcbiAgICAgICAgICAgICAqICBUaGlzIGlzIHRoZSByZXF1ZXN0IG9iamVjdCBwYXNzZWQgYWxvbmcgdG8gdGhlIEV1bGl0aC5Vbmlzd2FwLnN0YXJ0U3dhcCBBUEkuXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgIHB1YmxpYyBzd2FwUmVxdWVzdDogU3dhcFJlcXVlc3Q7XG5cbiAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICogIFRoZSBmZWUgaXMgaW4gdW5pdHMgb2Ygb2YgdGhlICdzZWxsVG9rZW4nLCBhbmQgYSBwZXJjZW50IG9mIHRoZSBzd2FwUmVxdWVzdC5zZWxsQW1vdW50IC0gdXNlZCB0byBjcmVhdGUgdGhlIHF1b3RlLlxuICAgICAgICAgICAgICovXG4gICAgICAgICAgICBwdWJsaWMgZ2V0IGZlZUFtdCgpOiBFdWxpdGguVG9rZW5zLlZhbHVlLklUb2tlblZhbHVlIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zd2FwUmVxdWVzdC5zZWxsVG9rZW4uYXNUb2tlblZhbHVlKHRoaXMuc3dhcFJlcXVlc3Quc2VsbEFtb3VudCAqIHRoaXMuZmVlUGN0Xyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICogIFRoZSBmZWUgaXMgaW4gdW5pdHMgb2Ygb2YgdGhlICdzZWxsVG9rZW4nLCBhbmQgYSBwZXJjZW50IG9mIHRoZSBzd2FwUmVxdWVzdC5zZWxsQW1vdW50IC0gdXNlZCB0byBjcmVhdGUgdGhlIHF1b3RlLlxuICAgICAgICAgICAgICovXG4gICAgICAgICAgICBwdWJsaWMgZ2V0IGZlZVBjdCgpOiBudW1iZXIge1xuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmZlZVBjdF87XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvKipcbiAgICAgICAgICogQHR5cGVkZWYgRXVsaXRoLlVuaXN3YXAuUG9vbCAvIEB0eXBlZGVmIEV1bGl0aC5Vbmlzd2FwLlYzLlBvb2xcbiAgICAgICAgICpcbiAgICAgICAgICogIEEgVU5JU1dBUCBwb29sIGNvbnRhaW5zIGV4YWN0bHkgdHdvIGRpZmZlcmVudCB0b2tlbiBjb250cmFjdHMsIGFuZCBhbGxvd3MgdGhlIHVzZXJcbiAgICAgICAgICogIHRvIGV4Y2hhbmdlIGN1cnJlbmN5IGluIG9uZSwgZm9yIHRoZSBvdGhlci5cbiAgICAgICAgICpcbiAgICAgICAgICogIFRoaXMgY2Fubm90IGJlIGNvbnN0cnVjdGVkIGRpcmVjdGx5LCBidXQgcmF0aGVyIGJ5IEV1bGl0aC5Vbmlzd2FwLmdldFN3YXBQb29sICgpXG4gICAgICAgICAqXG4gICAgICAgICAqICBISVNUT1JJQ0FMIE5PVEU6IEJBU0VEIE9OIFBZVEhPTiBFdWxpdGhVbmlzd2FwVjNQb29sXG4gICAgICAgICAqL1xuICAgICAgICBleHBvcnQgY2xhc3MgUG9vbCB7XG4gICAgICAgICAgICBwcml2YXRlIHdlYjM6IEV1bGl0aC5XZWIzO1xuICAgICAgICAgICAgcHJpdmF0ZSBwb29sQWRkcmVzczogc3RyaW5nO1xuICAgICAgICAgICAgcHJpdmF0ZSBmZWVfPzogUG9vbEZlZTtcbiAgICAgICAgICAgIHByaXZhdGUgdG9rZW4wXzogRXVsaXRoLkNvbnRyYWN0cy5FUkMyMFRva2VuQ29udHJhY3Q7XG4gICAgICAgICAgICBwcml2YXRlIHRva2VuMV86IEV1bGl0aC5Db250cmFjdHMuRVJDMjBUb2tlbkNvbnRyYWN0O1xuICAgICAgICAgICAgcHJpdmF0ZSBjb250cmFjdF86IElVbmlzd2FwVjNQb29sO1xuICAgICAgICAgICAgcHJpdmF0ZSBsb2dnZXJfOiBFdWxpdGguTG9nZ2luZy5JTG9nZ2VyO1xuXG4gICAgICAgICAgICBwcml2YXRlIGNvbnN0cnVjdG9yKHsgd2ViMywgcG9vbEFkZHJlc3MsIGZlZSB9OiB7IHdlYjM6IEV1bGl0aC5XZWIzOyBwb29sQWRkcmVzczogc3RyaW5nOyBmZWU/OiBQb29sRmVlIH0pIHtcbiAgICAgICAgICAgICAgICB0aGlzLmZlZV8gPSBmZWU7XG4gICAgICAgICAgICAgICAgdGhpcy5wb29sQWRkcmVzcyA9IHBvb2xBZGRyZXNzO1xuICAgICAgICAgICAgICAgIHRoaXMud2ViMyA9IHdlYjM7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2dnZXJfID0gd2ViMy5sb2dnZXI7XG4gICAgICAgICAgICAgICAgdGhpcy5jb250cmFjdF8gPSBuZXcgd2ViMy5ldGguQ29udHJhY3QoXG4gICAgICAgICAgICAgICAgICAgIGtVbmlzd2FwVjNQb29sQUJJIGFzIGFueSBhcyBBYmlJdGVtW10sXG4gICAgICAgICAgICAgICAgICAgIHBvb2xBZGRyZXNzXG4gICAgICAgICAgICAgICAgKSBhcyBhbnkgYXMgSVVuaXN3YXBWM1Bvb2w7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICogQGNvbnN0cnVjdG9yXG4gICAgICAgICAgICAgKi9cbiAgICAgICAgICAgIHB1YmxpYyBzdGF0aWMgYXN5bmMgQXN5bmNDb25zdHJ1Y3Rvcih7XG4gICAgICAgICAgICAgICAgcHJvdmlkZXIsXG4gICAgICAgICAgICAgICAgcG9vbEFkZHJlc3MsXG4gICAgICAgICAgICAgICAgZmVlLFxuICAgICAgICAgICAgICAgIHRva2VuMEFkZHJlc3MsXG4gICAgICAgICAgICAgICAgdG9rZW4xQWRkcmVzc1xuICAgICAgICAgICAgfToge1xuICAgICAgICAgICAgICAgIHByb3ZpZGVyOiBFdWxpdGguV2ViMyB8IEV1bGl0aC5Qcm92aWRlcjtcbiAgICAgICAgICAgICAgICBwb29sQWRkcmVzczogc3RyaW5nO1xuICAgICAgICAgICAgICAgIGZlZT86IFBvb2xGZWU7XG4gICAgICAgICAgICAgICAgdG9rZW4wQWRkcmVzcz86IHN0cmluZztcbiAgICAgICAgICAgICAgICB0b2tlbjFBZGRyZXNzPzogc3RyaW5nO1xuICAgICAgICAgICAgfSk6IFByb21pc2U8UG9vbD4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IHVzZVByb3ZpZGVyID0gRXVsaXRoLlByb3ZpZGVyLlByb3ZpZGVyT3JXZWIzKHByb3ZpZGVyKTtcbiAgICAgICAgICAgICAgICBjb25zdCB1c2VXZWIzID0gbmV3IEV1bGl0aC5XZWIzKHsgcHJvdmlkZXI6IHVzZVByb3ZpZGVyIH0pO1xuXG4gICAgICAgICAgICAgICAgY29uc3QgdGhpc1Bvb2wgPSBuZXcgUG9vbCh7IHdlYjM6IHVzZVdlYjMsIHBvb2xBZGRyZXNzLCBmZWUgfSk7XG4gICAgICAgICAgICAgICAgdGhpc1Bvb2wudG9rZW4wXyA9IGF3YWl0IEV1bGl0aC5Db250cmFjdHMuRVJDMjBUb2tlbkNvbnRyYWN0Lm1rKHtcbiAgICAgICAgICAgICAgICAgICAgcHJvdmlkZXI6IHVzZVByb3ZpZGVyLFxuICAgICAgICAgICAgICAgICAgICBjb250cmFjdEFkZHJlc3M6IHRva2VuMEFkZHJlc3MgPz8gKGF3YWl0IHRoaXNQb29sLmNvbnRyYWN0Xy5tZXRob2RzLnRva2VuMCgpLmNhbGwoKSksXG4gICAgICAgICAgICAgICAgICAgIGRlY2ltYWxzOiAxOFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHRoaXNQb29sLnRva2VuMV8gPSBhd2FpdCBFdWxpdGguQ29udHJhY3RzLkVSQzIwVG9rZW5Db250cmFjdC5tayh7XG4gICAgICAgICAgICAgICAgICAgIHByb3ZpZGVyOiB1c2VQcm92aWRlcixcbiAgICAgICAgICAgICAgICAgICAgY29udHJhY3RBZGRyZXNzOiB0b2tlbjFBZGRyZXNzID8/IChhd2FpdCB0aGlzUG9vbC5jb250cmFjdF8ubWV0aG9kcy50b2tlbjEoKS5jYWxsKCkpLFxuICAgICAgICAgICAgICAgICAgICBkZWNpbWFsczogMThcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpc1Bvb2w7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICogIFRoaXMgcmV0dXJuIHRoZSBmZWUgKHBlcmNlbnRhZ2UpIGFzc29jaWF0ZWQgd2l0aCB0aGlzIHBvb2wgIC0gRXVsaXRoLlVuaXN3YXAuUG9vbEZlZVxuICAgICAgICAgICAgICpcbiAgICAgICAgICAgICAqICBUaG