@decaf-ts/for-fabric 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/LICENSE.md +22 -0
  2. package/README.md +647 -0
  3. package/dist/for-fabric.cjs +6223 -0
  4. package/dist/for-fabric.esm.cjs +6180 -0
  5. package/lib/client/FabricClientAdapter.cjs +760 -0
  6. package/lib/client/FabricClientAdapter.d.ts +381 -0
  7. package/lib/client/FabricClientDispatch.cjs +186 -0
  8. package/lib/client/FabricClientDispatch.d.ts +125 -0
  9. package/lib/client/FabricClientRepository.cjs +131 -0
  10. package/lib/client/FabricClientRepository.d.ts +100 -0
  11. package/lib/client/erc20/erc20ClientRepository.cjs +343 -0
  12. package/lib/client/erc20/erc20ClientRepository.d.ts +254 -0
  13. package/lib/client/fabric-fs.cjs +234 -0
  14. package/lib/client/fabric-fs.d.ts +92 -0
  15. package/lib/client/index.cjs +30 -0
  16. package/lib/client/index.d.ts +13 -0
  17. package/lib/client/logging.cjs +102 -0
  18. package/lib/client/logging.d.ts +60 -0
  19. package/lib/client/services/LoggedService.cjs +47 -0
  20. package/lib/client/services/LoggedService.d.ts +42 -0
  21. package/lib/client/services/constants.cjs +3 -0
  22. package/lib/client/services/constants.d.ts +15 -0
  23. package/lib/client/services/enrollementService.cjs +344 -0
  24. package/lib/client/services/enrollementService.d.ts +176 -0
  25. package/lib/client/services/index.cjs +18 -0
  26. package/lib/client/services/index.d.ts +1 -0
  27. package/lib/contracts/ContractAdapter.cjs +730 -0
  28. package/lib/contracts/ContractAdapter.d.ts +296 -0
  29. package/lib/contracts/ContractContext.cjs +85 -0
  30. package/lib/contracts/ContractContext.d.ts +64 -0
  31. package/lib/contracts/ContractPrivateDataAdapter.cjs +281 -0
  32. package/lib/contracts/ContractPrivateDataAdapter.d.ts +74 -0
  33. package/lib/contracts/FabricConstruction.cjs +441 -0
  34. package/lib/contracts/FabricConstruction.d.ts +304 -0
  35. package/lib/contracts/FabricContractRepository.cjs +306 -0
  36. package/lib/contracts/FabricContractRepository.d.ts +162 -0
  37. package/lib/contracts/FabricContractRepositoryObservableHandler.cjs +85 -0
  38. package/lib/contracts/FabricContractRepositoryObservableHandler.d.ts +62 -0
  39. package/lib/contracts/FabricContractSequence.cjs +139 -0
  40. package/lib/contracts/FabricContractSequence.d.ts +61 -0
  41. package/lib/contracts/FabricContractStatement.cjs +119 -0
  42. package/lib/contracts/FabricContractStatement.d.ts +34 -0
  43. package/lib/contracts/PrivateSequence.cjs +36 -0
  44. package/lib/contracts/PrivateSequence.d.ts +15 -0
  45. package/lib/contracts/crud/crud-contract.cjs +257 -0
  46. package/lib/contracts/crud/crud-contract.d.ts +168 -0
  47. package/lib/contracts/crud/index.cjs +19 -0
  48. package/lib/contracts/crud/index.d.ts +2 -0
  49. package/lib/contracts/crud/serialized-crud-contract.cjs +172 -0
  50. package/lib/contracts/crud/serialized-crud-contract.d.ts +37 -0
  51. package/lib/contracts/erc20/erc20contract.cjs +569 -0
  52. package/lib/contracts/erc20/erc20contract.d.ts +151 -0
  53. package/lib/contracts/erc20/index.cjs +21 -0
  54. package/lib/contracts/erc20/index.d.ts +2 -0
  55. package/lib/contracts/erc20/models.cjs +209 -0
  56. package/lib/contracts/erc20/models.d.ts +114 -0
  57. package/lib/contracts/index.cjs +32 -0
  58. package/lib/contracts/index.d.ts +15 -0
  59. package/lib/contracts/logging.cjs +96 -0
  60. package/lib/contracts/logging.d.ts +49 -0
  61. package/lib/contracts/private-data.cjs +121 -0
  62. package/lib/contracts/private-data.d.ts +16 -0
  63. package/lib/contracts/types.cjs +3 -0
  64. package/lib/contracts/types.d.ts +26 -0
  65. package/lib/esm/client/FabricClientAdapter.d.ts +381 -0
  66. package/lib/esm/client/FabricClientAdapter.js +723 -0
  67. package/lib/esm/client/FabricClientDispatch.d.ts +125 -0
  68. package/lib/esm/client/FabricClientDispatch.js +182 -0
  69. package/lib/esm/client/FabricClientRepository.d.ts +100 -0
  70. package/lib/esm/client/FabricClientRepository.js +127 -0
  71. package/lib/esm/client/erc20/erc20ClientRepository.d.ts +254 -0
  72. package/lib/esm/client/erc20/erc20ClientRepository.js +339 -0
  73. package/lib/esm/client/fabric-fs.d.ts +92 -0
  74. package/lib/esm/client/fabric-fs.js +191 -0
  75. package/lib/esm/client/index.d.ts +13 -0
  76. package/lib/esm/client/index.js +14 -0
  77. package/lib/esm/client/logging.d.ts +60 -0
  78. package/lib/esm/client/logging.js +98 -0
  79. package/lib/esm/client/services/LoggedService.d.ts +42 -0
  80. package/lib/esm/client/services/LoggedService.js +43 -0
  81. package/lib/esm/client/services/constants.d.ts +15 -0
  82. package/lib/esm/client/services/constants.js +2 -0
  83. package/lib/esm/client/services/enrollementService.d.ts +176 -0
  84. package/lib/esm/client/services/enrollementService.js +337 -0
  85. package/lib/esm/client/services/index.d.ts +1 -0
  86. package/lib/esm/client/services/index.js +2 -0
  87. package/lib/esm/contracts/ContractAdapter.d.ts +296 -0
  88. package/lib/esm/contracts/ContractAdapter.js +724 -0
  89. package/lib/esm/contracts/ContractContext.d.ts +64 -0
  90. package/lib/esm/contracts/ContractContext.js +81 -0
  91. package/lib/esm/contracts/ContractPrivateDataAdapter.d.ts +74 -0
  92. package/lib/esm/contracts/ContractPrivateDataAdapter.js +277 -0
  93. package/lib/esm/contracts/FabricConstruction.d.ts +304 -0
  94. package/lib/esm/contracts/FabricConstruction.js +433 -0
  95. package/lib/esm/contracts/FabricContractRepository.d.ts +162 -0
  96. package/lib/esm/contracts/FabricContractRepository.js +302 -0
  97. package/lib/esm/contracts/FabricContractRepositoryObservableHandler.d.ts +62 -0
  98. package/lib/esm/contracts/FabricContractRepositoryObservableHandler.js +81 -0
  99. package/lib/esm/contracts/FabricContractSequence.d.ts +61 -0
  100. package/lib/esm/contracts/FabricContractSequence.js +135 -0
  101. package/lib/esm/contracts/FabricContractStatement.d.ts +34 -0
  102. package/lib/esm/contracts/FabricContractStatement.js +115 -0
  103. package/lib/esm/contracts/PrivateSequence.d.ts +15 -0
  104. package/lib/esm/contracts/PrivateSequence.js +33 -0
  105. package/lib/esm/contracts/crud/crud-contract.d.ts +168 -0
  106. package/lib/esm/contracts/crud/crud-contract.js +253 -0
  107. package/lib/esm/contracts/crud/index.d.ts +2 -0
  108. package/lib/esm/contracts/crud/index.js +3 -0
  109. package/lib/esm/contracts/crud/serialized-crud-contract.d.ts +37 -0
  110. package/lib/esm/contracts/crud/serialized-crud-contract.js +168 -0
  111. package/lib/esm/contracts/erc20/erc20contract.d.ts +151 -0
  112. package/lib/esm/contracts/erc20/erc20contract.js +565 -0
  113. package/lib/esm/contracts/erc20/index.d.ts +2 -0
  114. package/lib/esm/contracts/erc20/index.js +4 -0
  115. package/lib/esm/contracts/erc20/models.d.ts +114 -0
  116. package/lib/esm/contracts/erc20/models.js +206 -0
  117. package/lib/esm/contracts/index.d.ts +15 -0
  118. package/lib/esm/contracts/index.js +16 -0
  119. package/lib/esm/contracts/logging.d.ts +49 -0
  120. package/lib/esm/contracts/logging.js +92 -0
  121. package/lib/esm/contracts/private-data.d.ts +16 -0
  122. package/lib/esm/contracts/private-data.js +113 -0
  123. package/lib/esm/contracts/types.d.ts +26 -0
  124. package/lib/esm/contracts/types.js +2 -0
  125. package/lib/esm/index.d.ts +8 -0
  126. package/lib/esm/index.js +9 -0
  127. package/lib/esm/shared/ClientSerializer.d.ts +52 -0
  128. package/lib/esm/shared/ClientSerializer.js +80 -0
  129. package/lib/esm/shared/DeterministicSerializer.d.ts +40 -0
  130. package/lib/esm/shared/DeterministicSerializer.js +50 -0
  131. package/lib/esm/shared/SimpleDeterministicSerializer.d.ts +7 -0
  132. package/lib/esm/shared/SimpleDeterministicSerializer.js +42 -0
  133. package/lib/esm/shared/constants.d.ts +39 -0
  134. package/lib/esm/shared/constants.js +42 -0
  135. package/lib/esm/shared/crypto.d.ts +107 -0
  136. package/lib/esm/shared/crypto.js +331 -0
  137. package/lib/esm/shared/decorators.d.ts +24 -0
  138. package/lib/esm/shared/decorators.js +98 -0
  139. package/lib/esm/shared/erc20/erc20-constants.d.ts +25 -0
  140. package/lib/esm/shared/erc20/erc20-constants.js +27 -0
  141. package/lib/esm/shared/errors.d.ts +116 -0
  142. package/lib/esm/shared/errors.js +132 -0
  143. package/lib/esm/shared/events.d.ts +39 -0
  144. package/lib/esm/shared/events.js +47 -0
  145. package/lib/esm/shared/fabric-types.d.ts +33 -0
  146. package/lib/esm/shared/fabric-types.js +2 -0
  147. package/lib/esm/shared/index.d.ts +13 -0
  148. package/lib/esm/shared/index.js +14 -0
  149. package/lib/esm/shared/interfaces/Checkable.d.ts +21 -0
  150. package/lib/esm/shared/interfaces/Checkable.js +2 -0
  151. package/lib/esm/shared/math.d.ts +34 -0
  152. package/lib/esm/shared/math.js +61 -0
  153. package/lib/esm/shared/model/Identity.d.ts +42 -0
  154. package/lib/esm/shared/model/Identity.js +78 -0
  155. package/lib/esm/shared/model/IdentityCredentials.d.ts +41 -0
  156. package/lib/esm/shared/model/IdentityCredentials.js +74 -0
  157. package/lib/esm/shared/model/index.d.ts +1 -0
  158. package/lib/esm/shared/model/index.js +2 -0
  159. package/lib/esm/shared/model/utils.d.ts +60 -0
  160. package/lib/esm/shared/model/utils.js +108 -0
  161. package/lib/esm/shared/types.d.ts +79 -0
  162. package/lib/esm/shared/types.js +2 -0
  163. package/lib/esm/shared/utils.d.ts +55 -0
  164. package/lib/esm/shared/utils.js +148 -0
  165. package/lib/index.cjs +25 -0
  166. package/lib/index.d.ts +8 -0
  167. package/lib/shared/ClientSerializer.cjs +84 -0
  168. package/lib/shared/ClientSerializer.d.ts +52 -0
  169. package/lib/shared/DeterministicSerializer.cjs +54 -0
  170. package/lib/shared/DeterministicSerializer.d.ts +40 -0
  171. package/lib/shared/SimpleDeterministicSerializer.cjs +46 -0
  172. package/lib/shared/SimpleDeterministicSerializer.d.ts +7 -0
  173. package/lib/shared/constants.cjs +45 -0
  174. package/lib/shared/constants.d.ts +39 -0
  175. package/lib/shared/crypto.cjs +369 -0
  176. package/lib/shared/crypto.d.ts +107 -0
  177. package/lib/shared/decorators.cjs +105 -0
  178. package/lib/shared/decorators.d.ts +24 -0
  179. package/lib/shared/erc20/erc20-constants.cjs +30 -0
  180. package/lib/shared/erc20/erc20-constants.d.ts +25 -0
  181. package/lib/shared/errors.cjs +142 -0
  182. package/lib/shared/errors.d.ts +116 -0
  183. package/lib/shared/events.cjs +51 -0
  184. package/lib/shared/events.d.ts +39 -0
  185. package/lib/shared/fabric-types.cjs +4 -0
  186. package/lib/shared/fabric-types.d.ts +33 -0
  187. package/lib/shared/index.cjs +30 -0
  188. package/lib/shared/index.d.ts +13 -0
  189. package/lib/shared/interfaces/Checkable.cjs +3 -0
  190. package/lib/shared/interfaces/Checkable.d.ts +21 -0
  191. package/lib/shared/math.cjs +66 -0
  192. package/lib/shared/math.d.ts +34 -0
  193. package/lib/shared/model/Identity.cjs +81 -0
  194. package/lib/shared/model/Identity.d.ts +42 -0
  195. package/lib/shared/model/IdentityCredentials.cjs +77 -0
  196. package/lib/shared/model/IdentityCredentials.d.ts +41 -0
  197. package/lib/shared/model/index.cjs +18 -0
  198. package/lib/shared/model/index.d.ts +1 -0
  199. package/lib/shared/model/utils.cjs +114 -0
  200. package/lib/shared/model/utils.d.ts +60 -0
  201. package/lib/shared/types.cjs +3 -0
  202. package/lib/shared/types.d.ts +79 -0
  203. package/lib/shared/utils.cjs +185 -0
  204. package/lib/shared/utils.d.ts +55 -0
  205. package/package.json +166 -0
@@ -0,0 +1,565 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import { AuthorizationError, Condition } from "@decaf-ts/core";
11
+ import { Context, Transaction } from "fabric-contract-api";
12
+ import { add, sub } from "./../../shared/math.js";
13
+ import { AllowanceError, BalanceError, NotInitializedError, } from "./../../shared/errors.js";
14
+ import { FabricContractAdapter } from "./../ContractAdapter.js";
15
+ import { Allowance, ERC20Token, ERC20Wallet } from "./models.js";
16
+ import { Owner } from "./../../shared/decorators.js";
17
+ import { FabricContractRepository } from "./../FabricContractRepository.js";
18
+ import { BaseError, InternalError, NotFoundError, ValidationError, } from "@decaf-ts/db-decorators";
19
+ import { FabricCrudContract } from "./../crud/crud-contract.js";
20
+ import { ERC20Events } from "./../../shared/erc20/erc20-constants.js";
21
+ /**
22
+ * @description ERC20 token contract base for Hyperledger Fabric
23
+ * @summary Implements ERC20-like token logic using repositories and adapters, providing standard token operations such as balance queries, transfers, approvals, minting and burning.
24
+ * @param {string} name - The contract name used to scope token identity
25
+ * @note https://eips.ethereum.org/EIPS/eip-20
26
+ * @return {void}
27
+ * @class FabricERC20Contract
28
+ * @example
29
+ * class MyTokenContract extends FabricERC20Contract {
30
+ * constructor() { super('MyToken'); }
31
+ * }
32
+ * // The contract exposes methods like Transfer, Approve, Mint, Burn, etc.
33
+ * @mermaid
34
+ * sequenceDiagram
35
+ * participant Client
36
+ * participant Contract
37
+ * participant WalletRepo
38
+ * participant TokenRepo
39
+ * participant Ledger
40
+ * Client->>Contract: Transfer(ctx, to, value)
41
+ * Contract->>WalletRepo: read(from)
42
+ * Contract->>WalletRepo: read(to)
43
+ * Contract->>Ledger: putState(updated balances)
44
+ * Contract-->>Client: success
45
+ */
46
+ export class FabricERC20Contract extends FabricCrudContract {
47
+ constructor(name) {
48
+ super(name, ERC20Wallet);
49
+ FabricERC20Contract.adapter =
50
+ FabricERC20Contract.adapter || new FabricContractAdapter();
51
+ this.walletRepository = FabricContractRepository.forModel(ERC20Wallet, FabricERC20Contract.adapter.alias);
52
+ this.tokenRepository = FabricContractRepository.forModel(ERC20Token, FabricERC20Contract.adapter.alias);
53
+ this.allowanceRepository = FabricContractRepository.forModel(Allowance, FabricERC20Contract.adapter.alias);
54
+ }
55
+ async TokenName(ctx) {
56
+ // Check contract options are already set first to execute the function
57
+ await this.CheckInitialized(ctx);
58
+ const select = await this.tokenRepository.selectWithContext(undefined, ctx);
59
+ const token = (await select.execute())[0];
60
+ return token.name;
61
+ }
62
+ /**
63
+ * Return the symbol of the token. E.g. “HIX”.
64
+ *
65
+ * @param {Context} ctx the transaction context
66
+ * @returns {String} Returns the symbol of the token
67
+ */
68
+ async Symbol(ctx) {
69
+ // Check contract options are already set first to execute the function
70
+ await this.CheckInitialized(ctx);
71
+ const select = await this.tokenRepository.selectWithContext(undefined, ctx);
72
+ const token = (await select.execute())[0];
73
+ return token.symbol;
74
+ }
75
+ /**
76
+ * Return the number of decimals the token uses
77
+ * e.g. 8, means to divide the token amount by 100000000 to get its user representation.
78
+ *
79
+ * @param {Context} ctx the transaction context
80
+ * @returns {Number} Returns the number of decimals
81
+ */
82
+ async Decimals(ctx) {
83
+ // Check contract options are already set first to execute the function
84
+ await this.CheckInitialized(ctx);
85
+ const select = await this.tokenRepository.selectWithContext(undefined, ctx);
86
+ const token = (await select.execute())[0];
87
+ return token.decimals;
88
+ }
89
+ /**
90
+ * Return the total token supply.
91
+ *
92
+ * @param {Context} ctx the transaction context
93
+ * @returns {Number} Returns the total token supply
94
+ */
95
+ async TotalSupply(ctx) {
96
+ // Check contract options are already set first to execute the function
97
+ await this.CheckInitialized(ctx);
98
+ const select = await this.walletRepository.selectWithContext(undefined, ctx);
99
+ const wallets = await select.execute();
100
+ if (wallets.length == 0) {
101
+ throw new NotFoundError(`The token ${this.getName()} does not exist`);
102
+ }
103
+ let total = 0;
104
+ wallets.forEach((wallet) => {
105
+ total += wallet.balance;
106
+ });
107
+ return total;
108
+ }
109
+ /**
110
+ * BalanceOf returns the balance of the given account.
111
+ *
112
+ * @param {Context} ctx the transaction context
113
+ * @param {String} owner The owner from which the balance will be retrieved
114
+ * @returns {Number} Returns the account balance
115
+ */
116
+ async BalanceOf(ctx, owner) {
117
+ // Check contract options are already set first to execute the function
118
+ await this.CheckInitialized(ctx);
119
+ const wallet = await this.walletRepository.read(owner, ctx);
120
+ return wallet.balance;
121
+ }
122
+ /**
123
+ * @summary Transfer transfers tokens from client account to recipient account.
124
+ * @description recipient account must be a valid clientID as returned by the ClientAccountID() function.
125
+ *
126
+ * @param {Context} ctx the transaction context
127
+ * @param {String} to The recipient
128
+ * @param {number} value The amount of token to be transferred
129
+ *
130
+ * @returns {Boolean} Return whether the transfer was successful or not
131
+ */
132
+ async Transfer(ctx, to, value) {
133
+ // Check contract options are already set first to execute the function
134
+ await this.CheckInitialized(ctx);
135
+ const from = ctx.clientIdentity.getID();
136
+ const transferResp = await this._transfer(ctx, from, to, value);
137
+ if (!transferResp) {
138
+ throw new InternalError("Failed to transfer");
139
+ }
140
+ return true;
141
+ }
142
+ /**
143
+ * Transfer `value` amount of tokens from `from` to `to`.
144
+ *
145
+ * @param {Context} ctx the transaction context
146
+ * @param {String} from The sender
147
+ * @param {String} to The recipient
148
+ * @param {number} value The amount of token to be transferred
149
+ * @returns {Boolean} Return whether the transfer was successful or not
150
+ */
151
+ async TransferFrom(ctx, from, to, value) {
152
+ // Check contract options are already set first to execute the function
153
+ await this.CheckInitialized(ctx);
154
+ // Retrieve the allowance of the spender
155
+ const spender = ctx.clientIdentity.getID();
156
+ const allowance = await this._getAllowance(ctx, from, spender);
157
+ if (!allowance || allowance.value < 0) {
158
+ throw new AllowanceError(`spender ${spender} has no allowance from ${from}`);
159
+ }
160
+ const currentAllowance = allowance.value;
161
+ // Check if the transferred value is less than the allowance
162
+ if (currentAllowance < value) {
163
+ throw new BalanceError("The spender does not have enough allowance to spend.");
164
+ }
165
+ // Decrease the allowance
166
+ const updatedAllowance = sub(currentAllowance, value);
167
+ const newAllowance = Object.assign({}, allowance, {
168
+ value: updatedAllowance,
169
+ });
170
+ await this.allowanceRepository.update(newAllowance, ctx);
171
+ //Realize the transfer
172
+ const transferResp = await this._transfer(ctx, from, to, value);
173
+ if (!transferResp) {
174
+ throw new InternalError("Failed to transfer");
175
+ }
176
+ return true;
177
+ }
178
+ async _transfer(ctx, from, to, value) {
179
+ const logger = this.logFor(ctx).for(this._transfer);
180
+ if (from === to) {
181
+ throw new AuthorizationError("cannot transfer to and from same client account");
182
+ }
183
+ if (value < 0) {
184
+ // transfer of 0 is allowed in ERC20, so just validate against negative amounts
185
+ throw new BalanceError("transfer amount cannot be negative");
186
+ }
187
+ // Retrieve the current balance of the sender
188
+ const fromWallet = await this.walletRepository.read(from, ctx);
189
+ const fromBalance = fromWallet.balance;
190
+ // Check if the sender has enough tokens to spend.
191
+ if (fromBalance < value) {
192
+ throw new BalanceError(`client account ${from} has insufficient funds.`);
193
+ }
194
+ // Retrieve the current balance of the recepient
195
+ let toWallet;
196
+ let newToWallet = false;
197
+ try {
198
+ toWallet = await this.walletRepository.read(to, ctx);
199
+ }
200
+ catch (e) {
201
+ if (e instanceof BaseError) {
202
+ if (e.code === 404) {
203
+ // Create a new wallet for the minter
204
+ toWallet = new ERC20Wallet({
205
+ id: to,
206
+ balance: 0,
207
+ token: await this.TokenName(ctx),
208
+ });
209
+ newToWallet = true;
210
+ }
211
+ else {
212
+ throw new InternalError(e.message);
213
+ }
214
+ }
215
+ else {
216
+ throw new InternalError(e);
217
+ }
218
+ }
219
+ const toBalance = toWallet.balance;
220
+ // Update the balance
221
+ const fromUpdatedBalance = sub(fromBalance, value);
222
+ const toUpdatedBalance = add(toBalance, value);
223
+ const updatedFromWallet = Object.assign({}, fromWallet, {
224
+ balance: fromUpdatedBalance,
225
+ });
226
+ await this.walletRepository.update(updatedFromWallet, ctx);
227
+ const updatedToWallet = Object.assign({}, toWallet, {
228
+ balance: toUpdatedBalance,
229
+ });
230
+ if (newToWallet) {
231
+ await this.walletRepository.create(updatedToWallet, ctx);
232
+ }
233
+ else {
234
+ await this.walletRepository.update(updatedToWallet, ctx);
235
+ }
236
+ // Emit the Transfer event
237
+ const transferEvent = { from, to, value: value };
238
+ const eventHandler = this.repo.ObserverHandler();
239
+ eventHandler.updateObservers(logger, "", ERC20Events.TRANSFER, "", ctx, "", transferEvent);
240
+ return true;
241
+ }
242
+ /**
243
+ * Allows `spender` to spend `value` amount of tokens from the owner. New Approve calls override the previous allowance.
244
+ * @note https://eips.ethereum.org/EIPS/eip-20
245
+ *
246
+ * @param {Context} ctx the transaction context
247
+ * @param {String} spender The spender
248
+ * @param {number} value The amount of tokens to be approved for transfer
249
+ * @returns {Boolean} Return whether the approval was successful or not
250
+ */
251
+ async Approve(ctx, spender, value) {
252
+ // Check contract options are already set first to execute the function
253
+ await this.CheckInitialized(ctx);
254
+ const logger = this.logFor(ctx).for(this.Approve);
255
+ const owner = ctx.clientIdentity.getID();
256
+ let allowance = await this._getAllowance(ctx, owner, spender);
257
+ const ownerWallet = await this.walletRepository.read(owner, ctx);
258
+ if (ownerWallet.balance < value) {
259
+ throw new BalanceError(`client account ${owner} has insufficient funds.`);
260
+ }
261
+ if (allowance) {
262
+ // Overwrite the allowance
263
+ allowance.value = value;
264
+ await this.allowanceRepository.update(allowance, ctx);
265
+ }
266
+ else {
267
+ allowance = new Allowance({
268
+ owner: owner,
269
+ spender: spender,
270
+ value: value,
271
+ });
272
+ await this.allowanceRepository.create(allowance, ctx);
273
+ }
274
+ // Emit the Approval event
275
+ const approvalEvent = { owner, spender, value: value };
276
+ const eventHandler = this.repo.ObserverHandler();
277
+ eventHandler.updateObservers(logger, "", ERC20Events.APPROVAL, "", ctx, "", approvalEvent);
278
+ return true;
279
+ }
280
+ /**
281
+ * Returns the amount of tokens which ` ` is allowed to withdraw from `owner`.
282
+ *
283
+ * @param {Context} ctx the transaction context
284
+ * @param {String} owner The owner of tokens
285
+ * @param {String} spender The spender who are able to transfer the tokens
286
+ * @returns {number} Return the amount of remaining tokens allowed to spent
287
+ */
288
+ async Allowance(ctx, owner, spender) {
289
+ // Check contract options are already set first to execute the function
290
+ await this.CheckInitialized(ctx);
291
+ const allowance = await this._getAllowance(ctx, owner, spender);
292
+ if (!allowance) {
293
+ throw new AllowanceError(`spender ${spender} has no allowance from ${owner}`);
294
+ }
295
+ return allowance.value;
296
+ }
297
+ async _getAllowance(ctx, owner, spender) {
298
+ const allowanceCondition = Condition.and(Condition.attribute("owner").eq(owner), Condition.attribute("spender").eq(spender));
299
+ const select = await this.allowanceRepository.selectWithContext(undefined, ctx);
300
+ const allowance = await select.where(allowanceCondition).execute();
301
+ return allowance?.[0];
302
+ }
303
+ // ================== Extended Functions ==========================
304
+ /**
305
+ * Set optional infomation for a token.
306
+ *
307
+ * @param {Context} ctx the transaction context
308
+ * @param {String} name The name of the token
309
+ * @param {String} symbol The symbol of the token
310
+ * @param {String} decimals The decimals of the token
311
+ * @param {String} totalSupply The totalSupply of the token
312
+ */
313
+ async Initialize(ctx, token) {
314
+ // Check contract options are not already set, client is not authorized to change them once intitialized
315
+ const select = await this.tokenRepository.selectWithContext(undefined, ctx);
316
+ const tokens = await select.execute();
317
+ if (tokens.length > 0) {
318
+ throw new AuthorizationError("contract options are already set, client is not authorized to change them");
319
+ }
320
+ token.owner = ctx.clientIdentity.getID();
321
+ await this.tokenRepository.create(token, ctx);
322
+ return true;
323
+ }
324
+ // Checks that contract options have been already initialized
325
+ async CheckInitialized(ctx) {
326
+ const select = await this.tokenRepository.selectWithContext(undefined, ctx);
327
+ const tokens = await select.execute();
328
+ if (tokens.length == 0) {
329
+ throw new NotInitializedError("contract options need to be set before calling any function, call Initialize() to initialize contract");
330
+ }
331
+ }
332
+ /**
333
+ * Mint creates new tokens and adds them to minter's account balance
334
+ *
335
+ * @param {Context} ctx the transaction context
336
+ * @param {number} amount amount of tokens to be minted
337
+ * @returns {Object} The balance
338
+ */
339
+ async Mint(ctx, amount) {
340
+ // Check contract options are already set first to execute the function
341
+ await this.CheckInitialized(ctx);
342
+ const logger = this.logFor(ctx).for(this.Mint);
343
+ // Get ID of submitting client identity
344
+ const minter = ctx.clientIdentity.getID();
345
+ if (amount <= 0) {
346
+ throw new ValidationError("mint amount must be a positive integer");
347
+ }
348
+ let minterWallet;
349
+ try {
350
+ minterWallet = await this.walletRepository.read(minter, ctx);
351
+ const currentBalance = minterWallet.balance;
352
+ const updatedBalance = add(currentBalance, amount);
353
+ const updatedminter = Object.assign({}, minterWallet, {
354
+ balance: updatedBalance,
355
+ });
356
+ await this.walletRepository.update(updatedminter, ctx);
357
+ }
358
+ catch (e) {
359
+ if (e instanceof BaseError) {
360
+ if (e.code === 404) {
361
+ // Create a new wallet for the minter
362
+ const newWallet = new ERC20Wallet({
363
+ id: minter,
364
+ balance: amount,
365
+ token: await this.TokenName(ctx),
366
+ });
367
+ await this.walletRepository.create(newWallet, ctx);
368
+ }
369
+ else {
370
+ throw new InternalError(e.message);
371
+ }
372
+ }
373
+ else {
374
+ throw new InternalError(e);
375
+ }
376
+ }
377
+ // Emit the Transfer event
378
+ const transferEvent = { from: "0x0", to: minter, value: amount };
379
+ const eventHandler = this.repo.ObserverHandler();
380
+ eventHandler.updateObservers(logger, "", ERC20Events.TRANSFER, "", ctx, "", transferEvent);
381
+ }
382
+ /**
383
+ * Burn redeem tokens from minter's account balance
384
+ *
385
+ * @param {Context} ctx the transaction context
386
+ * @param {number} amount amount of tokens to be burned
387
+ * @returns {Object} The balance
388
+ */
389
+ async Burn(ctx, amount) {
390
+ // Check contract options are already set first to execute the function
391
+ await this.CheckInitialized(ctx);
392
+ const logger = this.logFor(ctx).for(this.Burn);
393
+ const minter = ctx.clientIdentity.getID();
394
+ const minterWallet = await this.walletRepository.read(minter, ctx);
395
+ const currentBalance = minterWallet.balance;
396
+ if (currentBalance < amount) {
397
+ throw new BalanceError(`Minter has insufficient funds.`);
398
+ }
399
+ const updatedBalance = sub(currentBalance, amount);
400
+ const updatedminter = Object.assign({}, minterWallet, {
401
+ balance: updatedBalance,
402
+ });
403
+ await this.walletRepository.update(updatedminter, ctx);
404
+ logger.info(`${amount} tokens were burned`);
405
+ // Emit the Transfer event
406
+ const transferEvent = { from: minter, to: "0x0", value: amount };
407
+ const eventHandler = this.repo.ObserverHandler();
408
+ eventHandler.updateObservers(logger, "", ERC20Events.TRANSFER, "", ctx, "", transferEvent);
409
+ }
410
+ /**
411
+ * BurnFrom redeem tokens from account allowence and balance
412
+ *
413
+ * @param {Context} ctx the transaction context
414
+ * @param {number} account account from where tokens will be burned
415
+ * @param {number} amount amount of tokens to be burned
416
+ * @returns {Object} The balance
417
+ */
418
+ async BurnFrom(ctx, account, amount) {
419
+ // Check contract options are already set first to execute the function
420
+ await this.CheckInitialized(ctx);
421
+ const logger = this.logFor(ctx).for(this.BurnFrom);
422
+ const accountWallet = await this.walletRepository.read(account, ctx);
423
+ const currentBalance = accountWallet.balance;
424
+ if (currentBalance < amount) {
425
+ throw new BalanceError(`${account} has insufficient funds.`);
426
+ }
427
+ const updatedBalance = sub(currentBalance, amount);
428
+ const updatedaccount = Object.assign({}, accountWallet, {
429
+ balance: updatedBalance,
430
+ });
431
+ await this.walletRepository.update(updatedaccount, ctx);
432
+ logger.info(`${amount} tokens were berned from ${account}`);
433
+ // Emit the Transfer event
434
+ const transferEvent = { from: account, to: "0x0", value: amount };
435
+ const eventHandler = this.repo.ObserverHandler();
436
+ eventHandler.updateObservers(logger, "", ERC20Events.TRANSFER, "", ctx, "", transferEvent);
437
+ }
438
+ /**
439
+ * ClientAccountBalance returns the balance of the requesting client's account.
440
+ *
441
+ * @param {Context} ctx the transaction context
442
+ * @returns {Number} Returns the account balance
443
+ */
444
+ async ClientAccountBalance(ctx) {
445
+ // Check contract options are already set first to execute the function
446
+ await this.CheckInitialized(ctx);
447
+ // Get ID of submitting client identity
448
+ const clientAccountID = ctx.clientIdentity.getID();
449
+ const clientWallet = await this.walletRepository.read(clientAccountID, ctx);
450
+ if (!clientWallet) {
451
+ throw new BalanceError(`The account ${clientAccountID} does not exist`);
452
+ }
453
+ return clientWallet.balance;
454
+ }
455
+ // ClientAccountID returns the id of the requesting client's account.
456
+ // In this implementation, the client account ID is the clientId itself.
457
+ // Users can use this function to get their own account id, which they can then give to others as the payment address
458
+ async ClientAccountID(ctx) {
459
+ // Check contract options are already set first to execute the function
460
+ await this.CheckInitialized(ctx);
461
+ // Get ID of submitting client identity
462
+ const clientAccountID = ctx.clientIdentity.getID();
463
+ return clientAccountID;
464
+ }
465
+ }
466
+ __decorate([
467
+ Transaction(false),
468
+ __metadata("design:type", Function),
469
+ __metadata("design:paramtypes", [Context]),
470
+ __metadata("design:returntype", Promise)
471
+ ], FabricERC20Contract.prototype, "TokenName", null);
472
+ __decorate([
473
+ Transaction(false),
474
+ __metadata("design:type", Function),
475
+ __metadata("design:paramtypes", [Context]),
476
+ __metadata("design:returntype", Promise)
477
+ ], FabricERC20Contract.prototype, "Symbol", null);
478
+ __decorate([
479
+ Transaction(false),
480
+ __metadata("design:type", Function),
481
+ __metadata("design:paramtypes", [Context]),
482
+ __metadata("design:returntype", Promise)
483
+ ], FabricERC20Contract.prototype, "Decimals", null);
484
+ __decorate([
485
+ Transaction(false),
486
+ __metadata("design:type", Function),
487
+ __metadata("design:paramtypes", [Context]),
488
+ __metadata("design:returntype", Promise)
489
+ ], FabricERC20Contract.prototype, "TotalSupply", null);
490
+ __decorate([
491
+ Transaction(false),
492
+ __metadata("design:type", Function),
493
+ __metadata("design:paramtypes", [Context, String]),
494
+ __metadata("design:returntype", Promise)
495
+ ], FabricERC20Contract.prototype, "BalanceOf", null);
496
+ __decorate([
497
+ Transaction(),
498
+ __metadata("design:type", Function),
499
+ __metadata("design:paramtypes", [Context, String, Number]),
500
+ __metadata("design:returntype", Promise)
501
+ ], FabricERC20Contract.prototype, "Transfer", null);
502
+ __decorate([
503
+ Transaction(),
504
+ __metadata("design:type", Function),
505
+ __metadata("design:paramtypes", [Context, String, String, Number]),
506
+ __metadata("design:returntype", Promise)
507
+ ], FabricERC20Contract.prototype, "TransferFrom", null);
508
+ __decorate([
509
+ Transaction(),
510
+ __metadata("design:type", Function),
511
+ __metadata("design:paramtypes", [Context, String, Number]),
512
+ __metadata("design:returntype", Promise)
513
+ ], FabricERC20Contract.prototype, "Approve", null);
514
+ __decorate([
515
+ Transaction(),
516
+ __metadata("design:type", Function),
517
+ __metadata("design:paramtypes", [Context, String, String]),
518
+ __metadata("design:returntype", Promise)
519
+ ], FabricERC20Contract.prototype, "Allowance", null);
520
+ __decorate([
521
+ Transaction(),
522
+ __metadata("design:type", Function),
523
+ __metadata("design:paramtypes", [Context, ERC20Token]),
524
+ __metadata("design:returntype", Promise)
525
+ ], FabricERC20Contract.prototype, "Initialize", null);
526
+ __decorate([
527
+ Transaction(false),
528
+ __metadata("design:type", Function),
529
+ __metadata("design:paramtypes", [Context]),
530
+ __metadata("design:returntype", Promise)
531
+ ], FabricERC20Contract.prototype, "CheckInitialized", null);
532
+ __decorate([
533
+ Owner(),
534
+ Transaction(),
535
+ __metadata("design:type", Function),
536
+ __metadata("design:paramtypes", [Context, Number]),
537
+ __metadata("design:returntype", Promise)
538
+ ], FabricERC20Contract.prototype, "Mint", null);
539
+ __decorate([
540
+ Owner(),
541
+ Transaction(),
542
+ __metadata("design:type", Function),
543
+ __metadata("design:paramtypes", [Context, Number]),
544
+ __metadata("design:returntype", Promise)
545
+ ], FabricERC20Contract.prototype, "Burn", null);
546
+ __decorate([
547
+ Owner(),
548
+ Transaction(),
549
+ __metadata("design:type", Function),
550
+ __metadata("design:paramtypes", [Context, String, Number]),
551
+ __metadata("design:returntype", Promise)
552
+ ], FabricERC20Contract.prototype, "BurnFrom", null);
553
+ __decorate([
554
+ Transaction(),
555
+ __metadata("design:type", Function),
556
+ __metadata("design:paramtypes", [Context]),
557
+ __metadata("design:returntype", Promise)
558
+ ], FabricERC20Contract.prototype, "ClientAccountBalance", null);
559
+ __decorate([
560
+ Transaction(),
561
+ __metadata("design:type", Function),
562
+ __metadata("design:paramtypes", [Context]),
563
+ __metadata("design:returntype", Promise)
564
+ ], FabricERC20Contract.prototype, "ClientAccountID", null);
565
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXJjMjBjb250cmFjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb250cmFjdHMvZXJjMjAvZXJjMjBjb250cmFjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQSxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDL0QsT0FBTyxFQUFFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSwrQkFBMEI7QUFDN0MsT0FBTyxFQUNMLGNBQWMsRUFDZCxZQUFZLEVBQ1osbUJBQW1CLEdBQ3BCLGlDQUE0QjtBQUM3QixPQUFPLEVBQUUscUJBQXFCLEVBQUUsZ0NBQTJCO0FBQzNELE9BQU8sRUFBRSxTQUFTLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxvQkFBaUI7QUFDOUQsT0FBTyxFQUFFLEtBQUssRUFBRSxxQ0FBZ0M7QUFDaEQsT0FBTyxFQUFFLHdCQUF3QixFQUFFLHlDQUFvQztBQUN2RSxPQUFPLEVBQ0wsU0FBUyxFQUNULGFBQWEsRUFDYixhQUFhLEVBQ2IsZUFBZSxHQUNoQixNQUFNLHlCQUF5QixDQUFDO0FBQ2pDLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxtQ0FBOEI7QUFFM0QsT0FBTyxFQUFFLFdBQVcsRUFBRSxnREFBMkM7QUFFakU7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCRztBQUNILE1BQU0sT0FBZ0IsbUJBQW9CLFNBQVEsa0JBQStCO0lBTy9FLFlBQXNCLElBQVk7UUFDaEMsS0FBSyxDQUFDLElBQUksRUFBRSxXQUFXLENBQUMsQ0FBQztRQUV6QixtQkFBbUIsQ0FBQyxPQUFPO1lBQ3pCLG1CQUFtQixDQUFDLE9BQU8sSUFBSSxJQUFJLHFCQUFxQixFQUFFLENBQUM7UUFFN0QsSUFBSSxDQUFDLGdCQUFnQixHQUFHLHdCQUF3QixDQUFDLFFBQVEs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QUFXLENBQUMsS0FBSyxDQUFDOztxQ0FDRCxPQUFPOztpREFReEI7QUFVSztJQURMLFdBQVcsQ0FBQyxLQUFLLENBQUM7O3FDQUNDLE9BQU87O21EQVExQjtBQVNLO0lBREwsV0FBVyxDQUFDLEtBQUssQ0FBQzs7cUNBQ0ksT0FBTzs7c0RBcUI3QjtBQVVLO0lBREwsV0FBVyxDQUFDLEtBQUssQ0FBQzs7cUNBQ0UsT0FBTzs7b0RBTzNCO0FBYUs7SUFETCxXQUFXLEVBQUU7O3FDQUNNLE9BQU87O21EQVkxQjtBQVlLO0lBREwsV0FBVyxFQUFFOztxQ0FFUCxPQUFPOzt1REEyQ2I7QUFvR0s7SUFETCxXQUFXLEVBQUU7O3FDQUVQLE9BQU87O2tEQStDYjtBQVdLO0lBREwsV0FBVyxFQUFFOztxQ0FFUCxPQUFPOztvREFlYjtBQWdDSztJQURMLFdBQVcsRUFBRTs7cUNBQ1EsT0FBTyxFQUFTLFVBQVU7O3FEQWUvQztBQUlLO0lBREwsV0FBVyxDQUFDLEtBQUssQ0FBQzs7cUNBQ1MsT0FBTzs7MkRBUWxDO0FBV0s7SUFGTCxLQUFLLEVBQUU7SUFDUCxXQUFXLEVBQUU7O3FDQUNFLE9BQU87OytDQXlEdEI7QUFXSztJQUZMLEtBQUssRUFBRTtJQUNQLFdBQVcsRUFBRTs7cUNBQ0UsT0FBTzs7K0NBdUN0QjtBQVlLO0lBRkwsS0FBSyxFQUFFO0lBQ1AsV0FBVyxFQUFFOztxQ0FDTSxPQUFPOzttREFxQzFCO0FBU0s7SUFETCxXQUFXLEVBQUU7O3FDQUNrQixPQUFPOzsrREFjdEM7QUFNSztJQURMLFdBQVcsRUFBRTs7cUNBQ2EsT0FBTzs7MERBT2pDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQXV0aG9yaXphdGlvbkVycm9yLCBDb25kaXRpb24gfSBmcm9tIFwiQGRlY2FmLXRzL2NvcmVcIjtcbmltcG9ydCB7IENvbnRleHQsIFRyYW5zYWN0aW9uIH0gZnJvbSBcImZhYnJpYy1jb250cmFjdC1hcGlcIjtcbmltcG9ydCB7IGFkZCwgc3ViIH0gZnJvbSBcIi4uLy4uL3NoYXJlZC9tYXRoXCI7XG5pbXBvcnQge1xuICBBbGxvd2FuY2VFcnJvcixcbiAgQmFsYW5jZUVycm9yLFxuICBOb3RJbml0aWFsaXplZEVycm9yLFxufSBmcm9tIFwiLi4vLi4vc2hhcmVkL2Vycm9yc1wiO1xuaW1wb3J0IHsgRmFicmljQ29udHJhY3RBZGFwdGVyIH0gZnJvbSBcIi4uL0NvbnRyYWN0QWRhcHRlclwiO1xuaW1wb3J0IHsgQWxsb3dhbmNlLCBFUkMyMFRva2VuLCBFUkMyMFdhbGxldCB9IGZyb20gXCIuL21vZGVsc1wiO1xuaW1wb3J0IHsgT3duZXIgfSBmcm9tIFwiLi4vLi4vc2hhcmVkL2RlY29yYXRvcnNcIjtcbmltcG9ydCB7IEZhYnJpY0NvbnRyYWN0UmVwb3NpdG9yeSB9IGZyb20gXCIuLi9GYWJyaWNDb250cmFjdFJlcG9zaXRvcnlcIjtcbmltcG9ydCB7XG4gIEJhc2VFcnJvcixcbiAgSW50ZXJuYWxFcnJvcixcbiAgTm90Rm91bmRFcnJvcixcbiAgVmFsaWRhdGlvbkVycm9yLFxufSBmcm9tIFwiQGRlY2FmLXRzL2RiLWRlY29yYXRvcnNcIjtcbmltcG9ydCB7IEZhYnJpY0NydWRDb250cmFjdCB9IGZyb20gXCIuLi9jcnVkL2NydWQtY29udHJhY3RcIjtcbmltcG9ydCB7IEZhYnJpY0NvbnRyYWN0UmVwb3NpdG9yeU9ic2VydmFibGVIYW5kbGVyIH0gZnJvbSBcIi4uL0ZhYnJpY0NvbnRyYWN0UmVwb3NpdG9yeU9ic2VydmFibGVIYW5kbGVyXCI7XG5pbXBvcnQgeyBFUkMyMEV2ZW50cyB9IGZyb20gXCIuLi8uLi9zaGFyZWQvZXJjMjAvZXJjMjAtY29uc3RhbnRzXCI7XG5cbi8qKlxuICogQGRlc2NyaXB0aW9uIEVSQzIwIHRva2VuIGNvbnRyYWN0IGJhc2UgZm9yIEh5cGVybGVkZ2VyIEZhYnJpY1xuICogQHN1bW1hcnkgSW1wbGVtZW50cyBFUkMyMC1saWtlIHRva2VuIGxvZ2ljIHVzaW5nIHJlcG9zaXRvcmllcyBhbmQgYWRhcHRlcnMsIHByb3ZpZGluZyBzdGFuZGFyZCB0b2tlbiBvcGVyYXRpb25zIHN1Y2ggYXMgYmFsYW5jZSBxdWVyaWVzLCB0cmFuc2ZlcnMsIGFwcHJvdmFscywgbWludGluZyBhbmQgYnVybmluZy5cbiAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIC0gVGhlIGNvbnRyYWN0IG5hbWUgdXNlZCB0byBzY29wZSB0b2tlbiBpZGVudGl0eVxuICogQG5vdGUgaHR0cHM6Ly9laXBzLmV0aGVyZXVtLm9yZy9FSVBTL2VpcC0yMFxuICogQHJldHVybiB7dm9pZH1cbiAqIEBjbGFzcyBGYWJyaWNFUkMyMENvbnRyYWN0XG4gKiBAZXhhbXBsZVxuICogY2xhc3MgTXlUb2tlbkNvbnRyYWN0IGV4dGVuZHMgRmFicmljRVJDMjBDb250cmFjdCB7XG4gKiAgIGNvbnN0cnVjdG9yKCkgeyBzdXBlcignTXlUb2tlbicpOyB9XG4gKiB9XG4gKiAvLyBUaGUgY29udHJhY3QgZXhwb3NlcyBtZXRob2RzIGxpa2UgVHJhbnNmZXIsIEFwcHJvdmUsIE1pbnQsIEJ1cm4sIGV0Yy5cbiAqIEBtZXJtYWlkXG4gKiBzZXF1ZW5jZURpYWdyYW1cbiAqICAgcGFydGljaXBhbnQgQ2xpZW50XG4gKiAgIHBhcnRpY2lwYW50IENvbnRyYWN0XG4gKiAgIHBhcnRpY2lwYW50IFdhbGxldFJlcG9cbiAqICAgcGFydGljaXBhbnQgVG9rZW5SZXBvXG4gKiAgIHBhcnRpY2lwYW50IExlZGdlclxuICogICBDbGllbnQtPj5Db250cmFjdDogVHJhbnNmZXIoY3R4LCB0bywgdmFsdWUpXG4gKiAgIENvbnRyYWN0LT4+V2FsbGV0UmVwbzogcmVhZChmcm9tKVxuICogICBDb250cmFjdC0+PldhbGxldFJlcG86IHJlYWQodG8pXG4gKiAgIENvbnRyYWN0LT4+TGVkZ2VyOiBwdXRTdGF0ZSh1cGRhdGVkIGJhbGFuY2VzKVxuICogICBDb250cmFjdC0tPj5DbGllbnQ6IHN1Y2Nlc3NcbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEZhYnJpY0VSQzIwQ29udHJhY3QgZXh0ZW5kcyBGYWJyaWNDcnVkQ29udHJhY3Q8RVJDMjBXYWxsZXQ+IHtcbiAgcHJpdmF0ZSB3YWxsZXRSZXBvc2l0b3J5OiBGYWJyaWNDb250cmFjdFJlcG9zaXRvcnk8RVJDMjBXYWxsZXQ+O1xuXG4gIHByaXZhdGUgdG9rZW5SZXBvc2l0b3J5OiBGYWJyaWNDb250cmFjdFJlcG9zaXRvcnk8RVJDMjBUb2tlbj47XG5cbiAgcHJpdmF0ZSBhbGxvd2FuY2VSZXBvc2l0b3J5OiBGYWJyaWNDb250cmFjdFJlcG9zaXRvcnk8QWxsb3dhbmNlPjtcblxuICBwcm90ZWN0ZWQgY29uc3RydWN0b3IobmFtZTogc3RyaW5nKSB7XG4gICAgc3VwZXIobmFtZSwgRVJDMjBXYWxsZXQpO1xuXG4gICAgRmFicmljRVJDMjBDb250cmFjdC5hZGFwdGVyID1cbiAgICAgIEZhYnJpY0VSQzIwQ29udHJhY3QuYWRhcHRlciB8fCBuZXcgRmFicmljQ29udHJhY3RBZGFwdGVyKCk7XG5cbiAgICB0aGlzLndhbGxldFJlcG9zaXRvcnkgPSBGYWJyaWNDb250cmFjdFJlcG9zaXRvcnkuZm9yTW9kZWwoXG4gICAgICBFUkMyMFdhbGxldCxcbiAgICAgIEZhYnJpY0VSQzIwQ29udHJhY3QuYWRhcHRlci5hbGlhc1xuICAgICk7XG5cbiAgICB0aGlzLnRva2VuUmVwb3NpdG9yeSA9IEZhYnJpY0NvbnRyYWN0UmVwb3NpdG9yeS5mb3JNb2RlbChcbiAgICAgIEVSQzIwVG9rZW4sXG4gICAgICBGYWJyaWNFUkMyMENvbnRyYWN0LmFkYXB0ZXIuYWxpYXNcbiAgICApO1xuXG4gICAgdGhpcy5hbGxvd2FuY2VSZXBvc2l0b3J5ID0gRmFicmljQ29udHJhY3RSZXBvc2l0b3J5LmZvck1vZGVsKFxuICAgICAgQWxsb3dhbmNlLFxuICAgICAgRmFicmljRVJDMjBDb250cmFjdC5hZGFwdGVyLmFsaWFzXG4gICAgKTtcbiAgfVxuXG4gIEBUcmFuc2FjdGlvbihmYWxzZSlcbiAgYXN5bmMgVG9rZW5OYW1lKGN0eDogQ29udGV4dCk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgLy8gQ2hlY2sgY29udHJhY3Qgb3B0aW9ucyBhcmUgYWxyZWFkeSBzZXQgZmlyc3QgdG8gZXhlY3V0ZSB0aGUgZnVuY3Rpb25cbiAgICBhd2FpdCB0aGlzLkNoZWNrSW5pdGlhbGl6ZWQoY3R4KTtcblxuICAgIGNvbnN0IHNlbGVjdCA9IGF3YWl0IHRoaXMudG9rZW5SZXBvc2l0b3J5LnNlbGVjdFdpdGhDb250ZXh0KHVuZGVmaW5lZCwgY3R4KTtcbiAgICBjb25zdCB0b2tlbiA9IChhd2FpdCBzZWxlY3QuZXhlY3V0ZSgpKVswXTtcblxuICAgIHJldHVybiB0b2tlbi5uYW1lO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgc3ltYm9sIG9mIHRoZSB0b2tlbi4gRS5nLiDigJxISVjigJ0uXG4gICAqXG4gICAqIEBwYXJhbSB7Q29udGV4dH0gY3R4IHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0XG4gICAqIEByZXR1cm5zIHtTdHJpbmd9IFJldHVybnMgdGhlIHN5bWJvbCBvZiB0aGUgdG9rZW5cbiAgICovXG4gIEBUcmFuc2FjdGlvbihmYWxzZSlcbiAgYXN5bmMgU3ltYm9sKGN0eDogQ29udGV4dCk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgLy8gQ2hlY2sgY29udHJhY3Qgb3B0aW9ucyBhcmUgYWxyZWFkeSBzZXQgZmlyc3QgdG8gZXhlY3V0ZSB0aGUgZnVuY3Rpb25cbiAgICBhd2FpdCB0aGlzLkNoZWNrSW5pdGlhbGl6ZWQoY3R4KTtcblxuICAgIGNvbnN0IHNlbGVjdCA9IGF3YWl0IHRoaXMudG9rZW5SZXBvc2l0b3J5LnNlbGVjdFdpdGhDb250ZXh0KHVuZGVmaW5lZCwgY3R4KTtcbiAgICBjb25zdCB0b2tlbiA9IChhd2FpdCBzZWxlY3QuZXhlY3V0ZSgpKVswXTtcblxuICAgIHJldHVybiB0b2tlbi5zeW1ib2w7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJuIHRoZSBudW1iZXIgb2YgZGVjaW1hbHMgdGhlIHRva2VuIHVzZXNcbiAgICogZS5nLiA4LCBtZWFucyB0byBkaXZpZGUgdGhlIHRva2VuIGFtb3VudCBieSAxMDAwMDAwMDAgdG8gZ2V0IGl0cyB1c2VyIHJlcHJlc2VudGF0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0ge0NvbnRleHR9IGN0eCB0aGUgdHJhbnNhY3Rpb24gY29udGV4dFxuICAgKiBAcmV0dXJucyB7TnVtYmVyfSBSZXR1cm5zIHRoZSBudW1iZXIgb2YgZGVjaW1hbHNcbiAgICovXG4gIEBUcmFuc2FjdGlvbihmYWxzZSlcbiAgYXN5bmMgRGVjaW1hbHMoY3R4OiBDb250ZXh0KTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICAvLyBDaGVjayBjb250cmFjdCBvcHRpb25zIGFyZSBhbHJlYWR5IHNldCBmaXJzdCB0byBleGVjdXRlIHRoZSBmdW5jdGlvblxuICAgIGF3YWl0IHRoaXMuQ2hlY2tJbml0aWFsaXplZChjdHgpO1xuXG4gICAgY29uc3Qgc2VsZWN0ID0gYXdhaXQgdGhpcy50b2tlblJlcG9zaXRvcnkuc2VsZWN0V2l0aENvbnRleHQodW5kZWZpbmVkLCBjdHgpO1xuICAgIGNvbnN0IHRva2VuID0gKGF3YWl0IHNlbGVjdC5leGVjdXRlKCkpWzBdO1xuXG4gICAgcmV0dXJuIHRva2VuLmRlY2ltYWxzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybiB0aGUgdG90YWwgdG9rZW4gc3VwcGx5LlxuICAgKlxuICAgKiBAcGFyYW0ge0NvbnRleHR9IGN0eCB0aGUgdHJhbnNhY3Rpb24gY29udGV4dFxuICAgKiBAcmV0dXJucyB7TnVtYmVyfSBSZXR1cm5zIHRoZSB0b3RhbCB0b2tlbiBzdXBwbHlcbiAgICovXG4gIEBUcmFuc2FjdGlvbihmYWxzZSlcbiAgYXN5bmMgVG90YWxTdXBwbHkoY3R4OiBDb250ZXh0KTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICAvLyBDaGVjayBjb250cmFjdCBvcHRpb25zIGFyZSBhbHJlYWR5IHNldCBmaXJzdCB0byBleGVjdXRlIHRoZSBmdW5jdGlvblxuICAgIGF3YWl0IHRoaXMuQ2hlY2tJbml0aWFsaXplZChjdHgpO1xuXG4gICAgY29uc3Qgc2VsZWN0ID0gYXdhaXQgdGhpcy53YWxsZXRSZXBvc2l0b3J5LnNlbGVjdFdpdGhDb250ZXh0KFxuICAgICAgdW5kZWZpbmVkLFxuICAgICAgY3R4XG4gICAgKTtcbiAgICBjb25zdCB3YWxsZXRzID0gYXdhaXQgc2VsZWN0LmV4ZWN1dGUoKTtcblxuICAgIGlmICh3YWxsZXRzLmxlbmd0aCA9PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgTm90Rm91bmRFcnJvcihgVGhlIHRva2VuICR7dGhpcy5nZXROYW1lKCl9IGRvZXMgbm90IGV4aXN0YCk7XG4gICAgfVxuXG4gICAgbGV0IHRvdGFsID0gMDtcblxuICAgIHdhbGxldHMuZm9yRWFjaCgod2FsbGV0KSA9PiB7XG4gICAgICB0b3RhbCArPSB3YWxsZXQuYmFsYW5jZTtcbiAgICB9KTtcblxuICAgIHJldHVybiB0b3RhbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBCYWxhbmNlT2YgcmV0dXJucyB0aGUgYmFsYW5jZSBvZiB0aGUgZ2l2ZW4gYWNjb3VudC5cbiAgICpcbiAgICogQHBhcmFtIHtDb250ZXh0fSBjdHggdGhlIHRyYW5zYWN0aW9uIGNvbnRleHRcbiAgICogQHBhcmFtIHtTdHJpbmd9IG93bmVyIFRoZSBvd25lciBmcm9tIHdoaWNoIHRoZSBiYWxhbmNlIHdpbGwgYmUgcmV0cmlldmVkXG4gICAqIEByZXR1cm5zIHtOdW1iZXJ9IFJldHVybnMgdGhlIGFjY291bnQgYmFsYW5jZVxuICAgKi9cbiAgQFRyYW5zYWN0aW9uKGZhbHNlKVxuICBhc3luYyBCYWxhbmNlT2YoY3R4OiBDb250ZXh0LCBvd25lcjogc3RyaW5nKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICAvLyBDaGVjayBjb250cmFjdCBvcHRpb25zIGFyZSBhbHJlYWR5IHNldCBmaXJzdCB0byBleGVjdXRlIHRoZSBmdW5jdGlvblxuICAgIGF3YWl0IHRoaXMuQ2hlY2tJbml0aWFsaXplZChjdHgpO1xuXG4gICAgY29uc3Qgd2FsbGV0ID0gYXdhaXQgdGhpcy53YWxsZXRSZXBvc2l0b3J5LnJlYWQob3duZXIsIGN0eCk7XG5cbiAgICByZXR1cm4gd2FsbGV0LmJhbGFuY2U7XG4gIH1cblxuICAvKipcbiAgICogQHN1bW1hcnkgVHJhbnNmZXIgdHJhbnNmZXJzIHRva2VucyBmcm9tIGNsaWVudCBhY2NvdW50IHRvIHJlY2lwaWVudCBhY2NvdW50LlxuICAgKiBAZGVzY3JpcHRpb24gcmVjaXBpZW50IGFjY291bnQgbXVzdCBiZSBhIHZhbGlkIGNsaWVudElEIGFzIHJldHVybmVkIGJ5IHRoZSBDbGllbnRBY2NvdW50SUQoKSBmdW5jdGlvbi5cbiAgICpcbiAgICogQHBhcmFtIHtDb250ZXh0fSBjdHggdGhlIHRyYW5zYWN0aW9uIGNvbnRleHRcbiAgICogQHBhcmFtIHtTdHJpbmd9IHRvIFRoZSByZWNpcGllbnRcbiAgICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIFRoZSBhbW91bnQgb2YgdG9rZW4gdG8gYmUgdHJhbnNmZXJyZWRcbiAgICpcbiAgICogQHJldHVybnMge0Jvb2xlYW59IFJldHVybiB3aGV0aGVyIHRoZSB0cmFuc2ZlciB3YXMgc3VjY2Vzc2Z1bCBvciBub3RcbiAgICovXG4gIEBUcmFuc2FjdGlvbigpXG4gIGFzeW5jIFRyYW5zZmVyKGN0eDogQ29udGV4dCwgdG86IHN0cmluZywgdmFsdWU6IG51bWJlcik6IFByb21pc2U8Ym9vbGVhbj4ge1xuICAgIC8vIENoZWNrIGNvbnRyYWN0IG9wdGlvbnMgYXJlIGFscmVhZHkgc2V0IGZpcnN0IHRvIGV4ZWN1dGUgdGhlIGZ1bmN0aW9uXG4gICAgYXdhaXQgdGhpcy5DaGVja0luaXRpYWxpemVkKGN0eCk7XG5cbiAgICBjb25zdCBmcm9tID0gY3R4LmNsaWVudElkZW50aXR5LmdldElEKCk7XG5cbiAgICBjb25zdCB0cmFuc2ZlclJlc3AgPSBhd2FpdCB0aGlzLl90cmFuc2ZlcihjdHgsIGZyb20sIHRvLCB2YWx1ZSk7XG4gICAgaWYgKCF0cmFuc2ZlclJlc3ApIHtcbiAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKFwiRmFpbGVkIHRvIHRyYW5zZmVyXCIpO1xuICAgIH1cblxuICAgIHJldHVybiB0cnVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYW5zZmVyIGB2YWx1ZWAgYW1vdW50IG9mIHRva2VucyBmcm9tIGBmcm9tYCB0byBgdG9gLlxuICAgKlxuICAgKiBAcGFyYW0ge0NvbnRleHR9IGN0eCB0aGUgdHJhbnNhY3Rpb24gY29udGV4dFxuICAgKiBAcGFyYW0ge1N0cmluZ30gZnJvbSBUaGUgc2VuZGVyXG4gICAqIEBwYXJhbSB7U3RyaW5nfSB0byBUaGUgcmVjaXBpZW50XG4gICAqIEBwYXJhbSB7bnVtYmVyfSB2YWx1ZSBUaGUgYW1vdW50IG9mIHRva2VuIHRvIGJlIHRyYW5zZmVycmVkXG4gICAqIEByZXR1cm5zIHtCb29sZWFufSBSZXR1cm4gd2hldGhlciB0aGUgdHJhbnNmZXIgd2FzIHN1Y2Nlc3NmdWwgb3Igbm90XG4gICAqL1xuICBAVHJhbnNhY3Rpb24oKVxuICBhc3luYyBUcmFuc2ZlckZyb20oXG4gICAgY3R4OiBDb250ZXh0LFxuICAgIGZyb206IHN0cmluZyxcbiAgICB0bzogc3RyaW5nLFxuICAgIHZhbHVlOiBudW1iZXJcbiAgKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgLy8gQ2hlY2sgY29udHJhY3Qgb3B0aW9ucyBhcmUgYWxyZWFkeSBzZXQgZmlyc3QgdG8gZXhlY3V0ZSB0aGUgZnVuY3Rpb25cbiAgICBhd2FpdCB0aGlzLkNoZWNrSW5pdGlhbGl6ZWQoY3R4KTtcblxuICAgIC8vIFJldHJpZXZlIHRoZSBhbGxvd2FuY2Ugb2YgdGhlIHNwZW5kZXJcblxuICAgIGNvbnN0IHNwZW5kZXIgPSBjdHguY2xpZW50SWRlbnRpdHkuZ2V0SUQoKTtcblxuICAgIGNvbnN0IGFsbG93YW5jZSA9IGF3YWl0IHRoaXMuX2dldEFsbG93YW5jZShjdHgsIGZyb20sIHNwZW5kZXIpO1xuICAgIGlmICghYWxsb3dhbmNlIHx8IGFsbG93YW5jZS52YWx1ZSA8IDApIHtcbiAgICAgIHRocm93IG5ldyBBbGxvd2FuY2VFcnJvcihcbiAgICAgICAgYHNwZW5kZXIgJHtzcGVuZGVyfSBoYXMgbm8gYWxsb3dhbmNlIGZyb20gJHtmcm9tfWBcbiAgICAgICk7XG4gICAgfVxuXG4gICAgY29uc3QgY3VycmVudEFsbG93YW5jZSA9IGFsbG93YW5jZS52YWx1ZTtcblxuICAgIC8vIENoZWNrIGlmIHRoZSB0cmFuc2ZlcnJlZCB2YWx1ZSBpcyBsZXNzIHRoYW4gdGhlIGFsbG93YW5jZVxuICAgIGlmIChjdXJyZW50QWxsb3dhbmNlIDwgdmFsdWUpIHtcbiAgICAgIHRocm93IG5ldyBCYWxhbmNlRXJyb3IoXG4gICAgICAgIFwiVGhlIHNwZW5kZXIgZG9lcyBub3QgaGF2ZSBlbm91Z2ggYWxsb3dhbmNlIHRvIHNwZW5kLlwiXG4gICAgICApO1xuICAgIH1cblxuICAgIC8vIERlY3JlYXNlIHRoZSBhbGxvd2FuY2VcbiAgICBjb25zdCB1cGRhdGVkQWxsb3dhbmNlID0gc3ViKGN1cnJlbnRBbGxvd2FuY2UsIHZhbHVlKTtcbiAgICBjb25zdCBuZXdBbGxvd2FuY2UgPSBPYmplY3QuYXNzaWduKHt9LCBhbGxvd2FuY2UsIHtcbiAgICAgIHZhbHVlOiB1cGRhdGVkQWxsb3dhbmNlLFxuICAgIH0pO1xuXG4gICAgYXdhaXQgdGhpcy5hbGxvd2FuY2VSZXBvc2l0b3J5LnVwZGF0ZShuZXdBbGxvd2FuY2UsIGN0eCk7XG5cbiAgICAvL1JlYWxpemUgdGhlIHRyYW5zZmVyXG4gICAgY29uc3QgdHJhbnNmZXJSZXNwID0gYXdhaXQgdGhpcy5fdHJhbnNmZXIoY3R4LCBmcm9tLCB0bywgdmFsdWUpO1xuICAgIGlmICghdHJhbnNmZXJSZXNwKSB7XG4gICAgICB0aHJvdyBuZXcgSW50ZXJuYWxFcnJvcihcIkZhaWxlZCB0byB0cmFuc2ZlclwiKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIGFzeW5jIF90cmFuc2ZlcihjdHg6IENvbnRleHQsIGZyb206IHN0cmluZywgdG86IHN0cmluZywgdmFsdWU6IG51bWJlcikge1xuICAgIGNvbnN0IGxvZ2dlciA9IHRoaXMubG9nRm9yKGN0eCkuZm9yKHRoaXMuX3RyYW5zZmVyKTtcblxuICAgIGlmIChmcm9tID09PSB0bykge1xuICAgICAgdGhyb3cgbmV3IEF1dGhvcml6YXRpb25FcnJvcihcbiAgICAgICAgXCJjYW5ub3QgdHJhbnNmZXIgdG8gYW5kIGZyb20gc2FtZSBjbGllbnQgYWNjb3VudFwiXG4gICAgICApO1xuICAgIH1cblxuICAgIGlmICh2YWx1ZSA8IDApIHtcbiAgICAgIC8vIHRyYW5zZmVyIG9mIDAgaXMgYWxsb3dlZCBpbiBFUkMyMCwgc28ganVzdCB2YWxpZGF0ZSBhZ2FpbnN0IG5lZ2F0aXZlIGFtb3VudHNcbiAgICAgIHRocm93IG5ldyBCYWxhbmNlRXJyb3IoXCJ0cmFuc2ZlciBhbW91bnQgY2Fubm90IGJlIG5lZ2F0aXZlXCIpO1xuICAgIH1cblxuICAgIC8vIFJldHJpZXZlIHRoZSBjdXJyZW50IGJhbGFuY2Ugb2YgdGhlIHNlbmRlclxuXG4gICAgY29uc3QgZnJvbVdhbGxldCA9IGF3YWl0IHRoaXMud2FsbGV0UmVwb3NpdG9yeS5yZWFkKGZyb20sIGN0eCk7XG5cbiAgICBjb25zdCBmcm9tQmFsYW5jZSA9IGZyb21XYWxsZXQuYmFsYW5jZTtcblxuICAgIC8vIENoZWNrIGlmIHRoZSBzZW5kZXIgaGFzIGVub3VnaCB0b2tlbnMgdG8gc3BlbmQuXG4gICAgaWYgKGZyb21CYWxhbmNlIDwgdmFsdWUpIHtcbiAgICAgIHRocm93IG5ldyBCYWxhbmNlRXJyb3IoYGNsaWVudCBhY2NvdW50ICR7ZnJvbX0gaGFzIGluc3VmZmljaWVudCBmdW5kcy5gKTtcbiAgICB9XG5cbiAgICAvLyBSZXRyaWV2ZSB0aGUgY3VycmVudCBiYWxhbmNlIG9mIHRoZSByZWNlcGllbnRcblxuICAgIGxldCB0b1dhbGxldDogRVJDMjBXYWxsZXQ7XG4gICAgbGV0IG5ld1RvV2FsbGV0OiBib29sZWFuID0gZmFsc2U7XG4gICAgdHJ5IHtcbiAgICAgIHRvV2FsbGV0ID0gYXdhaXQgdGhpcy53YWxsZXRSZXBvc2l0b3J5LnJlYWQodG8sIGN0eCk7XG4gICAgfSBjYXRjaCAoZTogdW5rbm93bikge1xuICAgICAgaWYgKGUgaW5zdGFuY2VvZiBCYXNlRXJyb3IpIHtcbiAgICAgICAgaWYgKGUuY29kZSA9PT0gNDA0KSB7XG4gICAgICAgICAgLy8gQ3JlYXRlIGEgbmV3IHdhbGxldCBmb3IgdGhlIG1pbnRlclxuICAgICAgICAgIHRvV2FsbGV0ID0gbmV3IEVSQzIwV2FsbGV0KHtcbiAgICAgICAgICAgIGlkOiB0byxcbiAgICAgICAgICAgIGJhbGFuY2U6IDAsXG4gICAgICAgICAgICB0b2tlbjogYXdhaXQgdGhpcy5Ub2tlbk5hbWUoY3R4KSxcbiAgICAgICAgICB9KTtcbiAgICAgICAgICBuZXdUb1dhbGxldCA9IHRydWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoZS5tZXNzYWdlKTtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgdGhyb3cgbmV3IEludGVybmFsRXJyb3IoZSBhcyBzdHJpbmcpO1xuICAgICAgfVxuICAgIH1cblxuICAgIGNvbnN0IHRvQmFsYW5jZSA9IHRvV2FsbGV0LmJhbGFuY2U7XG5cbiAgICAvLyBVcGRhdGUgdGhlIGJhbGFuY2VcbiAgICBjb25zdCBmcm9tVXBkYXRlZEJhbGFuY2UgPSBzdWIoZnJvbUJhbGFuY2UsIHZhbHVlKTtcbiAgICBjb25zdCB0b1VwZGF0ZWRCYWxhbmNlID0gYWRkKHRvQmFsYW5jZSwgdmFsdWUpO1xuXG4gICAgY29uc3QgdXBkYXRlZEZyb21XYWxsZXQgPSBPYmplY3QuYXNzaWduKHt9LCBmcm9tV2FsbGV0LCB7XG4gICAgICBiYWxhbmNlOiBmcm9tVXBkYXRlZEJhbGFuY2UsXG4gICAgfSk7XG5cbiAgICBhd2FpdCB0aGlzLndhbGxldFJlcG9zaXRvcnkudXBkYXRlKHVwZGF0ZWRGcm9tV2FsbGV0LCBjdHgpO1xuXG4gICAgY29uc3QgdXBkYXRlZFRvV2FsbGV0ID0gT2JqZWN0LmFzc2lnbih7fSwgdG9XYWxsZXQsIHtcbiAgICAgIGJhbGFuY2U6IHRvVXBkYXRlZEJhbGFuY2UsXG4gICAgfSk7XG5cbiAgICBpZiAobmV3VG9XYWxsZXQpIHtcbiAgICAgIGF3YWl0IHRoaXMud2FsbGV0UmVwb3NpdG9yeS5jcmVhdGUodXBkYXRlZFRvV2FsbGV0LCBjdHgpO1xuICAgIH0gZWxzZSB7XG4gICAgICBhd2FpdCB0aGlzLndhbGxldFJlcG9zaXRvcnkudXBkYXRlKHVwZGF0ZWRUb1dhbGxldCwgY3R4KTtcbiAgICB9XG5cbiAgICAvLyBFbWl0IHRoZSBUcmFuc2ZlciBldmVudFxuICAgIGNvbnN0IHRyYW5zZmVyRXZlbnQgPSB7IGZyb20sIHRvLCB2YWx1ZTogdmFsdWUgfTtcbiAgICBjb25zdCBldmVudEhhbmRsZXIgPVxuICAgICAgdGhpcy5yZXBvLk9ic2VydmVySGFuZGxlcigpIGFzIEZhYnJpY0NvbnRyYWN0UmVwb3NpdG9yeU9ic2VydmFibGVIYW5kbGVyO1xuICAgIGV2ZW50SGFuZGxlci51cGRhdGVPYnNlcnZlcnMoXG4gICAgICBsb2dnZXIsXG4gICAgICBcIlwiLFxuICAgICAgRVJDMjBFdmVudHMuVFJBTlNGRVIsXG4gICAgICBcIlwiLFxuICAgICAgY3R4LFxuICAgICAgXCJcIixcbiAgICAgIHRyYW5zZmVyRXZlbnRcbiAgICApO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvKipcbiAgICogQWxsb3dzIGBzcGVuZGVyYCB0byBzcGVuZCBgdmFsdWVgIGFtb3VudCBvZiB0b2tlbnMgZnJvbSB0aGUgb3duZXIuIE5ldyBBcHByb3ZlIGNhbGxzIG92ZXJyaWRlIHRoZSBwcmV2aW91cyBhbGxvd2FuY2UuXG4gICAqIEBub3RlIGh0dHBzOi8vZWlwcy5ldGhlcmV1bS5vcmcvRUlQUy9laXAtMjBcbiAgICpcbiAgICogQHBhcmFtIHtDb250ZXh0fSBjdHggdGhlIHRyYW5zYWN0aW9uIGNvbnRleHRcbiAgICogQHBhcmFtIHtTdHJpbmd9IHNwZW5kZXIgVGhlIHNwZW5kZXJcbiAgICogQHBhcmFtIHtudW1iZXJ9IHZhbHVlIFRoZSBhbW91bnQgb2YgdG9rZW5zIHRvIGJlIGFwcHJvdmVkIGZvciB0cmFuc2ZlclxuICAgKiBAcmV0dXJucyB7Qm9vbGVhbn0gUmV0dXJuIHdoZXRoZXIgdGhlIGFwcHJvdmFsIHdhcyBzdWNjZXNzZnVsIG9yIG5vdFxuICAgKi9cbiAgQFRyYW5zYWN0aW9uKClcbiAgYXN5bmMgQXBwcm92ZShcbiAgICBjdHg6IENvbnRleHQsXG4gICAgc3BlbmRlcjogc3RyaW5nLFxuICAgIHZhbHVlOiBudW1iZXJcbiAgKTogUHJvbWlzZTxib29sZWFuPiB7XG4gICAgLy8gQ2hlY2sgY29udHJhY3Qgb3B0aW9ucyBhcmUgYWxyZWFkeSBzZXQgZmlyc3QgdG8gZXhlY3V0ZSB0aGUgZnVuY3Rpb25cbiAgICBhd2FpdCB0aGlzLkNoZWNrSW5pdGlhbGl6ZWQoY3R4KTtcbiAgICBjb25zdCBsb2dnZXIgPSB0aGlzLmxvZ0ZvcihjdHgpLmZvcih0aGlzLkFwcHJvdmUpO1xuXG4gICAgY29uc3Qgb3duZXIgPSBjdHguY2xpZW50SWRlbnRpdHkuZ2V0SUQoKTtcblxuICAgIGxldCBhbGxvd2FuY2UgPSBhd2FpdCB0aGlzLl9nZXRBbGxvd2FuY2UoY3R4LCBvd25lciwgc3BlbmRlcik7XG5cbiAgICBjb25zdCBvd25lcldhbGxldCA9IGF3YWl0IHRoaXMud2FsbGV0UmVwb3NpdG9yeS5yZWFkKG93bmVyLCBjdHgpO1xuXG4gICAgaWYgKG93bmVyV2FsbGV0LmJhbGFuY2UgPCB2YWx1ZSkge1xuICAgICAgdGhyb3cgbmV3IEJhbGFuY2VFcnJvcihgY2xpZW50IGFjY291bnQgJHtvd25lcn0gaGFzIGluc3VmZmljaWVudCBmdW5kcy5gKTtcbiAgICB9XG5cbiAgICBpZiAoYWxsb3dhbmNlKSB7XG4gICAgICAvLyBPdmVyd3JpdGUgdGhlIGFsbG93YW5jZVxuICAgICAgYWxsb3dhbmNlLnZhbHVlID0gdmFsdWU7XG4gICAgICBhd2FpdCB0aGlzLmFsbG93YW5jZVJlcG9zaXRvcnkudXBkYXRlKGFsbG93YW5jZSwgY3R4KTtcbiAgICB9IGVsc2Uge1xuICAgICAgYWxsb3dhbmNlID0gbmV3IEFsbG93YW5jZSh7XG4gICAgICAgIG93bmVyOiBvd25lcixcbiAgICAgICAgc3BlbmRlcjogc3BlbmRlcixcbiAgICAgICAgdmFsdWU6IHZhbHVlLFxuICAgICAgfSk7XG5cbiAgICAgIGF3YWl0IHRoaXMuYWxsb3dhbmNlUmVwb3NpdG9yeS5jcmVhdGUoYWxsb3dhbmNlLCBjdHgpO1xuICAgIH1cblxuICAgIC8vIEVtaXQgdGhlIEFwcHJvdmFsIGV2ZW50XG4gICAgY29uc3QgYXBwcm92YWxFdmVudCA9IHsgb3duZXIsIHNwZW5kZXIsIHZhbHVlOiB2YWx1ZSB9O1xuICAgIGNvbnN0IGV2ZW50SGFuZGxlciA9XG4gICAgICB0aGlzLnJlcG8uT2JzZXJ2ZXJIYW5kbGVyKCkgYXMgRmFicmljQ29udHJhY3RSZXBvc2l0b3J5T2JzZXJ2YWJsZUhhbmRsZXI7XG4gICAgZXZlbnRIYW5kbGVyLnVwZGF0ZU9ic2VydmVycyhcbiAgICAgIGxvZ2dlcixcbiAgICAgIFwiXCIsXG4gICAgICBFUkMyMEV2ZW50cy5BUFBST1ZBTCxcbiAgICAgIFwiXCIsXG4gICAgICBjdHgsXG4gICAgICBcIlwiLFxuICAgICAgYXBwcm92YWxFdmVudFxuICAgICk7XG5cbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBhbW91bnQgb2YgdG9rZW5zIHdoaWNoIGAgYCBpcyBhbGxvd2VkIHRvIHdpdGhkcmF3IGZyb20gYG93bmVyYC5cbiAgICpcbiAgICogQHBhcmFtIHtDb250ZXh0fSBjdHggdGhlIHRyYW5zYWN0aW9uIGNvbnRleHRcbiAgICogQHBhcmFtIHtTdHJpbmd9IG93bmVyIFRoZSBvd25lciBvZiB0b2tlbnNcbiAgICogQHBhcmFtIHtTdHJpbmd9IHNwZW5kZXIgVGhlIHNwZW5kZXIgd2hvIGFyZSBhYmxlIHRvIHRyYW5zZmVyIHRoZSB0b2tlbnNcbiAgICogQHJldHVybnMge251bWJlcn0gUmV0dXJuIHRoZSBhbW91bnQgb2YgcmVtYWluaW5nIHRva2VucyBhbGxvd2VkIHRvIHNwZW50XG4gICAqL1xuICBAVHJhbnNhY3Rpb24oKVxuICBhc3luYyBBbGxvd2FuY2UoXG4gICAgY3R4OiBDb250ZXh0LFxuICAgIG93bmVyOiBzdHJpbmcsXG4gICAgc3BlbmRlcjogc3RyaW5nXG4gICk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgLy8gQ2hlY2sgY29udHJhY3Qgb3B0aW9ucyBhcmUgYWxyZWFkeSBzZXQgZmlyc3QgdG8gZXhlY3V0ZSB0aGUgZnVuY3Rpb25cbiAgICBhd2FpdCB0aGlzLkNoZWNrSW5pdGlhbGl6ZWQoY3R4KTtcblxuICAgIGNvbnN0IGFsbG93YW5jZSA9IGF3YWl0IHRoaXMuX2dldEFsbG93YW5jZShjdHgsIG93bmVyLCBzcGVuZGVyKTtcblxuICAgIGlmICghYWxsb3dhbmNlKSB7XG4gICAgICB0aHJvdyBuZXcgQWxsb3dhbmNlRXJyb3IoXG4gICAgICAgIGBzcGVuZGVyICR7c3BlbmRlcn0gaGFzIG5vIGFsbG93YW5jZSBmcm9tICR7b3duZXJ9YFxuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIGFsbG93YW5jZS52YWx1ZTtcbiAgfVxuXG4gIGFzeW5jIF9nZXRBbGxvd2FuY2UoXG4gICAgY3R4OiBDb250ZXh0LFxuICAgIG93bmVyOiBzdHJpbmcsXG4gICAgc3BlbmRlcjogc3RyaW5nXG4gICk6IFByb21pc2U8QWxsb3dhbmNlPiB7XG4gICAgY29uc3QgYWxsb3dhbmNlQ29uZGl0aW9uID0gQ29uZGl0aW9uLmFuZChcbiAgICAgIENvbmRpdGlvbi5hdHRyaWJ1dGU8QWxsb3dhbmNlPihcIm93bmVyXCIpLmVxKG93bmVyKSxcbiAgICAgIENvbmRpdGlvbi5hdHRyaWJ1dGU8QWxsb3dhbmNlPihcInNwZW5kZXJcIikuZXEoc3BlbmRlcilcbiAgICApO1xuXG4gICAgY29uc3Qgc2VsZWN0ID0gYXdhaXQgdGhpcy5hbGxvd2FuY2VSZXBvc2l0b3J5LnNlbGVjdFdpdGhDb250ZXh0KFxuICAgICAgdW5kZWZpbmVkLFxuICAgICAgY3R4XG4gICAgKTtcbiAgICBjb25zdCBhbGxvd2FuY2UgPSBhd2FpdCBzZWxlY3Qud2hlcmUoYWxsb3dhbmNlQ29uZGl0aW9uKS5leGVjdXRlKCk7XG4gICAgcmV0dXJuIGFsbG93YW5jZT8uWzBdO1xuICB9XG5cbiAgLy8gPT09PT09PT09PT09PT09PT09IEV4dGVuZGVkIEZ1bmN0aW9ucyA9PT09PT09PT09PT09PT09PT09PT09PT09PVxuXG4gIC8qKlxuICAgKiBTZXQgb3B0aW9uYWwgaW5mb21hdGlvbiBmb3IgYSB0b2tlbi5cbiAgICpcbiAgICogQHBhcmFtIHtDb250ZXh0fSBjdHggdGhlIHRyYW5zYWN0aW9uIGNvbnRleHRcbiAgICogQHBhcmFtIHtTdHJpbmd9IG5hbWUgVGhlIG5hbWUgb2YgdGhlIHRva2VuXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBzeW1ib2wgVGhlIHN5bWJvbCBvZiB0aGUgdG9rZW5cbiAgICogQHBhcmFtIHtTdHJpbmd9IGRlY2ltYWxzIFRoZSBkZWNpbWFscyBvZiB0aGUgdG9rZW5cbiAgICogQHBhcmFtIHtTdHJpbmd9IHRvdGFsU3VwcGx5IFRoZSB0b3RhbFN1cHBseSBvZiB0aGUgdG9rZW5cbiAgICovXG4gIEBUcmFuc2FjdGlvbigpXG4gIGFzeW5jIEluaXRpYWxpemUoY3R4OiBDb250ZXh0LCB0b2tlbjogRVJDMjBUb2tlbikge1xuICAgIC8vIENoZWNrIGNvbnRyYWN0IG9wdGlvbnMgYXJlIG5vdCBhbHJlYWR5IHNldCwgY2xpZW50IGlzIG5vdCBhdXRob3JpemVkIHRvIGNoYW5nZSB0aGVtIG9uY2UgaW50aXRpYWxpemVkXG4gICAgY29uc3Qgc2VsZWN0ID0gYXdhaXQgdGhpcy50b2tlblJlcG9zaXRvcnkuc2VsZWN0V2l0aENvbnRleHQodW5kZWZpbmVkLCBjdHgpO1xuICAgIGNvbnN0IHRva2VucyA9IGF3YWl0IHNlbGVjdC5leGVjdXRlKCk7XG4gICAgaWYgKHRva2Vucy5sZW5ndGggPiAwKSB7XG4gICAgICB0aHJvdyBuZXcgQXV0aG9yaXphdGlvbkVycm9yKFxuICAgICAgICBcImNvbnRyYWN0IG9wdGlvbnMgYXJlIGFscmVhZHkgc2V0LCBjbGllbnQgaXMgbm90IGF1dGhvcml6ZWQgdG8gY2hhbmdlIHRoZW1cIlxuICAgICAgKTtcbiAgICB9XG5cbiAgICB0b2tlbi5vd25lciA9IGN0eC5jbGllbnRJZGVudGl0eS5nZXRJRCgpO1xuXG4gICAgYXdhaXQgdGhpcy50b2tlblJlcG9zaXRvcnkuY3JlYXRlKHRva2VuLCBjdHgpO1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH1cblxuICAvLyBDaGVja3MgdGhhdCBjb250cmFjdCBvcHRpb25zIGhhdmUgYmVlbiBhbHJlYWR5IGluaXRpYWxpemVkXG4gIEBUcmFuc2FjdGlvbihmYWxzZSlcbiAgYXN5bmMgQ2hlY2tJbml0aWFsaXplZChjdHg6IENvbnRleHQpIHtcbiAgICBjb25zdCBzZWxlY3QgPSBhd2FpdCB0aGlzLnRva2VuUmVwb3NpdG9yeS5zZWxlY3RXaXRoQ29udGV4dCh1bmRlZmluZWQsIGN0eCk7XG4gICAgY29uc3QgdG9rZW5zID0gYXdhaXQgc2VsZWN0LmV4ZWN1dGUoKTtcbiAgICBpZiAodG9rZW5zLmxlbmd0aCA9PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgTm90SW5pdGlhbGl6ZWRFcnJvcihcbiAgICAgICAgXCJjb250cmFjdCBvcHRpb25zIG5lZWQgdG8gYmUgc2V0IGJlZm9yZSBjYWxsaW5nIGFueSBmdW5jdGlvbiwgY2FsbCBJbml0aWFsaXplKCkgdG8gaW5pdGlhbGl6ZSBjb250cmFjdFwiXG4gICAgICApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBNaW50IGNyZWF0ZXMgbmV3IHRva2VucyBhbmQgYWRkcyB0aGVtIHRvIG1pbnRlcidzIGFjY291bnQgYmFsYW5jZVxuICAgKlxuICAgKiBAcGFyYW0ge0NvbnRleHR9IGN0eCB0aGUgdHJhbnNhY3Rpb24gY29udGV4dFxuICAgKiBAcGFyYW0ge251bWJlcn0gYW1vdW50IGFtb3VudCBvZiB0b2tlbnMgdG8gYmUgbWludGVkXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBiYWxhbmNlXG4gICAqL1xuICBAT3duZXIoKVxuICBAVHJhbnNhY3Rpb24oKVxuICBhc3luYyBNaW50KGN0eDogQ29udGV4dCwgYW1vdW50OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBDaGVjayBjb250cmFjdCBvcHRpb25zIGFyZSBhbHJlYWR5IHNldCBmaXJzdCB0byBleGVjdXRlIHRoZSBmdW5jdGlvblxuICAgIGF3YWl0IHRoaXMuQ2hlY2tJbml0aWFsaXplZChjdHgpO1xuXG4gICAgY29uc3QgbG9nZ2VyID0gdGhpcy5sb2dGb3IoY3R4KS5mb3IodGhpcy5NaW50KTtcblxuICAgIC8vIEdldCBJRCBvZiBzdWJtaXR0aW5nIGNsaWVudCBpZGVudGl0eVxuICAgIGNvbnN0IG1pbnRlciA9IGN0eC5jbGllbnRJZGVudGl0eS5nZXRJRCgpO1xuXG4gICAgaWYgKGFtb3VudCA8PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgVmFsaWRhdGlvbkVycm9yKFwibWludCBhbW91bnQgbXVzdCBiZSBhIHBvc2l0aXZlIGludGVnZXJcIik7XG4gICAgfVxuXG4gICAgbGV0IG1pbnRlcldhbGxldDogRVJDMjBXYWxsZXQ7XG4gICAgdHJ5IHtcbiAgICAgIG1pbnRlcldhbGxldCA9IGF3YWl0IHRoaXMud2FsbGV0UmVwb3NpdG9yeS5yZWFkKG1pbnRlciwgY3R4KTtcblxuICAgICAgY29uc3QgY3VycmVudEJhbGFuY2UgPSBtaW50ZXJXYWxsZXQuYmFsYW5jZTtcblxuICAgICAgY29uc3QgdXBkYXRlZEJhbGFuY2UgPSBhZGQoY3VycmVudEJhbGFuY2UsIGFtb3VudCk7XG5cbiAgICAgIGNvbnN0IHVwZGF0ZWRtaW50ZXIgPSBPYmplY3QuYXNzaWduKHt9LCBtaW50ZXJXYWxsZXQsIHtcbiAgICAgICAgYmFsYW5jZTogdXBkYXRlZEJhbGFuY2UsXG4gICAgICB9KTtcblxuICAgICAgYXdhaXQgdGhpcy53YWxsZXRSZXBvc2l0b3J5LnVwZGF0ZSh1cGRhdGVkbWludGVyLCBjdHgpO1xuICAgIH0gY2F0Y2ggKGU6IHVua25vd24pIHtcbiAgICAgIGlmIChlIGluc3RhbmNlb2YgQmFzZUVycm9yKSB7XG4gICAgICAgIGlmIChlLmNvZGUgPT09IDQwNCkge1xuICAgICAgICAgIC8vIENyZWF0ZSBhIG5ldyB3YWxsZXQgZm9yIHRoZSBtaW50ZXJcbiAgICAgICAgICBjb25zdCBuZXdXYWxsZXQgPSBuZXcgRVJDMjBXYWxsZXQoe1xuICAgICAgICAgICAgaWQ6IG1pbnRlcixcbiAgICAgICAgICAgIGJhbGFuY2U6IGFtb3VudCxcbiAgICAgICAgICAgIHRva2VuOiBhd2FpdCB0aGlzLlRva2VuTmFtZShjdHgpLFxuICAgICAgICAgIH0pO1xuICAgICAgICAgIGF3YWl0IHRoaXMud2FsbGV0UmVwb3NpdG9yeS5jcmVhdGUobmV3V2FsbGV0LCBjdHgpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKGUubWVzc2FnZSk7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRocm93IG5ldyBJbnRlcm5hbEVycm9yKGUgYXMgc3RyaW5nKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBFbWl0IHRoZSBUcmFuc2ZlciBldmVudFxuICAgIGNvbnN0IHRyYW5zZmVyRXZlbnQgPSB7IGZyb206IFwiMHgwXCIsIHRvOiBtaW50ZXIsIHZhbHVlOiBhbW91bnQgfTtcbiAgICBjb25zdCBldmVudEhhbmRsZXIgPVxuICAgICAgdGhpcy5yZXBvLk9ic2VydmVySGFuZGxlcigpIGFzIEZhYnJpY0NvbnRyYWN0UmVwb3NpdG9yeU9ic2VydmFibGVIYW5kbGVyO1xuICAgIGV2ZW50SGFuZGxlci51cGRhdGVPYnNlcnZlcnMoXG4gICAgICBsb2dnZXIsXG4gICAgICBcIlwiLFxuICAgICAgRVJDMjBFdmVudHMuVFJBTlNGRVIsXG4gICAgICBcIlwiLFxuICAgICAgY3R4LFxuICAgICAgXCJcIixcbiAgICAgIHRyYW5zZmVyRXZlbnRcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1cm4gcmVkZWVtIHRva2VucyBmcm9tIG1pbnRlcidzIGFjY291bnQgYmFsYW5jZVxuICAgKlxuICAgKiBAcGFyYW0ge0NvbnRleHR9IGN0eCB0aGUgdHJhbnNhY3Rpb24gY29udGV4dFxuICAgKiBAcGFyYW0ge251bWJlcn0gYW1vdW50IGFtb3VudCBvZiB0b2tlbnMgdG8gYmUgYnVybmVkXG4gICAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBiYWxhbmNlXG4gICAqL1xuICBAT3duZXIoKVxuICBAVHJhbnNhY3Rpb24oKVxuICBhc3luYyBCdXJuKGN0eDogQ29udGV4dCwgYW1vdW50OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBDaGVjayBjb250cmFjdCBvcHRpb25zIGFyZSBhbHJlYWR5IHNldCBmaXJzdCB0byBleGVjdXRlIHRoZSBmdW5jdGlvblxuICAgIGF3YWl0IHRoaXMuQ2hlY2tJbml0aWFsaXplZChjdHgpO1xuXG4gICAgY29uc3QgbG9nZ2VyID0gdGhpcy5sb2dGb3IoY3R4KS5mb3IodGhpcy5CdXJuKTtcblxuICAgIGNvbnN0IG1pbnRlciA9IGN0eC5jbGllbnRJZGVudGl0eS5nZXRJRCgpO1xuXG4gICAgY29uc3QgbWludGVyV2FsbGV0ID0gYXdhaXQgdGhpcy53YWxsZXRSZXBvc2l0b3J5LnJlYWQobWludGVyLCBjdHgpO1xuXG4gICAgY29uc3QgY3VycmVudEJhbGFuY2UgPSBtaW50ZXJXYWxsZXQuYmFsYW5jZTtcblxuICAgIGlmIChjdXJyZW50QmFsYW5jZSA8IGFtb3VudCkge1xuICAgICAgdGhyb3cgbmV3IEJhbGFuY2VFcnJvcihgTWludGVyIGhhcyBpbnN1ZmZpY2llbnQgZnVuZHMuYCk7XG4gICAgfVxuXG4gICAgY29uc3QgdXBkYXRlZEJhbGFuY2UgPSBzdWIoY3VycmVudEJhbGFuY2UsIGFtb3VudCk7XG5cbiAgICBjb25zdCB1cGRhdGVkbWludGVyID0gT2JqZWN0LmFzc2lnbih7fSwgbWludGVyV2FsbGV0LCB7XG4gICAgICBiYWxhbmNlOiB1cGRhdGVkQmFsYW5jZSxcbiAgICB9KTtcblxuICAgIGF3YWl0IHRoaXMud2FsbGV0UmVwb3NpdG9yeS51cGRhdGUodXBkYXRlZG1pbnRlciwgY3R4KTtcblxuICAgIGxvZ2dlci5pbmZvKGAke2Ftb3VudH0gdG9rZW5zIHdlcmUgYnVybmVkYCk7XG5cbiAgICAvLyBFbWl0IHRoZSBUcmFuc2ZlciBldmVudFxuICAgIGNvbnN0IHRyYW5zZmVyRXZlbnQgPSB7IGZyb206IG1pbnRlciwgdG86IFwiMHgwXCIsIHZhbHVlOiBhbW91bnQgfTtcbiAgICBjb25zdCBldmVudEhhbmRsZXIgPVxuICAgICAgdGhpcy5yZXBvLk9ic2VydmVySGFuZGxlcigpIGFzIEZhYnJpY0NvbnRyYWN0UmVwb3NpdG9yeU9ic2VydmFibGVIYW5kbGVyO1xuICAgIGV2ZW50SGFuZGxlci51cGRhdGVPYnNlcnZlcnMoXG4gICAgICBsb2dnZXIsXG4gICAgICBcIlwiLFxuICAgICAgRVJDMjBFdmVudHMuVFJBTlNGRVIsXG4gICAgICBcIlwiLFxuICAgICAgY3R4LFxuICAgICAgXCJcIixcbiAgICAgIHRyYW5zZmVyRXZlbnRcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIEJ1cm5Gcm9tIHJlZGVlbSB0b2tlbnMgZnJvbSBhY2NvdW50IGFsbG93ZW5jZSBhbmQgYmFsYW5jZVxuICAgKlxuICAgKiBAcGFyYW0ge0NvbnRleHR9IGN0eCB0aGUgdHJhbnNhY3Rpb24gY29udGV4dFxuICAgKiBAcGFyYW0ge251bWJlcn0gYWNjb3VudCBhY2NvdW50IGZyb20gd2hlcmUgdG9rZW5zIHdpbGwgYmUgYnVybmVkXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBhbW91bnQgYW1vdW50IG9mIHRva2VucyB0byBiZSBidXJuZWRcbiAgICogQHJldHVybnMge09iamVjdH0gVGhlIGJhbGFuY2VcbiAgICovXG4gIEBPd25lcigpXG4gIEBUcmFuc2FjdGlvbigpXG4gIGFzeW5jIEJ1cm5Gcm9tKGN0eDogQ29udGV4dCwgYWNjb3VudDogc3RyaW5nLCBhbW91bnQ6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIENoZWNrIGNvbnRyYWN0IG9wdGlvbnMgYXJlIGFscmVhZHkgc2V0IGZpcnN0IHRvIGV4ZWN1dGUgdGhlIGZ1bmN0aW9uXG4gICAgYXdhaXQgdGhpcy5DaGVja0luaXRpYWxpemVkKGN0eCk7XG5cbiAgICBjb25zdCBsb2dnZXIgPSB0aGlzLmxvZ0ZvcihjdHgpLmZvcih0aGlzLkJ1cm5Gcm9tKTtcblxuICAgIGNvbnN0IGFjY291bnRXYWxsZXQgPSBhd2FpdCB0aGlzLndhbGxldFJlcG9zaXRvcnkucmVhZChhY2NvdW50LCBjdHgpO1xuXG4gICAgY29uc3QgY3VycmVudEJhbGFuY2UgPSBhY2NvdW50V2FsbGV0LmJhbGFuY2U7XG5cbiAgICBpZiAoY3VycmVudEJhbGFuY2UgPCBhbW91bnQpIHtcbiAgICAgIHRocm93IG5ldyBCYWxhbmNlRXJyb3IoYCR7YWNjb3VudH0gaGFzIGluc3VmZmljaWVudCBmdW5kcy5gKTtcbiAgICB9XG5cbiAgICBjb25zdCB1cGRhdGVkQmFsYW5jZSA9IHN1YihjdXJyZW50QmFsYW5jZSwgYW1vdW50KTtcblxuICAgIGNvbnN0IHVwZGF0ZWRhY2NvdW50ID0gT2JqZWN0LmFzc2lnbih7fSwgYWNjb3VudFdhbGxldCwge1xuICAgICAgYmFsYW5jZTogdXBkYXRlZEJhbGFuY2UsXG4gICAgfSk7XG5cbiAgICBhd2FpdCB0aGlzLndhbGxldFJlcG9zaXRvcnkudXBkYXRlKHVwZGF0ZWRhY2NvdW50LCBjdHgpO1xuXG4gICAgbG9nZ2VyLmluZm8oYCR7YW1vdW50fSB0b2tlbnMgd2VyZSBiZXJuZWQgZnJvbSAke2FjY291bnR9YCk7XG5cbiAgICAvLyBFbWl0IHRoZSBUcmFuc2ZlciBldmVudFxuICAgIGNvbnN0IHRyYW5zZmVyRXZlbnQgPSB7IGZyb206IGFjY291bnQsIHRvOiBcIjB4MFwiLCB2YWx1ZTogYW1vdW50IH07XG4gICAgY29uc3QgZXZlbnRIYW5kbGVyID1cbiAgICAgIHRoaXMucmVwby5PYnNlcnZlckhhbmRsZXIoKSBhcyBGYWJyaWNDb250cmFjdFJlcG9zaXRvcnlPYnNlcnZhYmxlSGFuZGxlcjtcbiAgICBldmVudEhhbmRsZXIudXBkYXRlT2JzZXJ2ZXJzKFxuICAgICAgbG9nZ2VyLFxuICAgICAgXCJcIixcbiAgICAgIEVSQzIwRXZlbnRzLlRSQU5TRkVSLFxuICAgICAgXCJcIixcbiAgICAgIGN0eCxcbiAgICAgIFwiXCIsXG4gICAgICB0cmFuc2ZlckV2ZW50XG4gICAgKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDbGllbnRBY2NvdW50QmFsYW5jZSByZXR1cm5zIHRoZSBiYWxhbmNlIG9mIHRoZSByZXF1ZXN0aW5nIGNsaWVudCdzIGFjY291bnQuXG4gICAqXG4gICAqIEBwYXJhbSB7Q29udGV4dH0gY3R4IHRoZSB0cmFuc2FjdGlvbiBjb250ZXh0XG4gICAqIEByZXR1cm5zIHtOdW1iZXJ9IFJldHVybnMgdGhlIGFjY291bnQgYmFsYW5jZVxuICAgKi9cbiAgQFRyYW5zYWN0aW9uKClcbiAgYXN5bmMgQ2xpZW50QWNjb3VudEJhbGFuY2UoY3R4OiBDb250ZXh0KTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICAvLyBDaGVjayBjb250cmFjdCBvcHRpb25zIGFyZSBhbHJlYWR5IHNldCBmaXJzdCB0byBleGVjdXRlIHRoZSBmdW5jdGlvblxuICAgIGF3YWl0IHRoaXMuQ2hlY2tJbml0aWFsaXplZChjdHgpO1xuXG4gICAgLy8gR2V0IElEIG9mIHN1Ym1pdHRpbmcgY2xpZW50IGlkZW50aXR5XG4gICAgY29uc3QgY2xpZW50QWNjb3VudElEID0gY3R4LmNsaWVudElkZW50aXR5LmdldElEKCk7XG5cbiAgICBjb25zdCBjbGllbnRXYWxsZXQgPSBhd2FpdCB0aGlzLndhbGxldFJlcG9zaXRvcnkucmVhZChjbGllbnRBY2NvdW50SUQsIGN0eCk7XG5cbiAgICBpZiAoIWNsaWVudFdhbGxldCkge1xuICAgICAgdGhyb3cgbmV3IEJhbGFuY2VFcnJvcihgVGhlIGFjY291bnQgJHtjbGllbnRBY2NvdW50SUR9IGRvZXMgbm90IGV4aXN0YCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIGNsaWVudFdhbGxldC5iYWxhbmNlO1xuICB9XG5cbiAgLy8gQ2xpZW50QWNjb3VudElEIHJldHVybnMgdGhlIGlkIG9mIHRoZSByZXF1ZXN0aW5nIGNsaWVudCdzIGFjY291bnQuXG4gIC8vIEluIHRoaXMgaW1wbGVtZW50YXRpb24sIHRoZSBjbGllbnQgYWNjb3VudCBJRCBpcyB0aGUgY2xpZW50SWQgaXRzZWxmLlxuICAvLyBVc2VycyBjYW4gdXNlIHRoaXMgZnVuY3Rpb24gdG8gZ2V0IHRoZWlyIG93biBhY2NvdW50IGlkLCB3aGljaCB0aGV5IGNhbiB0aGVuIGdpdmUgdG8gb3RoZXJzIGFzIHRoZSBwYXltZW50IGFkZHJlc3NcbiAgQFRyYW5zYWN0aW9uKClcbiAgYXN5bmMgQ2xpZW50QWNjb3VudElEKGN0eDogQ29udGV4dCkge1xuICAgIC8vIENoZWNrIGNvbnRyYWN0IG9wdGlvbnMgYXJlIGFscmVhZHkgc2V0IGZpcnN0IHRvIGV4ZWN1dGUgdGhlIGZ1bmN0aW9uXG4gICAgYXdhaXQgdGhpcy5DaGVja0luaXRpYWxpemVkKGN0eCk7XG5cbiAgICAvLyBHZXQgSUQgb2Ygc3VibWl0dGluZyBjbGllbnQgaWRlbnRpdHlcbiAgICBjb25zdCBjbGllbnRBY2NvdW50SUQgPSBjdHguY2xpZW50SWRlbnRpdHkuZ2V0SUQoKTtcbiAgICByZXR1cm4gY2xpZW50QWNjb3VudElEO1xuICB9XG59XG4iXX0=