From e76fbcf3a9ca485c14bc995b5c0e5ffab4dc1ad3 Mon Sep 17 00:00:00 2001 From: imollov Date: Tue, 6 Feb 2024 11:28:40 +0200 Subject: [PATCH 1/5] fix: upgrade deprecated Functions dependencies --- .../FunctionsSandboxLibrary/Functions.js | 143 - contracts/FunctionsSandboxLibrary/Log.js | 59 - contracts/FunctionsSandboxLibrary/Sandbox.js | 81 - .../FunctionsSandboxLibrary/Validator.js | 69 - .../FunctionsSandboxLibrary/buildRequest.js | 17 - .../FunctionsSandboxLibrary/encryptSecrets.js | 23 - .../getRequestConfig.js | 90 - contracts/FunctionsSandboxLibrary/handler.js | 92 - contracts/FunctionsSandboxLibrary/index.js | 30 - .../simulateRequest.js | 79 - contracts/README.md | 58 +- contracts/contracts/ResultsConsumer.sol | 32 +- contracts/contracts/SportsPredictionGame.sol | 8 +- .../contracts/ccip/NativeTokenReceiver.sol | 2 +- .../contracts/ccip/NativeTokenSender.sol | 2 +- .../ccip/ProgrammableTokenReceiver.sol | 2 +- .../ccip/ProgrammableTokenSender.sol | 4 +- .../contracts/dev/AuthorizedReceiver.sol | 86 - .../AuthorizedOriginReceiverUpgradeable.sol | 151 - .../functions/ConfirmedOwnerUpgradeable.sol | 96 - .../contracts/dev/functions/Functions.sol | 132 - .../functions/FunctionsBillingRegistry.sol | 843 ---- .../dev/functions/FunctionsClient.sol | 138 - .../dev/functions/FunctionsOracle.sol | 274 -- .../dev/functions/vendor/ProxyAdmin.sol | 6 - .../vendor/TransparentUpgradeableProxy.sol | 6 - .../AuthorizedOriginReceiverInterface.sol | 57 - .../AuthorizedReceiverInterface.sol | 10 - .../FunctionsBillingRegistryInterface.sol | 99 - .../interfaces/FunctionsClientInterface.sol | 24 - .../interfaces/FunctionsOracleInterface.sol | 93 - contracts/contracts/dev/ocr2/OCR2Base.sol | 383 -- .../dev/ocr2/OCR2BaseUpgradeable.sol | 388 -- .../@ensdomains/buffer/0.1.0/Buffer.sol | 260 - .../v.4.8.0/contracts/security/Pausable.sol | 105 - .../v.4.8.0/contracts/utils/Context.sol | 24 - .../v.4.8.0/contracts/utils/SafeCast.sol | 1136 ----- .../contracts/utils/structs/EnumerableSet.sol | 378 -- .../v4.3.1/contracts/utils/Address.sol | 208 - .../vendor/solidity-cborutils/2.0.0/CBOR.sol | 210 - .../contracts/test/FunctionsConsumer.sol | 86 - .../contracts/test/MockFunctionsOracle.sol | 6 +- .../contracts/test/MockResultsConsumer.sol | 6 +- contracts/hardhat.config.js | 8 +- contracts/networks.js | 18 +- contracts/package-lock.json | 4297 ++++++++++++++++- contracts/package.json | 9 +- contracts/scripts/generateKeypair.js | 4 - .../scripts/simulateFunctionsJavaScript.js | 39 - contracts/tasks/Functions-billing/accept.js | 54 - contracts/tasks/Functions-billing/add.js | 62 - contracts/tasks/Functions-billing/cancel.js | 55 - contracts/tasks/Functions-billing/create.js | 97 - contracts/tasks/Functions-billing/fund.js | 67 - contracts/tasks/Functions-billing/index.js | 9 - contracts/tasks/Functions-billing/info.js | 35 - contracts/tasks/Functions-billing/remove.js | 61 - .../tasks/Functions-billing/timeoutRequest.js | 36 - contracts/tasks/Functions-billing/transfer.js | 55 - .../Functions-client/buildOffchainSecrets.js | 47 - .../Functions-client/buildRequestJSON.js | 144 - .../tasks/Functions-client/checkUpkeep.js | 25 - .../tasks/Functions-client/clearGists.js | 50 - .../Functions-client/deployAutoClient.js | 92 - .../tasks/Functions-client/deployClient.js | 56 - contracts/tasks/Functions-client/index.js | 12 - .../Functions-client/performManualUpkeep.js | 40 - .../Functions-client/readResultAndError.js | 45 - contracts/tasks/Functions-client/request.js | 308 -- .../tasks/Functions-client/setAutoRequest.js | 135 - .../tasks/Functions-client/setOracleAddr.js | 33 - contracts/tasks/Functions-client/simulate.js | 246 - contracts/tasks/deploy-game.js | 89 +- contracts/tasks/index.js | 4 +- contracts/tasks/simulate.js | 29 + contracts/tasks/utils/artifact.js | 203 - .../tasks/utils/generateOffchainSecrets.js | 118 - contracts/tasks/utils/github.js | 83 - contracts/tasks/utils/index.js | 7 - contracts/tasks/utils/logger.js | 30 - contracts/tasks/utils/network.js | 25 - contracts/tasks/utils/price.js | 40 - contracts/tasks/utils/prompt.js | 59 - contracts/tasks/utils/spin.js | 11 - .../test/integration/ResultsConsumer.spec.js | 149 - .../test/unit/SportsPredictionGame.spec.js | 2 +- 86 files changed, 4279 insertions(+), 8705 deletions(-) delete mode 100644 contracts/FunctionsSandboxLibrary/Functions.js delete mode 100644 contracts/FunctionsSandboxLibrary/Log.js delete mode 100644 contracts/FunctionsSandboxLibrary/Sandbox.js delete mode 100644 contracts/FunctionsSandboxLibrary/Validator.js delete mode 100644 contracts/FunctionsSandboxLibrary/buildRequest.js delete mode 100644 contracts/FunctionsSandboxLibrary/encryptSecrets.js delete mode 100644 contracts/FunctionsSandboxLibrary/getRequestConfig.js delete mode 100644 contracts/FunctionsSandboxLibrary/handler.js delete mode 100644 contracts/FunctionsSandboxLibrary/index.js delete mode 100644 contracts/FunctionsSandboxLibrary/simulateRequest.js delete mode 100644 contracts/contracts/dev/AuthorizedReceiver.sol delete mode 100644 contracts/contracts/dev/functions/AuthorizedOriginReceiverUpgradeable.sol delete mode 100644 contracts/contracts/dev/functions/ConfirmedOwnerUpgradeable.sol delete mode 100644 contracts/contracts/dev/functions/Functions.sol delete mode 100644 contracts/contracts/dev/functions/FunctionsBillingRegistry.sol delete mode 100644 contracts/contracts/dev/functions/FunctionsClient.sol delete mode 100644 contracts/contracts/dev/functions/FunctionsOracle.sol delete mode 100644 contracts/contracts/dev/functions/vendor/ProxyAdmin.sol delete mode 100644 contracts/contracts/dev/functions/vendor/TransparentUpgradeableProxy.sol delete mode 100644 contracts/contracts/dev/interfaces/AuthorizedOriginReceiverInterface.sol delete mode 100644 contracts/contracts/dev/interfaces/AuthorizedReceiverInterface.sol delete mode 100644 contracts/contracts/dev/interfaces/FunctionsBillingRegistryInterface.sol delete mode 100644 contracts/contracts/dev/interfaces/FunctionsClientInterface.sol delete mode 100644 contracts/contracts/dev/interfaces/FunctionsOracleInterface.sol delete mode 100644 contracts/contracts/dev/ocr2/OCR2Base.sol delete mode 100644 contracts/contracts/dev/ocr2/OCR2BaseUpgradeable.sol delete mode 100644 contracts/contracts/dev/vendor/@ensdomains/buffer/0.1.0/Buffer.sol delete mode 100644 contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/security/Pausable.sol delete mode 100644 contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/Context.sol delete mode 100644 contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/SafeCast.sol delete mode 100644 contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/structs/EnumerableSet.sol delete mode 100644 contracts/contracts/dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol delete mode 100644 contracts/contracts/dev/vendor/solidity-cborutils/2.0.0/CBOR.sol delete mode 100644 contracts/contracts/test/FunctionsConsumer.sol delete mode 100644 contracts/scripts/generateKeypair.js delete mode 100644 contracts/scripts/simulateFunctionsJavaScript.js delete mode 100644 contracts/tasks/Functions-billing/accept.js delete mode 100644 contracts/tasks/Functions-billing/add.js delete mode 100644 contracts/tasks/Functions-billing/cancel.js delete mode 100644 contracts/tasks/Functions-billing/create.js delete mode 100644 contracts/tasks/Functions-billing/fund.js delete mode 100644 contracts/tasks/Functions-billing/index.js delete mode 100644 contracts/tasks/Functions-billing/info.js delete mode 100644 contracts/tasks/Functions-billing/remove.js delete mode 100644 contracts/tasks/Functions-billing/timeoutRequest.js delete mode 100644 contracts/tasks/Functions-billing/transfer.js delete mode 100644 contracts/tasks/Functions-client/buildOffchainSecrets.js delete mode 100644 contracts/tasks/Functions-client/buildRequestJSON.js delete mode 100644 contracts/tasks/Functions-client/checkUpkeep.js delete mode 100644 contracts/tasks/Functions-client/clearGists.js delete mode 100644 contracts/tasks/Functions-client/deployAutoClient.js delete mode 100644 contracts/tasks/Functions-client/deployClient.js delete mode 100644 contracts/tasks/Functions-client/index.js delete mode 100644 contracts/tasks/Functions-client/performManualUpkeep.js delete mode 100644 contracts/tasks/Functions-client/readResultAndError.js delete mode 100644 contracts/tasks/Functions-client/request.js delete mode 100644 contracts/tasks/Functions-client/setAutoRequest.js delete mode 100644 contracts/tasks/Functions-client/setOracleAddr.js delete mode 100644 contracts/tasks/Functions-client/simulate.js create mode 100644 contracts/tasks/simulate.js delete mode 100644 contracts/tasks/utils/artifact.js delete mode 100644 contracts/tasks/utils/generateOffchainSecrets.js delete mode 100644 contracts/tasks/utils/github.js delete mode 100644 contracts/tasks/utils/index.js delete mode 100644 contracts/tasks/utils/logger.js delete mode 100644 contracts/tasks/utils/network.js delete mode 100644 contracts/tasks/utils/price.js delete mode 100644 contracts/tasks/utils/prompt.js delete mode 100644 contracts/tasks/utils/spin.js delete mode 100644 contracts/test/integration/ResultsConsumer.spec.js diff --git a/contracts/FunctionsSandboxLibrary/Functions.js b/contracts/FunctionsSandboxLibrary/Functions.js deleted file mode 100644 index 089807e..0000000 --- a/contracts/FunctionsSandboxLibrary/Functions.js +++ /dev/null @@ -1,143 +0,0 @@ -"use strict" -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -var _a -Object.defineProperty(exports, "__esModule", { value: true }) -exports.FunctionsModule = void 0 -const axios_1 = __importDefault(require("axios")) -class FunctionsModule { - constructor() { - this.buildFunctionsmodule = (numAllowedQueries) => { - return { - makeHttpRequest: this.makeHttpRequestFactory(numAllowedQueries), - encodeUint256: FunctionsModule.encodeUint256, - encodeInt256: FunctionsModule.encodeInt256, - encodeString: FunctionsModule.encodeString, - } - } - this.makeHttpRequestFactory = (maxHttpRequests) => { - let totalHttpRequests = 0 - return async ({ - url, - method = "get", - params, - headers, - data, - // Default timeout of 5 seconds - timeout = 5000, - responseType = "json", - }) => { - if (totalHttpRequests < maxHttpRequests) { - totalHttpRequests++ - let result - if (timeout > 9000) { - throw Error("HTTP request timeout >9000") - } - if (url.length > 2048) { - throw Error("HTTP request URL length >2048") - } - try { - result = await (0, axios_1.default)({ - method: method.toLowerCase(), - url, - params, - headers, - data, - timeout, - responseType, - maxBodyLength: 2000, - maxContentLength: 2000000, // Max response size: 2 megabytes - }) - // Delete the request to avoid exposing system information to the user's code - delete result.request - delete result.config - result.error = false - return result - } catch (untypedError) { - const error = untypedError - delete error.request - delete error.config - if (error.response) { - delete error.response.request - } - error.error = true - return error - } - } - throw Error("exceeded numAllowedQueries") - } - } - } - get userHttpQueries() { - return [] - } -} -exports.FunctionsModule = FunctionsModule -_a = FunctionsModule -FunctionsModule.encodeUint256 = (result) => { - if (typeof result === "number") { - if (!Number.isInteger(result)) { - throw Error("encodeUint256 invalid input") - } - if (result < 0) { - throw Error("encodeUint256 invalid input") - } - return _a.encodeUint256(BigInt(result)) - } - if (typeof result === "bigint") { - if (result > _a.maxUint256) { - throw Error("encodeUint256 invalid input") - } - if (result < BigInt(0)) { - throw Error("encodeUint256 invalid input") - } - if (result === BigInt(0)) { - return Buffer.from("0000000000000000000000000000000000000000000000000000000000000000", "hex") - } - const hex = result.toString(16).padStart(64, "0") - return Buffer.from(hex, "hex") - } - throw Error("encodeUint256 invalid input") -} -FunctionsModule.encodeInt256 = (result) => { - if (typeof result === "number") { - if (!Number.isInteger(result)) { - throw Error("encodeInt256 invalid input") - } - return _a.encodeInt256(BigInt(result)) - } - if (typeof result !== "bigint") { - throw Error("encodeInt256 invalid input") - } - if (result < _a.maxNegInt256) { - throw Error("encodeInt256 invalid input") - } - if (result > _a.maxPosInt256) { - throw Error("encodeInt256 invalid input") - } - if (result < BigInt(0)) { - return _a.encodeNegSignedInt(result) - } - return _a.encodePosSignedInt(result) -} -FunctionsModule.encodeString = (result) => { - if (typeof result !== "string") { - throw Error("encodeString invalid input") - } - return Buffer.from(result) -} -FunctionsModule.encodePosSignedInt = (int) => { - const hex = int.toString(16).padStart(64, "0") - return Buffer.from(hex, "hex") -} -FunctionsModule.encodeNegSignedInt = (int) => { - const overflowingHex = (BigInt(2) ** BigInt(256) + int).toString(16) - const int256Hex = overflowingHex.slice(-64) - return Buffer.from(int256Hex, "hex") -} -FunctionsModule.maxUint256 = BigInt("115792089237316195423570985008687907853269984665640564039457584007913129639935") -FunctionsModule.maxPosInt256 = BigInt("57896044618658097711785492504343953926634992332820282019728792003956564819967") -FunctionsModule.maxNegInt256 = BigInt("-57896044618658097711785492504343953926634992332820282019728792003956564819968") diff --git a/contracts/FunctionsSandboxLibrary/Log.js b/contracts/FunctionsSandboxLibrary/Log.js deleted file mode 100644 index 6210ff5..0000000 --- a/contracts/FunctionsSandboxLibrary/Log.js +++ /dev/null @@ -1,59 +0,0 @@ -"use strict" -Object.defineProperty(exports, "__esModule", { value: true }) -exports.Log = void 0 -class Log {} -exports.Log = Log -Log.error = (message, requestId) => - console.log( - JSON.stringify({ - logLevel: "error", - timestamp: Date.now(), - message, - requestId, - }) - ) -Log.warn = (message, requestId) => - console.log( - JSON.stringify({ - logLevel: "warn", - timestamp: Date.now(), - message, - requestId, - }) - ) -Log.info = (message, requestId) => { - if (process.env["LOG_LEVEL"] && process.env["LOG_LEVEL"]?.toLowerCase() !== "false") { - console.log( - JSON.stringify({ - logLevel: "info", - timestamp: Date.now(), - message, - requestId, - }) - ) - } -} -Log.debug = (message, requestId) => { - if (process.env["LOG_LEVEL"]?.toLowerCase() === "debug" || process.env["LOG_LEVEL"]?.toLowerCase() === "trace") { - console.log( - JSON.stringify({ - logLevel: "debug", - timestamp: Date.now(), - message, - requestId, - }) - ) - } -} -Log.trace = (message, requestId) => { - if (process.env["LOG_LEVEL"]?.toLowerCase() === "trace") { - console.log( - JSON.stringify({ - logLevel: "trace", - timestamp: Date.now(), - message, - requestId, - }) - ) - } -} diff --git a/contracts/FunctionsSandboxLibrary/Sandbox.js b/contracts/FunctionsSandboxLibrary/Sandbox.js deleted file mode 100644 index b36a8bb..0000000 --- a/contracts/FunctionsSandboxLibrary/Sandbox.js +++ /dev/null @@ -1,81 +0,0 @@ -"use strict" -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, "__esModule", { value: true }) -exports.SandboxError = exports.Sandbox = void 0 -const fs_1 = __importDefault(require("fs")) -const os_1 = __importDefault(require("os")) -const path_1 = __importDefault(require("path")) -const vm2_1 = require("vm2") -const Functions_1 = require("./Functions") -class Sandbox { - constructor(disableTmpClearing, enableSandboxedLogging) { - this.disableTmpClearing = disableTmpClearing - this.enableSandboxedLogging = enableSandboxedLogging - } - async evaluate(numAllowedQueries, javascriptString, args, secrets) { - const functionsModule = new Functions_1.FunctionsModule() - const Functions = functionsModule.buildFunctionsmodule(numAllowedQueries) - // Clear the tmp directory before running the untrusted code to ensure - // it does not have access to any cached data from the previously run script - // in the case that the previous script exited prematurely. - this.clearTmpDirectory() - const vm = new vm2_1.NodeVM({ - sandbox: { args, secrets, Functions }, - console: `${this.enableSandboxedLogging ? "inherit" : "off"}`, - eval: false, - wasm: false, - require: { - builtin: ["buffer", "crypto", "querystring", "string_decoder", "url", "util"], - }, - }) - let functionScript - // Try to compile the provided JavaScript code. - try { - functionScript = new vm2_1.VMScript("module.exports = async function () {\n" + javascriptString + "\n}").compile() - } catch (untypedError) { - const error = untypedError - throw new SandboxError(error.name, error.message, error.stack) - } - if (this.enableSandboxedLogging) { - console.log("\n__Console log messages from sandboxed code__") - } - // Try to run the provided JavaScript code. - let sandboxedFunction - let result - try { - sandboxedFunction = await vm.run(functionScript) - result = await sandboxedFunction() - } catch (untypedError) { - const error = untypedError - throw new SandboxError(error.name, error.message, error.stack, functionsModule.userHttpQueries) - } - // Clear the tmp directory after running the code to ensure it does not - // leave any cached data on the FaaS instance. - this.clearTmpDirectory() - return { result, userHttpQueries: functionsModule.userHttpQueries } - } - clearTmpDirectory() { - if (this.disableTmpClearing) { - return - } - fs_1.default.readdirSync(os_1.default.tmpdir()).forEach((dirent) => { - try { - fs_1.default.rmSync(path_1.default.join(os_1.default.tmpdir(), dirent), { recursive: true }) - } catch {} - }) - } -} -exports.Sandbox = Sandbox -class SandboxError { - constructor(name, message, details, userHttpQueries) { - this.name = name - this.message = message - this.details = details - this.userHttpQueries = userHttpQueries - } -} -exports.SandboxError = SandboxError diff --git a/contracts/FunctionsSandboxLibrary/Validator.js b/contracts/FunctionsSandboxLibrary/Validator.js deleted file mode 100644 index d1cf8d5..0000000 --- a/contracts/FunctionsSandboxLibrary/Validator.js +++ /dev/null @@ -1,69 +0,0 @@ -"use strict" -Object.defineProperty(exports, "__esModule", { value: true }) -exports.Validator = void 0 -class Validator { - constructor(defaultMaxResponseBytes, defaultMaxHttpQueries) { - this.defaultMaxResponseBytes = defaultMaxResponseBytes - this.defaultMaxHttpQueries = defaultMaxHttpQueries - this.isValidInput = (input) => { - const validInput = input - if (typeof validInput.source !== "string") { - throw Error("source param is missing") - } - if (validInput.requestId && typeof validInput.requestId !== "string") { - throw Error("requestId param not a string or number") - } - if (validInput.numAllowedQueries) { - if (typeof validInput.numAllowedQueries !== "number" || !Number.isInteger(validInput.numAllowedQueries)) { - throw Error("numAllowedQueries not integer") - } - } else { - validInput.numAllowedQueries = this.defaultMaxHttpQueries - } - if (validInput.args) { - if (!Array.isArray(validInput.args)) { - throw Error("args param not an array") - } - for (const arg of validInput.args) { - if (typeof arg !== "string") { - throw Error("args param not a string array") - } - } - } - if ( - validInput.secrets && - (typeof validInput.secrets !== "object" || - !Object.values(validInput.secrets).every((s) => { - return typeof s === "string" - })) - ) { - throw Error("secrets param not a string map") - } - this.maxResponseBytes = this.defaultMaxResponseBytes - if (validInput.maxResponseBytes) { - if (typeof validInput.maxResponseBytes !== "number" || !Number.isInteger(validInput.maxResponseBytes)) { - throw Error("maxResponseBytes not integer") - } - this.maxResponseBytes = validInput.maxResponseBytes - } - return true - } - this.getValidOutput = (sandboxOutput) => { - if (Buffer.isBuffer(sandboxOutput.result)) { - if (sandboxOutput.result.length <= this.maxResponseBytes) { - return sandboxOutput.result - } - throw Error(`returned Buffer >${this.maxResponseBytes} bytes`) - } - throw Error("returned value not a Buffer") - } - this.encodeResponse = (result) => { - if (result.length === 0) { - return "0x0" - } - return "0x" + result.toString("hex") - } - this.maxResponseBytes = defaultMaxResponseBytes - } -} -exports.Validator = Validator diff --git a/contracts/FunctionsSandboxLibrary/buildRequest.js b/contracts/FunctionsSandboxLibrary/buildRequest.js deleted file mode 100644 index 5975eea..0000000 --- a/contracts/FunctionsSandboxLibrary/buildRequest.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict" -Object.defineProperty(exports, "__esModule", { value: true }) -exports.buildRequest = void 0 -const getRequestConfig_1 = require("./getRequestConfig") -const encryptSecrets_1 = require("./encryptSecrets") -const buildRequest = async (unvalidatedConfig) => { - const config = (0, getRequestConfig_1.getRequestConfig)(unvalidatedConfig) - const request = { source: config.source } - if (config.secretsURLs && config.secretsURLs.length > 0) { - request.secrets = "0x" + (await (0, encryptSecrets_1.encrypt)(config.DONPublicKey, config.secretsURLs.join(" "))) - } - if (config.args) { - request.args = config.args - } - return request -} -exports.buildRequest = buildRequest diff --git a/contracts/FunctionsSandboxLibrary/encryptSecrets.js b/contracts/FunctionsSandboxLibrary/encryptSecrets.js deleted file mode 100644 index 8c01c7f..0000000 --- a/contracts/FunctionsSandboxLibrary/encryptSecrets.js +++ /dev/null @@ -1,23 +0,0 @@ -"use strict" -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, "__esModule", { value: true }) -exports.encrypt = exports.encryptWithSignature = void 0 -const eth_crypto_1 = __importDefault(require("eth-crypto")) -const encryptWithSignature = async (signerPrivateKey, readerPublicKey, message) => { - const signature = eth_crypto_1.default.sign(signerPrivateKey, eth_crypto_1.default.hash.keccak256(message)) - const payload = { - message, - signature, - } - return await (0, exports.encrypt)(readerPublicKey, JSON.stringify(payload)) -} -exports.encryptWithSignature = encryptWithSignature -const encrypt = async (readerPublicKey, message) => { - const encrypted = await eth_crypto_1.default.encryptWithPublicKey(readerPublicKey, message) - return eth_crypto_1.default.cipher.stringify(encrypted) -} -exports.encrypt = encrypt diff --git a/contracts/FunctionsSandboxLibrary/getRequestConfig.js b/contracts/FunctionsSandboxLibrary/getRequestConfig.js deleted file mode 100644 index 3b7df89..0000000 --- a/contracts/FunctionsSandboxLibrary/getRequestConfig.js +++ /dev/null @@ -1,90 +0,0 @@ -"use strict" -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, "__esModule", { value: true }) -exports.getRequestConfig = exports.Location_ = void 0 -const is_http_url_1 = __importDefault(require("is-http-url")) -var Location_ -;(function (Location_) { - Location_[(Location_["Inline"] = 0)] = "Inline" - Location_[(Location_["Remote"] = 1)] = "Remote" -})((Location_ = exports.Location_ || (exports.Location_ = {}))) -var CodeLanguage -;(function (CodeLanguage) { - CodeLanguage[(CodeLanguage["JavaScript"] = 0)] = "JavaScript" -})(CodeLanguage || (CodeLanguage = {})) -const getRequestConfig = (unvalidatedConfig) => { - const config = unvalidatedConfig - if (config.codeLocation !== Location_.Inline) { - throw Error(`codeLocation is not correctly specified in config`) - } - if (config.codeLanguage !== CodeLanguage.JavaScript) { - throw Error(`codeLanguage is not correctly specified in config`) - } - if (typeof config.source !== "string") { - throw Error(`source is not correctly specified in config`) - } - if (config.numAllowedQueries) { - if (typeof config.numAllowedQueries !== "number" || !Number.isInteger(config.numAllowedQueries)) { - throw Error(`numAllowedQueries is not correctly specified in config`) - } - } - if (config.secrets) { - if (typeof config.secrets !== "object") { - throw Error("secrets object is not correctly specified in config") - } - for (const secret in config.secrets) { - if (typeof config.secrets[secret] !== "string") { - throw Error("Secrets object is not correctly specified in config. It can only contain string values.") - } - } - } - if (config.secretsURLs && config.secretsURLs.length > 0) { - if (!Array.isArray(config.secretsURLs)) { - throw Error("secretsURLs array is not correctly specified in config") - } - config.secretsURLs.forEach((s) => { - if (!(0, is_http_url_1.default)(s)) { - throw Error(`invalid HTTP or HTTPs URL ${s} in secretsURLs specified in config`) - } - }) - if (typeof config.walletPrivateKey !== "string") { - throw Error(`walletPrivateKey is not correctly specified in config`) - } - if (config.DONPublicKey && typeof config.DONPublicKey !== "string") { - throw Error(`DONPublicKey is not correctly specified in config`) - } - } - if (config.args) { - if (!Array.isArray(config.args)) throw Error(`args array is not correctly specified in config`) - for (const arg of config.args) { - if (typeof arg !== "string") { - throw Error(`an element of the args array is not a string in config`) - } - } - } - if (config.maxResponseBytes) { - if (typeof config.maxResponseBytes !== "number" || !Number.isInteger(config.maxResponseBytes)) { - throw Error(`maxResponseBytes is not correctly specified in config`) - } - } - if (config.expectedReturnType) { - if (typeof config.expectedReturnType !== "string") { - throw Error(`expectedReturnType is not correctly specified in config`) - } - switch (config.expectedReturnType) { - case "uint256": - case "int256": - case "string": - case "Buffer": - break - default: - throw Error(`expectedReturnType is not correctly specified in config`) - } - } - return config -} -exports.getRequestConfig = getRequestConfig diff --git a/contracts/FunctionsSandboxLibrary/handler.js b/contracts/FunctionsSandboxLibrary/handler.js deleted file mode 100644 index 1418a77..0000000 --- a/contracts/FunctionsSandboxLibrary/handler.js +++ /dev/null @@ -1,92 +0,0 @@ -"use strict" -var __importDefault = - (this && this.__importDefault) || - function (mod) { - return mod && mod.__esModule ? mod : { default: mod } - } -Object.defineProperty(exports, "__esModule", { value: true }) -exports.handler = void 0 -const process_1 = __importDefault(require("process")) -const Log_1 = require("./Log") -const Validator_1 = require("./Validator") -const Sandbox_1 = require("./Sandbox") -const validator = new Validator_1.Validator( - parseInt(process_1.default.env["DEFAULT_MAX_RESPONSE_BYTES"] ?? "256"), - parseInt(process_1.default.env["DEFAULT_MAX_HTTP_QUERIES"] ?? "5") -) -const handler = async (event, _) => { - // Validate the request - try { - // This is wrapped in an `if` statement for TypeScript's type checking system - if (!validator.isValidInput(event)) { - throw Error("isValidInput should never return false.") - } - } catch (untypedError) { - const error = untypedError - Log_1.Log.debug(error.toString()) - return buildResult({ - error: { - name: "Invalid Input", - message: `${error.message}`, - }, - }) - } - Log_1.Log.trace("Valid Event Initiated", event.requestId) - const sandbox = new Sandbox_1.Sandbox( - process_1.default.env["DISABLE_TMP_CLEAR_FOR_TESTING"] === "true", - process_1.default.env["ENABLE_CONSOLE_LOG_FROM_SANDBOX"] === "true" - ) - // Execute the user-provided code in the sandbox - let output - try { - output = await sandbox.evaluate(event.numAllowedQueries, event.source, event.args, event.secrets) - } catch (untypedError) { - const sandboxError = untypedError - Log_1.Log.trace(JSON.stringify(sandboxError), event.requestId) - return buildResult({ - error: { ...sandboxError }, - }) - } - // Place in a try-catch in case of a cyclic reference in the returned object (or some other error) - try { - Log_1.Log.trace(`Sandbox Output: ${JSON.stringify(output)}`, event.requestId) - } catch { - Log_1.Log.debug( - "Sandbox output cannot be stringified (likely due to a circular reference in returned value)", - event.requestId - ) - return buildResult({ - error: { - name: "Output Validation Error", - message: `returned type ${typeof output} is not supported`, - userHttpQueries: output.userHttpQueries, - }, - }) - } - let result - try { - result = validator.getValidOutput(output) - } catch (untypedError) { - const error = untypedError - Log_1.Log.trace(`Invalid Output: ${JSON.stringify(output)}`, event.requestId) - return buildResult({ - error: { - name: "Output Validation Error", - message: error.message, - }, - }) - } - const success = validator.encodeResponse(result) - Log_1.Log.trace(`Success: ${success}`) - return buildResult({ - success, - userHttpQueries: output.userHttpQueries, - }) -} -exports.handler = handler -const buildResult = (result) => { - return { - statusCode: 200, - body: JSON.stringify(result), - } -} diff --git a/contracts/FunctionsSandboxLibrary/index.js b/contracts/FunctionsSandboxLibrary/index.js deleted file mode 100644 index 7ed9286..0000000 --- a/contracts/FunctionsSandboxLibrary/index.js +++ /dev/null @@ -1,30 +0,0 @@ -"use strict" -Object.defineProperty(exports, "__esModule", { value: true }) -exports.getRequestConfig = exports.buildRequest = exports.getDecodedResultLog = exports.simulateRequest = void 0 -var simulateRequest_1 = require("./simulateRequest") -Object.defineProperty(exports, "simulateRequest", { - enumerable: true, - get: function () { - return simulateRequest_1.simulateRequest - }, -}) -Object.defineProperty(exports, "getDecodedResultLog", { - enumerable: true, - get: function () { - return simulateRequest_1.getDecodedResultLog - }, -}) -var buildRequest_1 = require("./buildRequest") -Object.defineProperty(exports, "buildRequest", { - enumerable: true, - get: function () { - return buildRequest_1.buildRequest - }, -}) -var getRequestConfig_1 = require("./getRequestConfig") -Object.defineProperty(exports, "getRequestConfig", { - enumerable: true, - get: function () { - return getRequestConfig_1.getRequestConfig - }, -}) diff --git a/contracts/FunctionsSandboxLibrary/simulateRequest.js b/contracts/FunctionsSandboxLibrary/simulateRequest.js deleted file mode 100644 index 5c98bd4..0000000 --- a/contracts/FunctionsSandboxLibrary/simulateRequest.js +++ /dev/null @@ -1,79 +0,0 @@ -"use strict" -Object.defineProperty(exports, "__esModule", { value: true }) -exports.getDecodedResultLog = exports.simulateRequest = void 0 -const getRequestConfig_1 = require("./getRequestConfig") -const handler_1 = require("./handler") -const simulateRequest = async (unvalidatedConfig) => { - const config = (0, getRequestConfig_1.getRequestConfig)(unvalidatedConfig) - const savedEnv = { - DISABLE_TMP_CLEAR_FOR_TESTING: process.env["DISABLE_TMP_CLEAR_FOR_TESTING"], - ENABLE_CONSOLE_LOG_FROM_SANDBOX: process.env["ENABLE_CONSOLE_LOG_FROM_SANDBOX"], - } - process.env["DISABLE_TMP_CLEAR_FOR_TESTING"] = "true" - process.env["ENABLE_CONSOLE_LOG_FROM_SANDBOX"] = "true" - const timeout = setTimeout(() => { - throw Error("Runtime of 15 seconds for sandboxed source code has been exceeded") - }, 15000) - const resultString = ( - await (0, handler_1.handler)({ - source: config.source, - numAllowedQueries: config.numAllowedQueries, - args: config.args, - secrets: config.secrets, - maxResponseBytes: config.maxResponseBytes, - }) - ).body - clearTimeout(timeout) - for (const envVar in savedEnv) { - process.env[envVar] = savedEnv[envVar] - } - const result = JSON.parse(resultString) - if (result.success) { - return { - success: true, - result: result.success, - resultLog: `__Output from sandboxed source code__\nOutput represented as a hex string: ${result.success}\n${(0, - exports.getDecodedResultLog)(config, result.success)}`, - } - } - const { message } = result.error - const errorString = `${message}`.slice(0, config.maxResponseBytes) - return { - success: false, - result: `0x${Buffer.from(errorString).toString("hex")}`, - resultLog: `__Error thrown in sandboxed source code__\n${message}\n`, - } -} -exports.simulateRequest = simulateRequest -const getDecodedResultLog = (config, successResult) => { - let resultLog = "" - if (config.expectedReturnType && config.expectedReturnType !== "Buffer") { - let decodedOutput - switch (config.expectedReturnType) { - case "uint256": - decodedOutput = BigInt("0x" + successResult.slice(2).slice(-64)) - break - case "int256": - decodedOutput = signedInt256toBigInt("0x" + successResult.slice(2).slice(-64)) - break - case "string": - decodedOutput = Buffer.from(successResult.slice(2), "hex").toString() - break - default: - const end = config.expectedReturnType - throw new Error(`unused expectedReturnType ${end}`) - } - const decodedOutputLog = `Decoded as a ${config.expectedReturnType}: ${decodedOutput}` - resultLog += `${decodedOutputLog}\n` - } - return resultLog -} -exports.getDecodedResultLog = getDecodedResultLog -const signedInt256toBigInt = (hex) => { - const binary = BigInt(hex).toString(2).padStart(256, "0") - // if the first bit is 0, number is positive - if (binary[0] === "0") { - return BigInt(hex) - } - return -(BigInt(2) ** BigInt(255)) + BigInt(`0b${binary.slice(1)}`) -} diff --git a/contracts/README.md b/contracts/README.md index e8295df..a0085bb 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -34,8 +34,8 @@ All required configuration for supported networks is located in the [`networks.j - _API_KEY_ for the sports results API. Obtain one [here](https://dashboard.api-football.com/register). 3. If desired, the `_API_KEY` can be set in order to verify contracts, along with any values used in the _secrets_ object in _Functions-request-config.js_ such as `COINMARKETCAP_API_KEY`.

4. Test an end-to-end request and fulfillment locally by simulating it using:
`npx hardhat functions-simulate`

-5. Run the unit and integration tests by running:
`npx hardhat test`

-6. Before deploying the contract a new Functions billing subscription must be created and funded to be able to request sports results:
`npx hardhat functions-sub-create --network network_name_here --amount LINK_funding_amount_here`
**Note**: Ensure your wallet has a sufficient LINK balance before running this command. Testnet LINK can be obtained at faucets.chain.link.

+5. Run the unit tests by running:
`npx hardhat test`

+6. Before deploying the contract a new Functions billing subscription must be created and funded to be able to request sports results. You can do it from the Functions web interface at [https://functions.chain.link](https://functions.chain.link).

7. Deploy and configure the game contract to an actual blockchain network by running:
`npx hardhat deploy-game --network network_name_here --destination network_name_here --subid your_sub_id --verify true` - `network` is the network where the game contract will be deployed - `destination` is the network where the winnings receiver contract will be deployed @@ -53,14 +53,9 @@ Content below is general knowkedge from [function-hardhat-starter-kit](https://g - [Environment Variable Management](#environment-variable-management) - [Environment Variable Management Commands](#environment-variable-management-commands) -- [Functions Command Glossary](#functions-command-glossary) - - [Functions Commands](#functions-commands) - - [Functions Subscription Management Commands](#functions-subscription-management-commands) - [Request Configuration](#request-configuration) - [JavaScript Code](#javascript-code) - [Functions Library](#functions-library) - - [Modifying Contracts](#modifying-contracts) - - [Simulating Requests](#simulating-requests) - [Off-chain Secrets](#off-chain-secrets) - [Automation Integration](#automation-integration) - [Gas Spikes](#gas-spikes) @@ -101,44 +96,6 @@ The `--path` flag has no effect on the `npx env-enc set-pw` command as the passw | `npx env-enc remove ` | Removes a variable from the encrypted environment variable file | `name`: Variable name | | `npx env-enc remove-all` | Deletes the encrypted environment variable file | | -# Functions Command Glossary - -The Functions and Functions subscription management commands commands can be executed in the following format: -`npx hardhat command_here --parameter1 parameter_1_value_here --parameter2 parameter_2_value_here` - -Example: `npx hardhat functions-read --network polygonMumbai --contract 0x787Fe00416140b37B026f3605c6C72d096110Bb8` - -## Functions Commands - -| Command | Description | Parameters | -| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `compile` | Compiles all smart contracts | | -| `functions-simulate` | Simulates an end-to-end fulfillment locally for the _FunctionsConsumer_ contract | `gaslimit` (optional): Maximum amount of gas that can be used to call _fulfillRequest_ in the client contract (defaults to 100,000 & must be less than 300,000), `configpath` (optional): Path to request config file (defaults to _./Functions-request-config.js_) | -| `functions-deploy-client` | Deploys the _FunctionsConsumer_ contract | `network`: Name of blockchain network, `verify` (optional): Set to `true` to verify the deployed _FunctionsConsumer_ contract (defaults to `false`) | -| `functions-request` | Initiates a request from a _FunctionsConsumer_ client contract using data from the Functions request config file | `network`: Name of blockchain network, `contract`: Address of the client contract to call, `subid`: Billing subscription ID used to pay for the request, `gaslimit` (optional): Maximum amount of gas that can be used to call _fulfillRequest_ in the client contract (defaults to 100,000 & must be less than 300,000), `requestgas` (optional): Gas limit for calling the _executeRequest_ function (defaults to 1,500,000), `simulate` (optional): Flag indicating if simulation should be run before making an on-chain request (defaults to true), `configpath` (optional): Path to request config file (defaults to _./Functions-request-config.js_) | -| `functions-read` | Reads the latest response (or error) returned to a _FunctionsConsumer_ or _AutomatedFunctionsConsumer_ client contract | `network`: Name of blockchain network, `contract`: Address of the client contract to read, `configpath` (optional): Path to request config file (defaults to _./Functions-request-config.js_) | -| `functions-deploy-auto-client` | Deploys the _AutomatedFunctionsConsumer_ contract and sets the Functions request using data from the Functions request config file | `network`: Name of blockchain network, `subid`: Billing subscription ID used to pay for Functions requests, `gaslimit` (optional): Maximum amount of gas that can be used to call _fulfillRequest_ in the client contract (defaults to 250000), `interval` (optional): Update interval in seconds for Chainlink Automation to call _performUpkeep_ (defaults to 300), `verify` (optional): Set to `true` to verify the deployed _AutomatedFunctionsConsumer_ contract (defaults to `false`), `simulate` (optional): Flag indicating if simulation should be run before making an on-chain request (defaults to true), `configpath` (optional): Path to request config file (defaults to _./Functions-request-config.js_) | -| `functions-check-upkeep` | Checks if _checkUpkeep_ returns true for an Automation compatible contract | `network`: Name of blockchain network, `contract`: Address of the contract to check, `data` (optional): Hex string representing bytes that are passed to the _checkUpkeep_ function (defaults to empty bytes) | -| `functions-perform-upkeep` | Manually call _performUpkeep_ in an Automation compatible contract | `network`: Name of blockchain network, `contract`: Address of the contract to call, `data` (optional): Hex string representing bytes that are passed to the _performUpkeep_ function (defaults to empty bytes) | -| `functions-set-auto-request` | Updates the Functions request in deployed _AutomatedFunctionsConsumer_ contract using data from the Functions request config file | `network`: Name of blockchain network, `contract`: Address of the contract to update, `subid`: Billing subscription ID used to pay for Functions requests, `interval` (optional): Update interval in seconds for Chainlink Automation to call _performUpkeep_ (defaults to 300), `gaslimit` (optional): Maximum amount of gas that can be used to call _fulfillRequest_ in the client contract (defaults to 250,000), `configpath` (optional): Path to request config file (defaults to _./Functions-request-config.js_) | -| `functions-set-oracle-addr` | Updates the oracle address for a client contract using the _FunctionsOracle_ address from _network-config.js_ | `network`: Name of blockchain network, `contract`: Address of the client contract to update | -| `functions-build-request` | Creates a JSON file with Functions request parameters including encrypted secrets, using data from the Functions request config file | `network`: Name of blockchain network, `output` (optional): Output JSON file name (defaults to _Functions-request.json_), `simulate` (optional): Flag indicating if simulation should be run before building the request JSON file (defaults to true), `configpath` (optional): Path to request config file (defaults to _./Functions-request-config.js_) | -| `functions-build-offchain-secrets` | Builds an off-chain secrets object that can be uploaded and referenced via URL | `network`: Name of blockchain network, `output` (optional): Output JSON file name (defaults to _offchain-secrets.json_), `configpath` (optional): Path to request config file (defaults to _./Functions-request-config.js_) | - -## Functions Subscription Management Commands - -| Command | Description | Parameters | -| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `functions-sub-create` | Creates a new Functions billing subscription for Functions client contracts | `network`: Name of blockchain network, `amount` (optional): Initial amount used to fund the subscription in LINK (decimals are accepted), `contract` (optional): Address of the client contract to add to the subscription | -| `functions-sub-info` | Gets the Functions billing subscription balance, owner, and list of authorized client contract addresses | `network`: Name of blockchain network, `subid`: Subscription ID | -| `functions-sub-fund` | Funds a Functions billing subscription with LINK | `network`: Name of blockchain network, `subid`: Subscription ID, `amount`: Amount to fund subscription in LINK (decimals are accepted) | -| `functions-sub-cancel` | Cancels a Functions billing subscription and refunds the unused balance. Cancellation is only possible if there are no pending requests. | `network`: Name of blockchain network, `subid`: Subscription ID, `refundaddress` (optional): Address where the remaining subscription balance is sent (defaults to caller's address) | -| `functions-sub-add` | Authorizes a client contract to use the Functions billing subscription | `network`: Name of blockchain network, `subid`: Subscription ID, `contract`: Address of the client contract to authorize for billing | -| `functions-sub-remove` | Removes a client contract from a Functions billing subscription | `network`: Name of blockchain network, `subid`: Subscription ID, `contract`: Address of the client contract to remove from billing subscription | -| `functions-sub-transfer` | Request ownership of a Functions subscription be transferred to a new address | `network`: Name of blockchain network, `subid`: Subscription ID, `newowner`: Address of the new owner | -| `functions-sub-accept` | Accepts ownership of a Functions subscription after a transfer is requested | `network`: Name of blockchain network, `subid`: Subscription ID | -| `functions-timeout-requests` | Times out expired requests | `network`: Name of blockchain network, `requestids`: 1 or more request IDs to timeout separated by commas | - # Request Configuration Chainlink Functions requests can be configured by modifying values in the `requestConfig` object found in the _Functions-request-config.js_ file located in the root of this repository. @@ -218,17 +175,6 @@ This library also exposes functions for encoding JavaScript values into Buffers Remember, it is not required to use these encoding functions. The JavaScript code must only return a Buffer which represents the `bytes` array that is returned on-chain. -## Modifying Contracts - -Client contracts which initiate a request and receive a fulfillment can be modified for specific use cases. The only requirements are that the contract successfully calls _sendRequest_ in the _FunctionsOracle_ contract and correctly implements their own _handleOracleFulfillment_ function. At this time, the maximum amount of gas that _handleOracleFulfillment_ can use is 300,000. See _FunctionsClient.sol_ for details. - -## Simulating Requests - -An end-to-end request initiation and fulfillment can be simulated for the default _FunctionsConsumer_ contract using the `functions-simulate` command. This command will report the total estimated gas use. -If the _FunctionsConsumer_ client contract is modified, this task must also be modified to accomodate the changes. See `tasks/Functions-client/simulate` for details. - -**Note:** The actual gas use on-chain can vary, so it is recommended to set a higher fulfillment gas limit when making a request to account for any differences. - ## Off-chain Secrets Instead of using encrypted secrets written directly on the blockchain, encrypted secrets are hosted off-chain and be fetched by DON nodes via HTTP when a request is initiated. This allows encrypted secrets to be deleted when they are no longer in use. By default, the tooling automatically uploads secrets to private Github Gists and deletes them once a request is fulfilled unless the secrets are being used for an `AutomatedFunctionsConsumer.sol` contract. If integrating with Chainlink Automation, it is recommended to delete the secrets Gist manually once it is not longer in use. Note that if there are URL(s) provided for the `secretsURLs` parameter in _Functions_request_config.js_, automatic Gist uploading will be disabled in favor of using the provided URL(s). diff --git a/contracts/contracts/ResultsConsumer.sol b/contracts/contracts/ResultsConsumer.sol index b6db7c5..4415f71 100644 --- a/contracts/contracts/ResultsConsumer.sol +++ b/contracts/contracts/ResultsConsumer.sol @@ -1,13 +1,17 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.0; -import {Functions, FunctionsClient} from "./dev/functions/FunctionsClient.sol"; +import {FunctionsClient} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/FunctionsClient.sol"; +import {FunctionsRequest} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/libraries/FunctionsRequest.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; /// @title ResultsConsumer /// @notice Requests and receives sports results using Chainlink Functions abstract contract ResultsConsumer is FunctionsClient { - using Functions for Functions.Request; + using FunctionsRequest for FunctionsRequest.Request; + + /// @notice The gas limit for the sports API request callback + uint32 private constant GAS_LIMIT = 250000; /// @notice The source code for the sports API request string private source; @@ -15,8 +19,8 @@ abstract contract ResultsConsumer is FunctionsClient { bytes private secrets; /// @notice The subscription ID for Chainlink Functions uint64 private subscriptionId; - /// @notice The gas limit for the sports API request callback - uint32 private gasLimit; + /// @notice The ID of the Chainlink oracle network + bytes32 public donId; /// @notice The pending Functions requests mapping(bytes32 => PendingRequest) private pending; @@ -39,21 +43,21 @@ abstract contract ResultsConsumer is FunctionsClient { /// @notice Initializes the contract /// @param _oracle The address of the Chainlink Function oracle + /// @param _donId The ID of the Chainlink oracle network /// @param _subscriptionId The subscription ID for Chainlink Functions /// @param _source The source code for the Chainlink Functions request /// @param _secrets The secrets used in the Chainlink Functions request - /// @param _gasLimit The gas limit for the Chainlink Functions request callback constructor( address _oracle, + bytes32 _donId, uint64 _subscriptionId, string memory _source, - bytes memory _secrets, - uint32 _gasLimit + bytes memory _secrets ) FunctionsClient(_oracle) { + donId = _donId; subscriptionId = _subscriptionId; source = _source; secrets = _secrets; - gasLimit = _gasLimit; } // INTERNAL @@ -79,13 +83,13 @@ abstract contract ResultsConsumer is FunctionsClient { /// @param args The arguments for the Chainlink Functions request /// @return requestId The Chainlink Functions request ID function _executeRequest(string[] memory args) internal returns (bytes32 requestId) { - Functions.Request memory req; - req.initializeRequest(Functions.Location.Inline, Functions.CodeLanguage.JavaScript, source); + FunctionsRequest.Request memory req; + req.initializeRequest(FunctionsRequest.Location.Inline, FunctionsRequest.CodeLanguage.JavaScript, source); if (secrets.length > 0) { - req.addRemoteSecrets(secrets); + req.addSecretsReference(secrets); } - if (args.length > 0) req.addArgs(args); - requestId = sendRequest(req, subscriptionId, gasLimit); + if (args.length > 0) req.setArgs(args); + requestId = _sendRequest(req.encodeCBOR(), subscriptionId, GAS_LIMIT, donId); } /// @notice Processes the result of a sports API request diff --git a/contracts/contracts/SportsPredictionGame.sol b/contracts/contracts/SportsPredictionGame.sol index 4a16629..175654d 100644 --- a/contracts/contracts/SportsPredictionGame.sol +++ b/contracts/contracts/SportsPredictionGame.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.0; import {ResultsConsumer} from "./ResultsConsumer.sol"; import {NativeTokenSender} from "./ccip/NativeTokenSender.sol"; -import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/AutomationCompatible.sol"; +import {AutomationCompatibleInterface} from "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol"; // Configuration parameters for initializing the contract struct Config { @@ -15,7 +15,7 @@ struct Config { address uniswapV3Router; // The address of the Uniswap V3 router uint64 subscriptionId; // The ID of the Chainlink Functions subscription uint64 destinationChainSelector; // The chain selector for the winnings transfer destination chain - uint32 gasLimit; // The gas limit for the Chainlink Functions request callback + bytes32 donId; // The ID of the Chainlink oracle network bytes secrets; // The secrets for the Chainlink Functions request string source; // The source code for the Chainlink Functions request } @@ -96,7 +96,7 @@ contract SportsPredictionGame is ResultsConsumer, NativeTokenSender, AutomationC constructor( Config memory config ) - ResultsConsumer(config.oracle, config.subscriptionId, config.source, config.secrets, config.gasLimit) + ResultsConsumer(config.oracle, config.donId, config.subscriptionId, config.source, config.secrets) NativeTokenSender( config.ccipRouter, config.link, diff --git a/contracts/contracts/ccip/NativeTokenReceiver.sol b/contracts/contracts/ccip/NativeTokenReceiver.sol index ca466cf..aae97eb 100644 --- a/contracts/contracts/ccip/NativeTokenReceiver.sol +++ b/contracts/contracts/ccip/NativeTokenReceiver.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.0; import {ProgrammableTokenReceiver} from "./ProgrammableTokenReceiver.sol"; import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; diff --git a/contracts/contracts/ccip/NativeTokenSender.sol b/contracts/contracts/ccip/NativeTokenSender.sol index 0415b0a..75fa262 100644 --- a/contracts/contracts/ccip/NativeTokenSender.sol +++ b/contracts/contracts/ccip/NativeTokenSender.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; +pragma solidity ^0.8.0; import {ProgrammableTokenSender} from "./ProgrammableTokenSender.sol"; import {ISwapRouter} from "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; diff --git a/contracts/contracts/ccip/ProgrammableTokenReceiver.sol b/contracts/contracts/ccip/ProgrammableTokenReceiver.sol index f9683db..44bdfd1 100644 --- a/contracts/contracts/ccip/ProgrammableTokenReceiver.sol +++ b/contracts/contracts/ccip/ProgrammableTokenReceiver.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.7; +pragma solidity ^0.8.0; import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol"; import {OwnerIsCreator} from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol"; diff --git a/contracts/contracts/ccip/ProgrammableTokenSender.sol b/contracts/contracts/ccip/ProgrammableTokenSender.sol index c1004d0..6bf5372 100644 --- a/contracts/contracts/ccip/ProgrammableTokenSender.sol +++ b/contracts/contracts/ccip/ProgrammableTokenSender.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.7; +pragma solidity ^0.8.0; import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol"; import {OwnerIsCreator} from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol"; import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/token/ERC20/IERC20.sol"; -import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; +import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"; /// @title ProgrammableTokenSender abstract contract ProgrammableTokenSender is OwnerIsCreator { diff --git a/contracts/contracts/dev/AuthorizedReceiver.sol b/contracts/contracts/dev/AuthorizedReceiver.sol deleted file mode 100644 index 9128f55..0000000 --- a/contracts/contracts/dev/AuthorizedReceiver.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -import "./vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/structs/EnumerableSet.sol"; -import "./interfaces/AuthorizedReceiverInterface.sol"; - -abstract contract AuthorizedReceiver is AuthorizedReceiverInterface { - using EnumerableSet for EnumerableSet.AddressSet; - - event AuthorizedSendersChanged(address[] senders, address changedBy); - - error EmptySendersList(); - error UnauthorizedSender(); - error NotAllowedToSetSenders(); - - EnumerableSet.AddressSet private s_authorizedSenders; - address[] private s_authorizedSendersList; - - /** - * @notice Sets the fulfillment permission for a given node. Use `true` to allow, `false` to disallow. - * @param senders The addresses of the authorized Chainlink node - */ - function setAuthorizedSenders(address[] calldata senders) external override validateAuthorizedSenderSetter { - if (senders.length == 0) { - revert EmptySendersList(); - } - for (uint256 i = 0; i < s_authorizedSendersList.length; i++) { - s_authorizedSenders.remove(s_authorizedSendersList[i]); - } - for (uint256 i = 0; i < senders.length; i++) { - s_authorizedSenders.add(senders[i]); - } - s_authorizedSendersList = senders; - emit AuthorizedSendersChanged(senders, msg.sender); - } - - /** - * @notice Retrieve a list of authorized senders - * @return array of addresses - */ - function getAuthorizedSenders() public view override returns (address[] memory) { - return s_authorizedSendersList; - } - - /** - * @notice Use this to check if a node is authorized for fulfilling requests - * @param sender The address of the Chainlink node - * @return The authorization status of the node - */ - function isAuthorizedSender(address sender) public view override returns (bool) { - return s_authorizedSenders.contains(sender); - } - - /** - * @notice customizable guard of who can update the authorized sender list - * @return bool whether sender can update authorized sender list - */ - function _canSetAuthorizedSenders() internal virtual returns (bool); - - /** - * @notice validates the sender is an authorized sender - */ - function _validateIsAuthorizedSender() internal view { - if (!isAuthorizedSender(msg.sender)) { - revert UnauthorizedSender(); - } - } - - /** - * @notice prevents non-authorized addresses from calling this method - */ - modifier validateAuthorizedSender() { - _validateIsAuthorizedSender(); - _; - } - - /** - * @notice prevents non-authorized addresses from calling this method - */ - modifier validateAuthorizedSenderSetter() { - if (!_canSetAuthorizedSenders()) { - revert NotAllowedToSetSenders(); - } - _; - } -} diff --git a/contracts/contracts/dev/functions/AuthorizedOriginReceiverUpgradeable.sol b/contracts/contracts/dev/functions/AuthorizedOriginReceiverUpgradeable.sol deleted file mode 100644 index 4dbf1c1..0000000 --- a/contracts/contracts/dev/functions/AuthorizedOriginReceiverUpgradeable.sol +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -import {EnumerableSet} from "../vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/structs/EnumerableSet.sol"; -import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -/** - * @notice Modified AuthorizedReciever abstract for use on the FunctionsOracle contract to limit usage - * @notice Uses tx.origin instead of msg.sender because the client contract sends messages to the Oracle contract - */ - -abstract contract AuthorizedOriginReceiverUpgradeable is Initializable { - using EnumerableSet for EnumerableSet.AddressSet; - - event AuthorizedSendersChanged(address[] senders, address changedBy); - event AuthorizedSendersActive(address account); - event AuthorizedSendersDeactive(address account); - - error EmptySendersList(); - error UnauthorizedSender(); - error NotAllowedToSetSenders(); - error AlreadySet(); - - bool private s_active; - EnumerableSet.AddressSet private s_authorizedSenders; - - /** - * @dev Initializes the contract in active state. - */ - function __AuthorizedOriginReceiver_initialize(bool active) internal onlyInitializing { - s_active = active; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function authorizedReceiverActive() public view virtual returns (bool) { - return s_active; - } - - /** - * @dev Triggers AuthorizedOriginReceiver usage to block unuthorized senders. - * - * Requirements: - * - * - The contract must not be deactive. - */ - function activateAuthorizedReceiver() external validateAuthorizedSenderSetter { - if (authorizedReceiverActive()) { - revert AlreadySet(); - } - s_active = true; - emit AuthorizedSendersActive(msg.sender); - } - - /** - * @dev Triggers AuthorizedOriginReceiver usage to allow all senders. - * - * Requirements: - * - * - The contract must be active. - */ - function deactivateAuthorizedReceiver() external validateAuthorizedSenderSetter { - if (!authorizedReceiverActive()) { - revert AlreadySet(); - } - s_active = false; - emit AuthorizedSendersDeactive(msg.sender); - } - - /** - * @notice Sets the permission to request for the given wallet(s). - * @param senders The addresses of the wallet addresses to grant access - */ - function addAuthorizedSenders(address[] calldata senders) external validateAuthorizedSenderSetter { - if (senders.length == 0) { - revert EmptySendersList(); - } - for (uint256 i = 0; i < senders.length; i++) { - s_authorizedSenders.add(senders[i]); - } - emit AuthorizedSendersChanged(senders, msg.sender); - } - - /** - * @notice Remove the permission to request for the given wallet(s). - * @param senders The addresses of the wallet addresses to revoke access - */ - function removeAuthorizedSenders(address[] calldata senders) external validateAuthorizedSenderSetter { - if (senders.length == 0) { - revert EmptySendersList(); - } - for (uint256 i = 0; i < senders.length; i++) { - s_authorizedSenders.remove(senders[i]); - } - emit AuthorizedSendersChanged(senders, msg.sender); - } - - /** - * @notice Retrieve a list of authorized senders - * @return array of addresses - */ - function getAuthorizedSenders() public view returns (address[] memory) { - return EnumerableSet.values(s_authorizedSenders); - } - - /** - * @notice Use this to check if a node is authorized for fulfilling requests - * @param sender The address of the Chainlink node - * @return The authorization status of the node - */ - function isAuthorizedSender(address sender) public view returns (bool) { - if (!authorizedReceiverActive()) { - return true; - } - return s_authorizedSenders.contains(sender); - } - - /** - * @notice customizable guard of who can update the authorized sender list - * @return bool whether sender can update authorized sender list - */ - function _canSetAuthorizedSenders() internal virtual returns (bool); - - /** - * @notice validates the sender is an authorized sender - */ - function _validateIsAuthorizedSender() internal view { - if (!isAuthorizedSender(tx.origin)) { - revert UnauthorizedSender(); - } - } - - /** - * @notice prevents non-authorized addresses from calling this method - */ - modifier validateAuthorizedSender() { - _validateIsAuthorizedSender(); - _; - } - - /** - * @notice prevents non-authorized addresses from calling this method - */ - modifier validateAuthorizedSenderSetter() { - if (!_canSetAuthorizedSenders()) { - revert NotAllowedToSetSenders(); - } - _; - } -} diff --git a/contracts/contracts/dev/functions/ConfirmedOwnerUpgradeable.sol b/contracts/contracts/dev/functions/ConfirmedOwnerUpgradeable.sol deleted file mode 100644 index 374d704..0000000 --- a/contracts/contracts/dev/functions/ConfirmedOwnerUpgradeable.sol +++ /dev/null @@ -1,96 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "@chainlink/contracts/src/v0.8/interfaces/OwnableInterface.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -/** - * @title The ConfirmedOwnerUpgradeable contract - * @notice An upgrade compatible contract with helpers for basic contract ownership. - */ -contract ConfirmedOwnerUpgradeable is Initializable, OwnableInterface { - address private s_owner; - address private s_pendingOwner; - - event OwnershipTransferRequested(address indexed from, address indexed to); - event OwnershipTransferred(address indexed from, address indexed to); - - error OwnerMustBeSet(); - error NotProposedOwner(); - error CannotSelfTransfer(); - error OnlyCallableByOwner(); - - /** - * @dev Initializes the contract in unpaused state. - */ - function __ConfirmedOwner_initialize(address newOwner, address pendingOwner) internal onlyInitializing { - if (newOwner == address(0)) { - revert OwnerMustBeSet(); - } - - s_owner = newOwner; - if (pendingOwner != address(0)) { - _transferOwnership(pendingOwner); - } - } - - /** - * @notice Allows an owner to begin transferring ownership to a new address, - * pending. - */ - function transferOwnership(address to) public override onlyOwner { - _transferOwnership(to); - } - - /** - * @notice Allows an ownership transfer to be completed by the recipient. - */ - function acceptOwnership() external override { - if (msg.sender != s_pendingOwner) { - revert NotProposedOwner(); - } - - address oldOwner = s_owner; - s_owner = msg.sender; - s_pendingOwner = address(0); - - emit OwnershipTransferred(oldOwner, msg.sender); - } - - /** - * @notice Get the current owner - */ - function owner() public view override returns (address) { - return s_owner; - } - - /** - * @notice validate, transfer ownership, and emit relevant events - */ - function _transferOwnership(address to) private { - if (to == msg.sender) { - revert CannotSelfTransfer(); - } - - s_pendingOwner = to; - - emit OwnershipTransferRequested(s_owner, to); - } - - /** - * @notice validate access - */ - function _validateOwnership() internal view { - if (msg.sender != s_owner) { - revert OnlyCallableByOwner(); - } - } - - /** - * @notice Reverts if called by anyone other than the contract owner. - */ - modifier onlyOwner() { - _validateOwnership(); - _; - } -} diff --git a/contracts/contracts/dev/functions/Functions.sol b/contracts/contracts/dev/functions/Functions.sol deleted file mode 100644 index 571f4ea..0000000 --- a/contracts/contracts/dev/functions/Functions.sol +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -import {CBOR, Buffer} from "../vendor/solidity-cborutils/2.0.0/CBOR.sol"; - -/** - * @title Library for Chainlink Functions - */ -library Functions { - uint256 internal constant DEFAULT_BUFFER_SIZE = 256; - - using CBOR for Buffer.buffer; - - enum Location { - Inline, - Remote - } - - enum CodeLanguage { - JavaScript - // In future version we may add other languages - } - - struct Request { - Location codeLocation; - Location secretsLocation; - CodeLanguage language; - string source; // Source code for Location.Inline or url for Location.Remote - bytes secrets; // Encrypted secrets blob for Location.Inline or url for Location.Remote - string[] args; - } - - error EmptySource(); - error EmptyUrl(); - error EmptySecrets(); - error EmptyArgs(); - error NoInlineSecrets(); - - /** - * @notice Encodes a Request to CBOR encoded bytes - * @param self The request to encode - * @return CBOR encoded bytes - */ - function encodeCBOR(Request memory self) internal pure returns (bytes memory) { - CBOR.CBORBuffer memory buffer; - Buffer.init(buffer.buf, DEFAULT_BUFFER_SIZE); - - CBOR.writeString(buffer, "codeLocation"); - CBOR.writeUInt256(buffer, uint256(self.codeLocation)); - - CBOR.writeString(buffer, "language"); - CBOR.writeUInt256(buffer, uint256(self.language)); - - CBOR.writeString(buffer, "source"); - CBOR.writeString(buffer, self.source); - - if (self.args.length > 0) { - CBOR.writeString(buffer, "args"); - CBOR.startArray(buffer); - for (uint256 i = 0; i < self.args.length; i++) { - CBOR.writeString(buffer, self.args[i]); - } - CBOR.endSequence(buffer); - } - - if (self.secrets.length > 0) { - if (self.secretsLocation == Location.Inline) { - revert NoInlineSecrets(); - } - CBOR.writeString(buffer, "secretsLocation"); - CBOR.writeUInt256(buffer, uint256(self.secretsLocation)); - CBOR.writeString(buffer, "secrets"); - CBOR.writeBytes(buffer, self.secrets); - } - - return buffer.buf.buf; - } - - /** - * @notice Initializes a Chainlink Functions Request - * @dev Sets the codeLocation and code on the request - * @param self The uninitialized request - * @param location The user provided source code location - * @param language The programming language of the user code - * @param source The user provided source code or a url - */ - function initializeRequest( - Request memory self, - Location location, - CodeLanguage language, - string memory source - ) internal pure { - if (bytes(source).length == 0) revert EmptySource(); - - self.codeLocation = location; - self.language = language; - self.source = source; - } - - /** - * @notice Initializes a Chainlink Functions Request - * @dev Simplified version of initializeRequest for PoC - * @param self The uninitialized request - * @param javaScriptSource The user provided JS code (must not be empty) - */ - function initializeRequestForInlineJavaScript(Request memory self, string memory javaScriptSource) internal pure { - initializeRequest(self, Location.Inline, CodeLanguage.JavaScript, javaScriptSource); - } - - /** - * @notice Adds Remote user encrypted secrets to a Request - * @param self The initialized request - * @param encryptedSecretsURLs Encrypted comma-separated string of URLs pointing to off-chain secrets - */ - function addRemoteSecrets(Request memory self, bytes memory encryptedSecretsURLs) internal pure { - if (encryptedSecretsURLs.length == 0) revert EmptySecrets(); - - self.secretsLocation = Location.Remote; - self.secrets = encryptedSecretsURLs; - } - - /** - * @notice Adds args for the user run function - * @param self The initialized request - * @param args The array of args (must not be empty) - */ - function addArgs(Request memory self, string[] memory args) internal pure { - if (args.length == 0) revert EmptyArgs(); - - self.args = args; - } -} diff --git a/contracts/contracts/dev/functions/FunctionsBillingRegistry.sol b/contracts/contracts/dev/functions/FunctionsBillingRegistry.sol deleted file mode 100644 index 7ffc21e..0000000 --- a/contracts/contracts/dev/functions/FunctionsBillingRegistry.sol +++ /dev/null @@ -1,843 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; -import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; -import "../interfaces/FunctionsBillingRegistryInterface.sol"; -import "../interfaces/FunctionsOracleInterface.sol"; -import "../interfaces/FunctionsClientInterface.sol"; -import "@chainlink/contracts/src/v0.8/interfaces/TypeAndVersionInterface.sol"; -import "@chainlink/contracts/src/v0.8/interfaces/ERC677ReceiverInterface.sol"; -import "../interfaces/AuthorizedOriginReceiverInterface.sol"; -import "./ConfirmedOwnerUpgradeable.sol"; -import "../AuthorizedReceiver.sol"; -import "../vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/SafeCast.sol"; -import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -/** - * @title Functions Billing Registry contract - * @notice Contract that coordinates payment from users to the nodes of the Decentralized Oracle Network (DON). - * @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD. - */ -contract FunctionsBillingRegistry is - Initializable, - ConfirmedOwnerUpgradeable, - PausableUpgradeable, - FunctionsBillingRegistryInterface, - ERC677ReceiverInterface, - AuthorizedReceiver -{ - LinkTokenInterface private LINK; - AggregatorV3Interface private LINK_ETH_FEED; - AuthorizedOriginReceiverInterface private ORACLE_WITH_ALLOWLIST; - - // We need to maintain a list of consuming addresses. - // This bound ensures we are able to loop over them as needed. - // Should a user require more consumers, they can use multiple subscriptions. - uint16 public constant MAX_CONSUMERS = 100; - - error TooManyConsumers(); - error InsufficientBalance(); - error InvalidConsumer(uint64 subscriptionId, address consumer); - error InvalidSubscription(); - error OnlyCallableFromLink(); - error InvalidCalldata(); - error MustBeSubOwner(address owner); - error PendingRequestExists(); - error MustBeRequestedOwner(address proposedOwner); - error BalanceInvariantViolated(uint256 internalBalance, uint256 externalBalance); // Should never happen - event FundsRecovered(address to, uint256 amount); - - struct Subscription { - // There are only 1e9*1e18 = 1e27 juels in existence, so the balance can fit in uint96 (2^96 ~ 7e28) - uint96 balance; // Common LINK balance that is controlled by the Registry to be used for all consumer requests. - uint96 blockedBalance; // LINK balance that is reserved to pay for pending consumer requests. - } - // We use the config for the mgmt APIs - struct SubscriptionConfig { - address owner; // Owner can fund/withdraw/cancel the sub. - address requestedOwner; // For safely transferring sub ownership. - // Maintains the list of keys in s_consumers. - // We do this for 2 reasons: - // 1. To be able to clean up all keys from s_consumers when canceling a subscription. - // 2. To be able to return the list of all consumers in getSubscription. - // Note that we need the s_consumers map to be able to directly check if a - // consumer is valid without reading all the consumers from storage. - address[] consumers; - } - // Note a nonce of 0 indicates an the consumer is not assigned to that subscription. - mapping(address => mapping(uint64 => uint64)) /* consumer */ /* subscriptionId */ /* nonce */ private s_consumers; - mapping(uint64 => SubscriptionConfig) /* subscriptionId */ /* subscriptionConfig */ private s_subscriptionConfigs; - mapping(uint64 => Subscription) /* subscriptionId */ /* subscription */ private s_subscriptions; - // We make the sub count public so that its possible to - // get all the current subscriptions via getSubscription. - uint64 private s_currentsubscriptionId; - // s_totalBalance tracks the total link sent to/from - // this contract through onTokenTransfer, cancelSubscription and oracleWithdraw. - // A discrepancy with this contract's link balance indicates someone - // sent tokens using transfer and so we may need to use recoverFunds. - uint96 private s_totalBalance; - event SubscriptionCreated(uint64 indexed subscriptionId, address owner); - event SubscriptionFunded(uint64 indexed subscriptionId, uint256 oldBalance, uint256 newBalance); - event SubscriptionConsumerAdded(uint64 indexed subscriptionId, address consumer); - event SubscriptionConsumerRemoved(uint64 indexed subscriptionId, address consumer); - event SubscriptionCanceled(uint64 indexed subscriptionId, address to, uint256 amount); - event SubscriptionOwnerTransferRequested(uint64 indexed subscriptionId, address from, address to); - event SubscriptionOwnerTransferred(uint64 indexed subscriptionId, address from, address to); - - error GasLimitTooBig(uint32 have, uint32 want); - error InvalidLinkWeiPrice(int256 linkWei); - error PaymentTooLarge(); - error Reentrant(); - - mapping(address => uint96) /* oracle node */ /* LINK balance */ private s_withdrawableTokens; - struct Commitment { - uint64 subscriptionId; - address client; - uint32 gasLimit; - uint256 gasPrice; - address don; - uint96 donFee; - uint96 registryFee; - uint96 estimatedCost; - uint256 timestamp; - } - mapping(bytes32 => Commitment) /* requestID */ /* Commitment */ private s_requestCommitments; - event BillingStart(bytes32 indexed requestId, Commitment commitment); - struct ItemizedBill { - uint96 signerPayment; - uint96 transmitterPayment; - uint96 totalCost; - } - event BillingEnd( - bytes32 indexed requestId, - uint64 subscriptionId, - uint96 signerPayment, - uint96 transmitterPayment, - uint96 totalCost, - bool success - ); - event RequestTimedOut(bytes32 indexed requestId); - - struct Config { - // Maxiumum amount of gas that can be given to a request's client callback - uint32 maxGasLimit; - // Reentrancy protection. - bool reentrancyLock; - // stalenessSeconds is how long before we consider the feed price to be stale - // and fallback to fallbackWeiPerUnitLink. - uint32 stalenessSeconds; - // Gas to cover transmitter oracle payment after we calculate the payment. - // We make it configurable in case those operations are repriced. - uint256 gasAfterPaymentCalculation; - // Represents the average gas execution cost. Used in estimating cost beforehand. - uint32 gasOverhead; - // how many seconds it takes before we consider a request to be timed out - uint32 requestTimeoutSeconds; - } - int256 private s_fallbackWeiPerUnitLink; - Config private s_config; - event ConfigSet( - uint32 maxGasLimit, - uint32 stalenessSeconds, - uint256 gasAfterPaymentCalculation, - int256 fallbackWeiPerUnitLink, - uint32 gasOverhead - ); - - /** - * @dev Initializes the contract. - */ - function initialize(address link, address linkEthFeed, address oracle) public initializer { - __Pausable_init(); - __ConfirmedOwner_initialize(msg.sender, address(0)); - LINK = LinkTokenInterface(link); - LINK_ETH_FEED = AggregatorV3Interface(linkEthFeed); - ORACLE_WITH_ALLOWLIST = AuthorizedOriginReceiverInterface(oracle); - } - - /** - * @notice Sets the configuration of the Chainlink Functions billing registry - * @param maxGasLimit global max for request gas limit - * @param stalenessSeconds if the eth/link feed is more stale then this, use the fallback price - * @param gasAfterPaymentCalculation gas used in doing accounting after completing the gas measurement - * @param fallbackWeiPerUnitLink fallback eth/link price in the case of a stale feed - * @param gasOverhead average gas execution cost used in estimating total cost - * @param requestTimeoutSeconds e2e timeout after which user won't be charged - */ - function setConfig( - uint32 maxGasLimit, - uint32 stalenessSeconds, - uint256 gasAfterPaymentCalculation, - int256 fallbackWeiPerUnitLink, - uint32 gasOverhead, - uint32 requestTimeoutSeconds - ) external onlyOwner { - if (fallbackWeiPerUnitLink <= 0) { - revert InvalidLinkWeiPrice(fallbackWeiPerUnitLink); - } - s_config = Config({ - maxGasLimit: maxGasLimit, - stalenessSeconds: stalenessSeconds, - gasAfterPaymentCalculation: gasAfterPaymentCalculation, - reentrancyLock: false, - gasOverhead: gasOverhead, - requestTimeoutSeconds: requestTimeoutSeconds - }); - s_fallbackWeiPerUnitLink = fallbackWeiPerUnitLink; - emit ConfigSet(maxGasLimit, stalenessSeconds, gasAfterPaymentCalculation, fallbackWeiPerUnitLink, gasOverhead); - } - - /** - * @notice Gets the configuration of the Chainlink Functions billing registry - * @return maxGasLimit global max for request gas limit - * @return stalenessSeconds if the eth/link feed is more stale then this, use the fallback price - * @return gasAfterPaymentCalculation gas used in doing accounting after completing the gas measurement - * @return fallbackWeiPerUnitLink fallback eth/link price in the case of a stale feed - * @return gasOverhead average gas execution cost used in estimating total cost - * @return linkAddress address of contract for the LINK token - * @return linkPriceFeed address of contract for a conversion price between LINK token and native token - */ - function getConfig() - external - view - returns ( - uint32 maxGasLimit, - uint32 stalenessSeconds, - uint256 gasAfterPaymentCalculation, - int256 fallbackWeiPerUnitLink, - uint32 gasOverhead, - address linkAddress, - address linkPriceFeed - ) - { - return ( - s_config.maxGasLimit, - s_config.stalenessSeconds, - s_config.gasAfterPaymentCalculation, - s_fallbackWeiPerUnitLink, - s_config.gasOverhead, - address(LINK), - address(LINK_ETH_FEED) - ); - } - - function pause() external onlyOwner { - _pause(); - } - - function unpause() external onlyOwner { - _unpause(); - } - - function getTotalBalance() external view returns (uint256) { - return s_totalBalance; - } - - /** - * @notice Owner cancel subscription, sends remaining link directly to the subscription owner. - * @param subscriptionId subscription id - * @dev notably can be called even if there are pending requests, outstanding ones may fail onchain - */ - function ownerCancelSubscription(uint64 subscriptionId) external onlyOwner { - address owner = s_subscriptionConfigs[subscriptionId].owner; - if (owner == address(0)) { - revert InvalidSubscription(); - } - cancelSubscriptionHelper(subscriptionId, owner); - } - - /** - * @notice Recover link sent with transfer instead of transferAndCall. - * @param to address to send link to - */ - function recoverFunds(address to) external onlyOwner { - uint256 externalBalance = LINK.balanceOf(address(this)); - uint256 internalBalance = uint256(s_totalBalance); - if (internalBalance > externalBalance) { - revert BalanceInvariantViolated(internalBalance, externalBalance); - } - if (internalBalance < externalBalance) { - uint256 amount = externalBalance - internalBalance; - LINK.transfer(to, amount); - emit FundsRecovered(to, amount); - } - // If the balances are equal, nothing to be done. - } - - /** - * @inheritdoc FunctionsBillingRegistryInterface - */ - function getRequestConfig() external view override returns (uint32, address[] memory) { - return (s_config.maxGasLimit, getAuthorizedSenders()); - } - - /** - * @inheritdoc FunctionsBillingRegistryInterface - */ - function getRequiredFee( - bytes calldata /* data */, - FunctionsBillingRegistryInterface.RequestBilling memory /* billing */ - ) public pure override returns (uint96) { - // NOTE: Optionally, compute additional fee here - return 200_000_000_000_000_000; // 0.2 LINK - } - - /** - * @inheritdoc FunctionsBillingRegistryInterface - */ - function estimateCost( - uint32 gasLimit, - uint256 gasPrice, - uint96 donFee, - uint96 registryFee - ) public view override returns (uint96) { - int256 weiPerUnitLink; - weiPerUnitLink = getFeedData(); - if (weiPerUnitLink <= 0) { - revert InvalidLinkWeiPrice(weiPerUnitLink); - } - uint256 executionGas = s_config.gasOverhead + s_config.gasAfterPaymentCalculation + gasLimit; - // (1e18 juels/link) (wei/gas * gas) / (wei/link) = juels - uint256 paymentNoFee = (1e18 * gasPrice * executionGas) / uint256(weiPerUnitLink); - uint256 fee = uint256(donFee) + uint256(registryFee); - if (paymentNoFee > (1e27 - fee)) { - revert PaymentTooLarge(); // Payment + fee cannot be more than all of the link in existence. - } - return uint96(paymentNoFee + fee); - } - - /** - * @inheritdoc FunctionsBillingRegistryInterface - */ - function startBilling( - bytes calldata data, - RequestBilling calldata billing - ) external override validateAuthorizedSender nonReentrant whenNotPaused returns (bytes32) { - // Input validation using the subscription storage. - if (s_subscriptionConfigs[billing.subscriptionId].owner == address(0)) { - revert InvalidSubscription(); - } - // It's important to ensure that the consumer is in fact who they say they - // are, otherwise they could use someone else's subscription balance. - // A nonce of 0 indicates consumer is not allocated to the sub. - uint64 currentNonce = s_consumers[billing.client][billing.subscriptionId]; - if (currentNonce == 0) { - revert InvalidConsumer(billing.subscriptionId, billing.client); - } - // No lower bound on the requested gas limit. A user could request 0 - // and they would simply be billed for the gas and computation. - if (billing.gasLimit > s_config.maxGasLimit) { - revert GasLimitTooBig(billing.gasLimit, s_config.maxGasLimit); - } - - // Check that subscription can afford the estimated cost - uint96 oracleFee = FunctionsOracleInterface(msg.sender).getRequiredFee(data, billing); - uint96 registryFee = getRequiredFee(data, billing); - uint96 estimatedCost = estimateCost(billing.gasLimit, billing.gasPrice, oracleFee, registryFee); - uint96 effectiveBalance = s_subscriptions[billing.subscriptionId].balance - - s_subscriptions[billing.subscriptionId].blockedBalance; - if (effectiveBalance < estimatedCost) { - revert InsufficientBalance(); - } - - uint64 nonce = currentNonce + 1; - bytes32 requestId = computeRequestId(msg.sender, billing.client, billing.subscriptionId, nonce); - - Commitment memory commitment = Commitment( - billing.subscriptionId, - billing.client, - billing.gasLimit, - billing.gasPrice, - msg.sender, - oracleFee, - registryFee, - estimatedCost, - block.timestamp - ); - s_requestCommitments[requestId] = commitment; - s_subscriptions[billing.subscriptionId].blockedBalance += estimatedCost; - - emit BillingStart(requestId, commitment); - s_consumers[billing.client][billing.subscriptionId] = nonce; - return requestId; - } - - function computeRequestId( - address don, - address client, - uint64 subscriptionId, - uint64 nonce - ) private pure returns (bytes32) { - return keccak256(abi.encode(don, client, subscriptionId, nonce)); - } - - /** - * @dev calls target address with exactly gasAmount gas and data as calldata - * or reverts if at least gasAmount gas is not available. - */ - function callWithExactGas(uint256 gasAmount, address target, bytes memory data) private returns (bool success) { - // solhint-disable-next-line no-inline-assembly - assembly { - let g := gas() - // GAS_FOR_CALL_EXACT_CHECK = 5000 - // Compute g -= GAS_FOR_CALL_EXACT_CHECK and check for underflow - // The gas actually passed to the callee is min(gasAmount, 63//64*gas available). - // We want to ensure that we revert if gasAmount > 63//64*gas available - // as we do not want to provide them with less, however that check itself costs - // gas. GAS_FOR_CALL_EXACT_CHECK ensures we have at least enough gas to be able - // to revert if gasAmount > 63//64*gas available. - if lt(g, 5000) { - revert(0, 0) - } - g := sub(g, 5000) - // if g - g//64 <= gasAmount, revert - // (we subtract g//64 because of EIP-150) - if iszero(gt(sub(g, div(g, 64)), gasAmount)) { - revert(0, 0) - } - // solidity calls check that a contract actually exists at the destination, so we do the same - if iszero(extcodesize(target)) { - revert(0, 0) - } - // call and return whether we succeeded. ignore return data - // call(gas,addr,value,argsOffset,argsLength,retOffset,retLength) - success := call(gasAmount, target, 0, add(data, 0x20), mload(data), 0, 0) - } - return success; - } - - /** - * @inheritdoc FunctionsBillingRegistryInterface - */ - function fulfillAndBill( - bytes32 requestId, - bytes calldata response, - bytes calldata err, - address transmitter, - address[31] memory signers, - uint8 signerCount, - uint256 reportValidationGas, - uint256 initialGas - ) external override validateAuthorizedSender nonReentrant whenNotPaused returns (FulfillResult) { - Commitment memory commitment = s_requestCommitments[requestId]; - if (commitment.don == address(0)) { - return FulfillResult.INVALID_REQUEST_ID; - } - delete s_requestCommitments[requestId]; - - bytes memory callback = abi.encodeWithSelector( - FunctionsClientInterface.handleOracleFulfillment.selector, - requestId, - response, - err - ); - // Call with explicitly the amount of callback gas requested - // Important to not let them exhaust the gas budget and avoid payment. - // Do not allow any non-view/non-pure coordinator functions to be called - // during the consumers callback code via reentrancyLock. - // NOTE: that callWithExactGas will revert if we do not have sufficient gas - // to give the callee their requested amount. - s_config.reentrancyLock = true; - bool success = callWithExactGas(commitment.gasLimit, commitment.client, callback); - s_config.reentrancyLock = false; - - // We want to charge users exactly for how much gas they use in their callback. - // The gasAfterPaymentCalculation is meant to cover these additional operations where we - // decrement the subscription balance and increment the oracle's withdrawable balance. - ItemizedBill memory bill = calculatePaymentAmount( - initialGas, - s_config.gasAfterPaymentCalculation, - commitment.donFee, - signerCount, - commitment.registryFee, - reportValidationGas, - tx.gasprice - ); - if (s_subscriptions[commitment.subscriptionId].balance < bill.totalCost) { - revert InsufficientBalance(); - } - s_subscriptions[commitment.subscriptionId].balance -= bill.totalCost; - // Pay out signers their portion of the DON fee - for (uint256 i = 0; i < signerCount; i++) { - s_withdrawableTokens[signers[i]] += bill.signerPayment; - } - // Pay out the registry fee - s_withdrawableTokens[owner()] += commitment.registryFee; - // Reimburse the transmitter for the execution gas cost + pay them their portion of the DON fee - s_withdrawableTokens[transmitter] += bill.transmitterPayment; - // Remove blocked balance - s_subscriptions[commitment.subscriptionId].blockedBalance -= commitment.estimatedCost; - // Include payment in the event for tracking costs. - emit BillingEnd( - requestId, - commitment.subscriptionId, - bill.signerPayment, - bill.transmitterPayment, - bill.totalCost, - success - ); - return success ? FulfillResult.USER_SUCCESS : FulfillResult.USER_ERROR; - } - - // Determine the cost breakdown for payment - function calculatePaymentAmount( - uint256 startGas, - uint256 gasAfterPaymentCalculation, - uint96 donFee, - uint8 signerCount, - uint96 registryFee, - uint256 reportValidationGas, - uint256 weiPerUnitGas - ) private view returns (ItemizedBill memory) { - int256 weiPerUnitLink; - weiPerUnitLink = getFeedData(); - if (weiPerUnitLink <= 0) { - revert InvalidLinkWeiPrice(weiPerUnitLink); - } - // (1e18 juels/link) (wei/gas * gas) / (wei/link) = juels - uint256 paymentNoFee = (1e18 * - weiPerUnitGas * - (reportValidationGas + gasAfterPaymentCalculation + startGas - gasleft())) / uint256(weiPerUnitLink); - uint256 fee = uint256(donFee) + uint256(registryFee); - if (paymentNoFee > (1e27 - fee)) { - revert PaymentTooLarge(); // Payment + fee cannot be more than all of the link in existence. - } - uint96 signerPayment = donFee / uint96(signerCount); - uint96 transmitterPayment = uint96(paymentNoFee); - uint96 totalCost = SafeCast.toUint96(paymentNoFee + fee); - return ItemizedBill(signerPayment, transmitterPayment, totalCost); - } - - function getFeedData() private view returns (int256) { - uint32 stalenessSeconds = s_config.stalenessSeconds; - bool staleFallback = stalenessSeconds > 0; - (, int256 weiPerUnitLink, , uint256 timestamp, ) = LINK_ETH_FEED.latestRoundData(); - // solhint-disable-next-line not-rely-on-time - if (staleFallback && stalenessSeconds < block.timestamp - timestamp) { - weiPerUnitLink = s_fallbackWeiPerUnitLink; - } - return weiPerUnitLink; - } - - /* - * @notice Oracle withdraw LINK earned through fulfilling requests - * @notice If amount is 0 the full balance will be withdrawn - * @notice Both signing and transmitting wallets will have a balance to withdraw - * @param recipient where to send the funds - * @param amount amount to withdraw - */ - function oracleWithdraw(address recipient, uint96 amount) external nonReentrant whenNotPaused { - if (amount == 0) { - amount = s_withdrawableTokens[msg.sender]; - } - if (s_withdrawableTokens[msg.sender] < amount) { - revert InsufficientBalance(); - } - s_withdrawableTokens[msg.sender] -= amount; - s_totalBalance -= amount; - if (!LINK.transfer(recipient, amount)) { - revert InsufficientBalance(); - } - } - - function onTokenTransfer( - address /* sender */, - uint256 amount, - bytes calldata data - ) external override nonReentrant whenNotPaused { - if (msg.sender != address(LINK)) { - revert OnlyCallableFromLink(); - } - if (data.length != 32) { - revert InvalidCalldata(); - } - uint64 subscriptionId = abi.decode(data, (uint64)); - if (s_subscriptionConfigs[subscriptionId].owner == address(0)) { - revert InvalidSubscription(); - } - // We do not check that the msg.sender is the subscription owner, - // anyone can fund a subscription. - uint256 oldBalance = s_subscriptions[subscriptionId].balance; - s_subscriptions[subscriptionId].balance += uint96(amount); - s_totalBalance += uint96(amount); - emit SubscriptionFunded(subscriptionId, oldBalance, oldBalance + amount); - } - - function getCurrentsubscriptionId() external view returns (uint64) { - return s_currentsubscriptionId; - } - - /** - * @notice Get details about a subscription. - * @param subscriptionId - ID of the subscription - * @return balance - LINK balance of the subscription in juels. - * @return owner - owner of the subscription. - * @return consumers - list of consumer address which are able to use this subscription. - */ - function getSubscription( - uint64 subscriptionId - ) external view returns (uint96 balance, address owner, address[] memory consumers) { - if (s_subscriptionConfigs[subscriptionId].owner == address(0)) { - revert InvalidSubscription(); - } - return ( - s_subscriptions[subscriptionId].balance, - s_subscriptionConfigs[subscriptionId].owner, - s_subscriptionConfigs[subscriptionId].consumers - ); - } - - /** - * @notice Create a new subscription. - * @return subscriptionId - A unique subscription id. - * @dev You can manage the consumer set dynamically with addConsumer/removeConsumer. - * @dev Note to fund the subscription, use transferAndCall. For example - * @dev LINKTOKEN.transferAndCall( - * @dev address(REGISTRY), - * @dev amount, - * @dev abi.encode(subscriptionId)); - */ - function createSubscription() external nonReentrant whenNotPaused onlyAuthorizedUsers returns (uint64) { - s_currentsubscriptionId++; - uint64 currentsubscriptionId = s_currentsubscriptionId; - address[] memory consumers = new address[](0); - s_subscriptions[currentsubscriptionId] = Subscription({balance: 0, blockedBalance: 0}); - s_subscriptionConfigs[currentsubscriptionId] = SubscriptionConfig({ - owner: msg.sender, - requestedOwner: address(0), - consumers: consumers - }); - - emit SubscriptionCreated(currentsubscriptionId, msg.sender); - return currentsubscriptionId; - } - - /** - * @notice Gets subscription owner. - * @param subscriptionId - ID of the subscription - * @return owner - owner of the subscription. - */ - function getSubscriptionOwner(uint64 subscriptionId) external view override returns (address owner) { - if (s_subscriptionConfigs[subscriptionId].owner == address(0)) { - revert InvalidSubscription(); - } - return s_subscriptionConfigs[subscriptionId].owner; - } - - /** - * @notice Request subscription owner transfer. - * @param subscriptionId - ID of the subscription - * @param newOwner - proposed new owner of the subscription - */ - function requestSubscriptionOwnerTransfer( - uint64 subscriptionId, - address newOwner - ) external onlySubOwner(subscriptionId) nonReentrant whenNotPaused { - // Proposing to address(0) would never be claimable so don't need to check. - if (s_subscriptionConfigs[subscriptionId].requestedOwner != newOwner) { - s_subscriptionConfigs[subscriptionId].requestedOwner = newOwner; - emit SubscriptionOwnerTransferRequested(subscriptionId, msg.sender, newOwner); - } - } - - /** - * @notice Request subscription owner transfer. - * @param subscriptionId - ID of the subscription - * @dev will revert if original owner of subscriptionId has - * not requested that msg.sender become the new owner. - */ - function acceptSubscriptionOwnerTransfer( - uint64 subscriptionId - ) external nonReentrant whenNotPaused onlyAuthorizedUsers { - if (s_subscriptionConfigs[subscriptionId].owner == address(0)) { - revert InvalidSubscription(); - } - if (s_subscriptionConfigs[subscriptionId].requestedOwner != msg.sender) { - revert MustBeRequestedOwner(s_subscriptionConfigs[subscriptionId].requestedOwner); - } - address oldOwner = s_subscriptionConfigs[subscriptionId].owner; - s_subscriptionConfigs[subscriptionId].owner = msg.sender; - s_subscriptionConfigs[subscriptionId].requestedOwner = address(0); - emit SubscriptionOwnerTransferred(subscriptionId, oldOwner, msg.sender); - } - - /** - * @notice Remove a consumer from a Chainlink Functions subscription. - * @param subscriptionId - ID of the subscription - * @param consumer - Consumer to remove from the subscription - */ - function removeConsumer( - uint64 subscriptionId, - address consumer - ) external onlySubOwner(subscriptionId) nonReentrant whenNotPaused { - if (s_consumers[consumer][subscriptionId] == 0) { - revert InvalidConsumer(subscriptionId, consumer); - } - // Note bounded by MAX_CONSUMERS - address[] memory consumers = s_subscriptionConfigs[subscriptionId].consumers; - uint256 lastConsumerIndex = consumers.length - 1; - for (uint256 i = 0; i < consumers.length; i++) { - if (consumers[i] == consumer) { - address last = consumers[lastConsumerIndex]; - // Storage write to preserve last element - s_subscriptionConfigs[subscriptionId].consumers[i] = last; - // Storage remove last element - s_subscriptionConfigs[subscriptionId].consumers.pop(); - break; - } - } - delete s_consumers[consumer][subscriptionId]; - emit SubscriptionConsumerRemoved(subscriptionId, consumer); - } - - /** - * @notice Add a consumer to a Chainlink Functions subscription. - * @param subscriptionId - ID of the subscription - * @param consumer - New consumer which can use the subscription - */ - function addConsumer( - uint64 subscriptionId, - address consumer - ) external onlySubOwner(subscriptionId) nonReentrant whenNotPaused { - // Already maxed, cannot add any more consumers. - if (s_subscriptionConfigs[subscriptionId].consumers.length == MAX_CONSUMERS) { - revert TooManyConsumers(); - } - if (s_consumers[consumer][subscriptionId] != 0) { - // Idempotence - do nothing if already added. - // Ensures uniqueness in s_subscriptions[subscriptionId].consumers. - return; - } - // Initialize the nonce to 1, indicating the consumer is allocated. - s_consumers[consumer][subscriptionId] = 1; - s_subscriptionConfigs[subscriptionId].consumers.push(consumer); - - emit SubscriptionConsumerAdded(subscriptionId, consumer); - } - - /** - * @notice Cancel a subscription - * @param subscriptionId - ID of the subscription - * @param to - Where to send the remaining LINK to - */ - function cancelSubscription( - uint64 subscriptionId, - address to - ) external onlySubOwner(subscriptionId) nonReentrant whenNotPaused { - if (pendingRequestExists(subscriptionId)) { - revert PendingRequestExists(); - } - cancelSubscriptionHelper(subscriptionId, to); - } - - function cancelSubscriptionHelper(uint64 subscriptionId, address to) private nonReentrant { - SubscriptionConfig memory subConfig = s_subscriptionConfigs[subscriptionId]; - uint96 balance = s_subscriptions[subscriptionId].balance; - // Note bounded by MAX_CONSUMERS; - // If no consumers, does nothing. - for (uint256 i = 0; i < subConfig.consumers.length; i++) { - delete s_consumers[subConfig.consumers[i]][subscriptionId]; - } - delete s_subscriptionConfigs[subscriptionId]; - delete s_subscriptions[subscriptionId]; - s_totalBalance -= balance; - if (!LINK.transfer(to, uint256(balance))) { - revert InsufficientBalance(); - } - emit SubscriptionCanceled(subscriptionId, to, balance); - } - - /** - * @notice Check to see if there exists a request commitment for all consumers for a given sub. - * @param subscriptionId - ID of the subscription - * @return true if there exists at least one unfulfilled request for the subscription, false - * otherwise. - * @dev Looping is bounded to MAX_CONSUMERS*(number of DONs). - * @dev Used to disable subscription canceling while outstanding request are present. - */ - - function pendingRequestExists(uint64 subscriptionId) public view returns (bool) { - address[] memory consumers = s_subscriptionConfigs[subscriptionId].consumers; - address[] memory authorizedSendersList = getAuthorizedSenders(); - for (uint256 i = 0; i < consumers.length; i++) { - for (uint256 j = 0; j < authorizedSendersList.length; j++) { - bytes32 requestId = computeRequestId( - authorizedSendersList[j], - consumers[i], - subscriptionId, - s_consumers[consumers[i]][subscriptionId] - ); - if (s_requestCommitments[requestId].don != address(0)) { - return true; - } - } - } - return false; - } - - /** - * @notice Time out all expired requests: unlocks funds and removes the ability for the request to be fulfilled - * @param requestIdsToTimeout - A list of request IDs to time out - */ - - function timeoutRequests(bytes32[] calldata requestIdsToTimeout) external whenNotPaused { - for (uint256 i = 0; i < requestIdsToTimeout.length; i++) { - bytes32 requestId = requestIdsToTimeout[i]; - Commitment memory commitment = s_requestCommitments[requestId]; - - // Check that the message sender is the subscription owner - if (msg.sender != s_subscriptionConfigs[commitment.subscriptionId].owner) { - revert MustBeSubOwner(s_subscriptionConfigs[commitment.subscriptionId].owner); - } - - if (commitment.timestamp + s_config.requestTimeoutSeconds > block.timestamp) { - // Decrement blocked balance - s_subscriptions[commitment.subscriptionId].blockedBalance -= commitment.estimatedCost; - // Delete commitment - delete s_requestCommitments[requestId]; - emit RequestTimedOut(requestId); - } - } - } - - /** - * @dev The allow list is kept on the Oracle contract. This modifier checks if a user is authorized from there. - */ - modifier onlyAuthorizedUsers() { - if (ORACLE_WITH_ALLOWLIST.authorizedReceiverActive() && !ORACLE_WITH_ALLOWLIST.isAuthorizedSender(msg.sender)) { - revert UnauthorizedSender(); - } - _; - } - - modifier onlySubOwner(uint64 subscriptionId) { - address owner = s_subscriptionConfigs[subscriptionId].owner; - if (owner == address(0)) { - revert InvalidSubscription(); - } - if (msg.sender != owner) { - revert MustBeSubOwner(owner); - } - _; - } - - modifier nonReentrant() { - if (s_config.reentrancyLock) { - revert Reentrant(); - } - _; - } - - function _canSetAuthorizedSenders() internal view override onlyOwner returns (bool) { - return true; - } - - /** - * @dev This empty reserved space is put in place to allow future versions to add new - * variables without shifting down storage in the inheritance chain. - * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - */ - uint256[49] private __gap; -} diff --git a/contracts/contracts/dev/functions/FunctionsClient.sol b/contracts/contracts/dev/functions/FunctionsClient.sol deleted file mode 100644 index 6ba24f8..0000000 --- a/contracts/contracts/dev/functions/FunctionsClient.sol +++ /dev/null @@ -1,138 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -import "./Functions.sol"; -import "../interfaces/FunctionsClientInterface.sol"; -import "../interfaces/FunctionsOracleInterface.sol"; - -/** - * @title The Chainlink Functions client contract - * @notice Contract writers can inherit this contract in order to create Chainlink Functions requests - */ -abstract contract FunctionsClient is FunctionsClientInterface { - FunctionsOracleInterface internal s_oracle; - mapping(bytes32 => address) internal s_pendingRequests; - - event RequestSent(bytes32 indexed id); - event RequestFulfilled(bytes32 indexed id); - - error SenderIsNotRegistry(); - error RequestIsAlreadyPending(); - error RequestIsNotPending(); - - constructor(address oracle) { - setOracle(oracle); - } - - /** - * @inheritdoc FunctionsClientInterface - */ - function getDONPublicKey() external view override returns (bytes memory) { - return s_oracle.getDONPublicKey(); - } - - /** - * @notice Estimate the total cost that will be charged to a subscription to make a request: gas re-imbursement, plus DON fee, plus Registry fee - * @param req The initialized Functions.Request - * @param subscriptionId The subscription ID - * @param gasLimit gas limit for the fulfillment callback - * @return billedCost Cost in Juels (1e18) of LINK - */ - function estimateCost( - Functions.Request memory req, - uint64 subscriptionId, - uint32 gasLimit, - uint256 gasPrice - ) public view returns (uint96) { - return s_oracle.estimateCost(subscriptionId, Functions.encodeCBOR(req), gasLimit, gasPrice); - } - - /** - * @notice Sends a Chainlink Functions request to the stored oracle address - * @param req The initialized Functions.Request - * @param subscriptionId The subscription ID - * @param gasLimit gas limit for the fulfillment callback - * @return requestId The generated request ID - */ - function sendRequest( - Functions.Request memory req, - uint64 subscriptionId, - uint32 gasLimit - ) internal returns (bytes32) { - bytes32 requestId = s_oracle.sendRequest(subscriptionId, Functions.encodeCBOR(req), gasLimit); - s_pendingRequests[requestId] = s_oracle.getRegistry(); - emit RequestSent(requestId); - return requestId; - } - - /** - * @notice User defined function to handle a response - * @param requestId The request ID, returned by sendRequest() - * @param response Aggregated response from the user code - * @param err Aggregated error from the user code or from the execution pipeline - * Either response or error parameter will be set, but never both - */ - function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal virtual; - - /** - * @inheritdoc FunctionsClientInterface - */ - function handleOracleFulfillment( - bytes32 requestId, - bytes memory response, - bytes memory err - ) external override recordChainlinkFulfillment(requestId) { - fulfillRequest(requestId, response, err); - } - - /** - * @notice Sets the stored Oracle address - * @param oracle The address of Functions Oracle contract - */ - function setOracle(address oracle) internal { - s_oracle = FunctionsOracleInterface(oracle); - } - - /** - * @notice Gets the stored address of the oracle contract - * @return The address of the oracle contract - */ - function getChainlinkOracleAddress() internal view returns (address) { - return address(s_oracle); - } - - /** - * @notice Allows for a request which was created on another contract to be fulfilled - * on this contract - * @param oracleAddress The address of the oracle contract that will fulfill the request - * @param requestId The request ID used for the response - */ - function addExternalRequest(address oracleAddress, bytes32 requestId) internal notPendingRequest(requestId) { - s_pendingRequests[requestId] = oracleAddress; - } - - /** - * @dev Reverts if the sender is not the oracle that serviced the request. - * Emits RequestFulfilled event. - * @param requestId The request ID for fulfillment - */ - modifier recordChainlinkFulfillment(bytes32 requestId) { - if (msg.sender != s_pendingRequests[requestId]) { - revert SenderIsNotRegistry(); - } - delete s_pendingRequests[requestId]; - emit RequestFulfilled(requestId); - _; - } - - /** - * @dev Reverts if the request is already pending - * @param requestId The request ID for fulfillment - */ - modifier notPendingRequest(bytes32 requestId) { - if (s_pendingRequests[requestId] != address(0)) { - revert RequestIsAlreadyPending(); - } - _; - } -} diff --git a/contracts/contracts/dev/functions/FunctionsOracle.sol b/contracts/contracts/dev/functions/FunctionsOracle.sol deleted file mode 100644 index 86ea4de..0000000 --- a/contracts/contracts/dev/functions/FunctionsOracle.sol +++ /dev/null @@ -1,274 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -import "../interfaces/FunctionsOracleInterface.sol"; -import "../ocr2/OCR2BaseUpgradeable.sol"; -import "./AuthorizedOriginReceiverUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -/** - * @title Functions Oracle contract - * @notice Contract that nodes of a Decentralized Oracle Network (DON) interact with - * @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD. - */ -contract FunctionsOracle is - Initializable, - FunctionsOracleInterface, - OCR2BaseUpgradeable, - AuthorizedOriginReceiverUpgradeable -{ - event OracleRequest( - bytes32 indexed requestId, - address requestingContract, - address requestInitiator, - uint64 subscriptionId, - address subscriptionOwner, - bytes data - ); - event OracleResponse(bytes32 indexed requestId); - event UserCallbackError(bytes32 indexed requestId, string reason); - event UserCallbackRawError(bytes32 indexed requestId, bytes lowLevelData); - event InvalidRequestID(bytes32 indexed requestId); - - error EmptyRequestData(); - error InconsistentReportData(); - error EmptyPublicKey(); - error EmptyBillingRegistry(); - error UnauthorizedPublicKeyChange(); - - bytes private s_donPublicKey; - FunctionsBillingRegistryInterface private s_registry; - mapping(address => bytes) private s_nodePublicKeys; - - /** - * @dev Initializes the contract. - */ - function initialize() public initializer { - __OCR2Base_initialize(true); - __AuthorizedOriginReceiver_initialize(true); - } - - /** - * @notice The type and version of this contract - * @return Type and version string - */ - function typeAndVersion() external pure override returns (string memory) { - return "FunctionsOracle 0.0.0"; - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function getRegistry() external view override returns (address) { - return address(s_registry); - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function setRegistry(address registryAddress) external override onlyOwner { - if (registryAddress == address(0)) { - revert EmptyBillingRegistry(); - } - s_registry = FunctionsBillingRegistryInterface(registryAddress); - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function getDONPublicKey() external view override returns (bytes memory) { - return s_donPublicKey; - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function setDONPublicKey(bytes calldata donPublicKey) external override onlyOwner { - if (donPublicKey.length == 0) { - revert EmptyPublicKey(); - } - s_donPublicKey = donPublicKey; - } - - /** - * @dev check if node is in current transmitter list - */ - function _isTransmitter(address node) internal view returns (bool) { - address[] memory nodes = this.transmitters(); - for (uint256 i = 0; i < nodes.length; i++) { - if (nodes[i] == node) { - return true; - } - } - return false; - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function setNodePublicKey(address node, bytes calldata publicKey) external override { - // Owner can set anything. Transmitters can set only their own key. - if (!(msg.sender == owner() || (_isTransmitter(msg.sender) && msg.sender == node))) { - revert UnauthorizedPublicKeyChange(); - } - s_nodePublicKeys[node] = publicKey; - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function deleteNodePublicKey(address node) external override { - // Owner can delete anything. Others can delete only their own key. - if (!(msg.sender == owner() || msg.sender == node)) { - revert UnauthorizedPublicKeyChange(); - } - delete s_nodePublicKeys[node]; - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function getAllNodePublicKeys() external view override returns (address[] memory, bytes[] memory) { - address[] memory nodes = this.transmitters(); - bytes[] memory keys = new bytes[](nodes.length); - for (uint256 i = 0; i < nodes.length; i++) { - keys[i] = s_nodePublicKeys[nodes[i]]; - } - return (nodes, keys); - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function getRequiredFee( - bytes calldata /* data */, - FunctionsBillingRegistryInterface.RequestBilling memory /* billing */ - ) public pure override returns (uint96) { - // NOTE: Optionally, compute additional fee split between nodes of the DON here - // e.g. 0.1 LINK * s_transmitters.length - return 0; - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function estimateCost( - uint64 subscriptionId, - bytes calldata data, - uint32 gasLimit, - uint256 gasPrice - ) external view override registryIsSet returns (uint96) { - FunctionsBillingRegistryInterface.RequestBilling memory billing = FunctionsBillingRegistryInterface.RequestBilling( - subscriptionId, - msg.sender, - gasLimit, - gasPrice - ); - uint96 donFee = getRequiredFee(data, billing); - uint96 registryFee = s_registry.getRequiredFee(data, billing); - return s_registry.estimateCost(gasLimit, gasPrice, donFee, registryFee); - } - - /** - * @inheritdoc FunctionsOracleInterface - */ - function sendRequest( - uint64 subscriptionId, - bytes calldata data, - uint32 gasLimit - ) external override registryIsSet validateAuthorizedSender returns (bytes32) { - if (data.length == 0) { - revert EmptyRequestData(); - } - bytes32 requestId = s_registry.startBilling( - data, - FunctionsBillingRegistryInterface.RequestBilling(subscriptionId, msg.sender, gasLimit, tx.gasprice) - ); - emit OracleRequest( - requestId, - msg.sender, - tx.origin, - subscriptionId, - s_registry.getSubscriptionOwner(subscriptionId), - data - ); - return requestId; - } - - function _beforeSetConfig(uint8 _f, bytes memory _onchainConfig) internal override {} - - function _afterSetConfig(uint8 _f, bytes memory _onchainConfig) internal override {} - - function _validateReport( - bytes32 /* configDigest */, - uint40 /* epochAndRound */, - bytes memory /* report */ - ) internal pure override returns (bool) { - // validate within _report to save gas - return true; - } - - function _report( - uint256 initialGas, - address transmitter, - uint8 signerCount, - address[maxNumOracles] memory signers, - bytes calldata report - ) internal override registryIsSet { - bytes32[] memory requestIds; - bytes[] memory results; - bytes[] memory errors; - (requestIds, results, errors) = abi.decode(report, (bytes32[], bytes[], bytes[])); - if (requestIds.length == 0 || requestIds.length != results.length || requestIds.length != errors.length) { - revert ReportInvalid(); - } - - uint256 reportValidationGasShare = (initialGas - gasleft()) / requestIds.length; - - for (uint256 i = 0; i < requestIds.length; i++) { - try - s_registry.fulfillAndBill( - requestIds[i], - results[i], - errors[i], - transmitter, - signers, - signerCount, - reportValidationGasShare, - gasleft() - ) - returns (FunctionsBillingRegistryInterface.FulfillResult result) { - if (result == FunctionsBillingRegistryInterface.FulfillResult.USER_SUCCESS) { - emit OracleResponse(requestIds[i]); - } else if (result == FunctionsBillingRegistryInterface.FulfillResult.USER_ERROR) { - emit UserCallbackError(requestIds[i], "error in callback"); - } else if (result == FunctionsBillingRegistryInterface.FulfillResult.INVALID_REQUEST_ID) { - emit InvalidRequestID(requestIds[i]); - } - } catch (bytes memory reason) { - emit UserCallbackRawError(requestIds[i], reason); - } - } - } - - /** - * @dev Reverts if the the billing registry is not set - */ - modifier registryIsSet() { - if (address(s_registry) == address(0)) { - revert EmptyBillingRegistry(); - } - _; - } - - function _canSetAuthorizedSenders() internal view override returns (bool) { - return msg.sender == owner(); - } - - /** - * @dev This empty reserved space is put in place to allow future versions to add new - * variables without shifting down storage in the inheritance chain. - * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps - */ - uint256[49] private __gap; -} diff --git a/contracts/contracts/dev/functions/vendor/ProxyAdmin.sol b/contracts/contracts/dev/functions/vendor/ProxyAdmin.sol deleted file mode 100644 index b97b2e5..0000000 --- a/contracts/contracts/dev/functions/vendor/ProxyAdmin.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol) - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; diff --git a/contracts/contracts/dev/functions/vendor/TransparentUpgradeableProxy.sol b/contracts/contracts/dev/functions/vendor/TransparentUpgradeableProxy.sol deleted file mode 100644 index 887aa90..0000000 --- a/contracts/contracts/dev/functions/vendor/TransparentUpgradeableProxy.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (proxy/transparent/TransparentUpgradeableProxy.sol) - -pragma solidity ^0.8.0; - -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; diff --git a/contracts/contracts/dev/interfaces/AuthorizedOriginReceiverInterface.sol b/contracts/contracts/dev/interfaces/AuthorizedOriginReceiverInterface.sol deleted file mode 100644 index 5f35676..0000000 --- a/contracts/contracts/dev/interfaces/AuthorizedOriginReceiverInterface.sol +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -/** - * @notice Modified AuthorizedReciever abstract for use on the Functions Oracle contract to limit usage - * @notice Uses tx.origin instead of msg.sender because the client contract sends messages to the Oracle contract - */ - -interface AuthorizedOriginReceiverInterface { - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function authorizedReceiverActive() external view returns (bool); - - /** - * @dev Triggers AuthorizedOriginReceiver usage to block unuthorized senders. - * - * Requirements: - * - * - The contract must not be deactive. - */ - function activateAuthorizedReceiver() external; - - /** - * @dev Triggers AuthorizedOriginReceiver usage to allow all senders. - * - * Requirements: - * - * - The contract must be active. - */ - function deactivateAuthorizedReceiver() external; - - /** - * @notice Sets the permission to request for the given wallet(s). - * @param senders The addresses of the wallet addresses to grant access - */ - function addAuthorizedSenders(address[] calldata senders) external; - - /** - * @notice Remove the permission to request for the given wallet(s). - * @param senders The addresses of the wallet addresses to revoke access - */ - function removeAuthorizedSenders(address[] calldata senders) external; - - /** - * @notice Retrieve a list of authorized senders - * @return array of addresses - */ - function getAuthorizedSenders() external view returns (address[] memory); - - /** - * @notice Use this to check if a node is authorized for fulfilling requests - * @param sender The address of the Chainlink node - * @return The authorization status of the node - */ - function isAuthorizedSender(address sender) external view returns (bool); -} diff --git a/contracts/contracts/dev/interfaces/AuthorizedReceiverInterface.sol b/contracts/contracts/dev/interfaces/AuthorizedReceiverInterface.sol deleted file mode 100644 index 28b20b1..0000000 --- a/contracts/contracts/dev/interfaces/AuthorizedReceiverInterface.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -interface AuthorizedReceiverInterface { - function isAuthorizedSender(address sender) external view returns (bool); - - function getAuthorizedSenders() external returns (address[] memory); - - function setAuthorizedSenders(address[] calldata senders) external; -} diff --git a/contracts/contracts/dev/interfaces/FunctionsBillingRegistryInterface.sol b/contracts/contracts/dev/interfaces/FunctionsBillingRegistryInterface.sol deleted file mode 100644 index 7428137..0000000 --- a/contracts/contracts/dev/interfaces/FunctionsBillingRegistryInterface.sol +++ /dev/null @@ -1,99 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -/** - * @title Chainlink Functions billing subscription registry interface. - */ -interface FunctionsBillingRegistryInterface { - struct RequestBilling { - // a unique subscription ID allocated by billing system, - uint64 subscriptionId; - // the client contract that initiated the request to the DON - // to use the subscription it must be added as a consumer on the subscription - address client; - // customer specified gas limit for the fulfillment callback - uint32 gasLimit; - // the expected gas price used to execute the transaction - uint256 gasPrice; - } - - enum FulfillResult { - USER_SUCCESS, - USER_ERROR, - INVALID_REQUEST_ID - } - - /** - * @notice Get configuration relevant for making requests - * @return uint32 global max for request gas limit - * @return address[] list of registered DONs - */ - function getRequestConfig() external view returns (uint32, address[] memory); - - /** - * @notice Determine the charged fee that will be paid to the Registry owner - * @param data Encoded Chainlink Functions request data, use FunctionsClient API to encode a request - * @param billing The request's billing configuration - * @return fee Cost in Juels (1e18) of LINK - */ - function getRequiredFee( - bytes calldata data, - FunctionsBillingRegistryInterface.RequestBilling memory billing - ) external view returns (uint96); - - /** - * @notice Estimate the total cost to make a request: gas re-imbursement, plus DON fee, plus Registry fee - * @param gasLimit Encoded Chainlink Functions request data, use FunctionsClient API to encode a request - * @param gasPrice The request's billing configuration - * @param donFee Fee charged by the DON that is paid to Oracle Node - * @param registryFee Fee charged by the DON that is paid to Oracle Node - * @return costEstimate Cost in Juels (1e18) of LINK - */ - function estimateCost( - uint32 gasLimit, - uint256 gasPrice, - uint96 donFee, - uint96 registryFee - ) external view returns (uint96); - - /** - * @notice Initiate the billing process for an Functions request - * @param data Encoded Chainlink Functions request data, use FunctionsClient API to encode a request - * @param billing Billing configuration for the request - * @return requestId - A unique identifier of the request. Can be used to match a request to a response in fulfillRequest. - * @dev Only callable by a node that has been approved on the Registry - */ - function startBilling(bytes calldata data, RequestBilling calldata billing) external returns (bytes32); - - /** - * @notice Finalize billing process for an Functions request by sending a callback to the Client contract and then charging the subscription - * @param requestId identifier for the request that was generated by the Registry in the beginBilling commitment - * @param response response data from DON consensus - * @param err error from DON consensus - * @param transmitter the Oracle who sent the report - * @param signers the Oracles who had a part in generating the report - * @param signerCount the number of signers on the report - * @param reportValidationGas the amount of gas used for the report validation. Cost is split by all fulfillments on the report. - * @param initialGas the initial amount of gas that should be used as a baseline to charge the single fulfillment for execution cost - * @return result fulfillment result - * @dev Only callable by a node that has been approved on the Registry - * @dev simulated offchain to determine if sufficient balance is present to fulfill the request - */ - function fulfillAndBill( - bytes32 requestId, - bytes calldata response, - bytes calldata err, - address transmitter, - address[31] memory signers, // 31 comes from OCR2Abstract.sol's maxNumOracles constant - uint8 signerCount, - uint256 reportValidationGas, - uint256 initialGas - ) external returns (FulfillResult); - - /** - * @notice Gets subscription owner. - * @param subscriptionId - ID of the subscription - * @return owner - owner of the subscription. - */ - function getSubscriptionOwner(uint64 subscriptionId) external view returns (address owner); -} diff --git a/contracts/contracts/dev/interfaces/FunctionsClientInterface.sol b/contracts/contracts/dev/interfaces/FunctionsClientInterface.sol deleted file mode 100644 index b047a67..0000000 --- a/contracts/contracts/dev/interfaces/FunctionsClientInterface.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -/** - * @title Chainlink Functions client interface. - */ -interface FunctionsClientInterface { - /** - * @notice Returns the DON's secp256k1 public key used to encrypt secrets - * @dev All Oracles nodes have the corresponding private key - * needed to decrypt the secrets encrypted with the public key - * @return publicKey DON's public key - */ - function getDONPublicKey() external view returns (bytes memory); - - /** - * @notice Chainlink Functions response handler called by the designated transmitter node in an OCR round. - * @param requestId The requestId returned by FunctionsClient.sendRequest(). - * @param response Aggregated response from the user code. - * @param err Aggregated error either from the user code or from the execution pipeline. - * Either response or error parameter will be set, but never both. - */ - function handleOracleFulfillment(bytes32 requestId, bytes memory response, bytes memory err) external; -} diff --git a/contracts/contracts/dev/interfaces/FunctionsOracleInterface.sol b/contracts/contracts/dev/interfaces/FunctionsOracleInterface.sol deleted file mode 100644 index 2076dcc..0000000 --- a/contracts/contracts/dev/interfaces/FunctionsOracleInterface.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.6; - -import "./FunctionsBillingRegistryInterface.sol"; - -/** - * @title Chainlink Functions oracle interface. - */ -interface FunctionsOracleInterface { - /** - * @notice Gets the stored billing registry address - * @return registryAddress The address of Chainlink Functions billing registry contract - */ - function getRegistry() external view returns (address); - - /** - * @notice Sets the stored billing registry address - * @param registryAddress The new address of Chainlink Functions billing registry contract - */ - function setRegistry(address registryAddress) external; - - /** - * @notice Returns the DON's secp256k1 public key that is used to encrypt secrets - * @dev All nodes on the DON have the corresponding private key - * needed to decrypt the secrets encrypted with the public key - * @return publicKey the DON's public key - */ - function getDONPublicKey() external view returns (bytes memory); - - /** - * @notice Sets DON's secp256k1 public key used to encrypt secrets - * @dev Used to rotate the key - * @param donPublicKey The new public key - */ - function setDONPublicKey(bytes calldata donPublicKey) external; - - /** - * @notice Sets a per-node secp256k1 public key used to encrypt secrets for that node - * @dev Callable only by contract owner and DON members - * @param node node's address - * @param publicKey node's public key - */ - function setNodePublicKey(address node, bytes calldata publicKey) external; - - /** - * @notice Deletes node's public key - * @dev Callable only by contract owner or the node itself - * @param node node's address - */ - function deleteNodePublicKey(address node) external; - - /** - * @notice Return two arrays of equal size containing DON members' addresses and their corresponding - * public keys (or empty byte arrays if per-node key is not defined) - */ - function getAllNodePublicKeys() external view returns (address[] memory, bytes[] memory); - - /** - * @notice Determine the fee charged by the DON that will be split between signing Node Operators for servicing the request - * @param data Encoded Chainlink Functions request data, use FunctionsClient API to encode a request - * @param billing The request's billing configuration - * @return fee Cost in Juels (1e18) of LINK - */ - function getRequiredFee( - bytes calldata data, - FunctionsBillingRegistryInterface.RequestBilling calldata billing - ) external view returns (uint96); - - /** - * @notice Estimate the total cost that will be charged to a subscription to make a request: gas re-imbursement, plus DON fee, plus Registry fee - * @param subscriptionId A unique subscription ID allocated by billing system, - * a client can make requests from different contracts referencing the same subscription - * @param data Encoded Chainlink Functions request data, use FunctionsClient API to encode a request - * @param gasLimit Gas limit for the fulfillment callback - * @return billedCost Cost in Juels (1e18) of LINK - */ - function estimateCost( - uint64 subscriptionId, - bytes calldata data, - uint32 gasLimit, - uint256 gasPrice - ) external view returns (uint96); - - /** - * @notice Sends a request (encoded as data) using the provided subscriptionId - * @param subscriptionId A unique subscription ID allocated by billing system, - * a client can make requests from different contracts referencing the same subscription - * @param data Encoded Chainlink Functions request data, use FunctionsClient API to encode a request - * @param gasLimit Gas limit for the fulfillment callback - * @return requestId A unique request identifier (unique per DON) - */ - function sendRequest(uint64 subscriptionId, bytes calldata data, uint32 gasLimit) external returns (bytes32); -} diff --git a/contracts/contracts/dev/ocr2/OCR2Base.sol b/contracts/contracts/dev/ocr2/OCR2Base.sol deleted file mode 100644 index 114a74c..0000000 --- a/contracts/contracts/dev/ocr2/OCR2Base.sol +++ /dev/null @@ -1,383 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol"; -import "@chainlink/contracts/src/v0.8/dev/ocr2/OCR2Abstract.sol"; - -/** - * @notice Onchain verification of reports from the offchain reporting protocol - * @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD. - * @dev For details on its operation, see the offchain reporting protocol design - * doc, which refers to this contract as simply the "contract". - * @dev This contract is meant to aid rapid development of new applications based on OCR2. - * However, for actual production contracts, it is expected that most of the logic of this contract - * will be folded directly into the application contract. Inheritance prevents us from doing lots - * of juicy storage layout optimizations, leading to a substantial increase in gas cost. - */ -abstract contract OCR2Base is ConfirmedOwner, OCR2Abstract { - error ReportInvalid(); - - bool internal immutable i_uniqueReports; - - constructor(bool uniqueReports) ConfirmedOwner(msg.sender) { - i_uniqueReports = uniqueReports; - } - - uint256 private constant maxUint32 = (1 << 32) - 1; - - // Storing these fields used on the hot path in a ConfigInfo variable reduces the - // retrieval of all of them to a single SLOAD. If any further fields are - // added, make sure that storage of the struct still takes at most 32 bytes. - struct ConfigInfo { - bytes32 latestConfigDigest; - uint8 f; // TODO: could be optimized by squeezing into one slot - uint8 n; - } - ConfigInfo internal s_configInfo; - - // incremented each time a new config is posted. This count is incorporated - // into the config digest, to prevent replay attacks. - uint32 internal s_configCount; - uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems - // to extract config from logs. - - // Used for s_oracles[a].role, where a is an address, to track the purpose - // of the address, or to indicate that the address is unset. - enum Role { - // No oracle role has been set for address a - Unset, - // Signing address for the s_oracles[a].index'th oracle. I.e., report - // signatures from this oracle should ecrecover back to address a. - Signer, - // Transmission address for the s_oracles[a].index'th oracle. I.e., if a - // report is received by OCR2Aggregator.transmit in which msg.sender is - // a, it is attributed to the s_oracles[a].index'th oracle. - Transmitter - } - - struct Oracle { - uint8 index; // Index of oracle in s_signers/s_transmitters - Role role; // Role of the address which mapped to this struct - } - - mapping(address => Oracle) /* signer OR transmitter address */ internal s_oracles; - - // s_signers contains the signing address of each oracle - address[] internal s_signers; - - // s_transmitters contains the transmission address of each oracle, - // i.e. the address the oracle actually sends transactions to the contract from - address[] internal s_transmitters; - - /* - * Config logic - */ - - // Reverts transaction if config args are invalid - modifier checkConfigValid( - uint256 _numSigners, - uint256 _numTransmitters, - uint256 _f - ) { - require(_numSigners <= maxNumOracles, "too many signers"); - require(_f > 0, "f must be positive"); - require(_numSigners == _numTransmitters, "oracle addresses out of registration"); - require(_numSigners > 3 * _f, "faulty-oracle f too high"); - _; - } - - struct SetConfigArgs { - address[] signers; - address[] transmitters; - uint8 f; - bytes onchainConfig; - uint64 offchainConfigVersion; - bytes offchainConfig; - } - - /// @inheritdoc OCR2Abstract - function latestConfigDigestAndEpoch() - external - view - virtual - override - returns (bool scanLogs, bytes32 configDigest, uint32 epoch) - { - return (true, bytes32(0), uint32(0)); - } - - /** - * @notice sets offchain reporting protocol configuration incl. participating oracles - * @param _signers addresses with which oracles sign the reports - * @param _transmitters addresses oracles use to transmit the reports - * @param _f number of faulty oracles the system can tolerate - * @param _onchainConfig encoded on-chain contract configuration - * @param _offchainConfigVersion version number for offchainEncoding schema - * @param _offchainConfig encoded off-chain oracle configuration - */ - function setConfig( - address[] memory _signers, - address[] memory _transmitters, - uint8 _f, - bytes memory _onchainConfig, - uint64 _offchainConfigVersion, - bytes memory _offchainConfig - ) external override checkConfigValid(_signers.length, _transmitters.length, _f) onlyOwner { - SetConfigArgs memory args = SetConfigArgs({ - signers: _signers, - transmitters: _transmitters, - f: _f, - onchainConfig: _onchainConfig, - offchainConfigVersion: _offchainConfigVersion, - offchainConfig: _offchainConfig - }); - - _beforeSetConfig(args.f, args.onchainConfig); - - while (s_signers.length != 0) { - // remove any old signer/transmitter addresses - uint256 lastIdx = s_signers.length - 1; - address signer = s_signers[lastIdx]; - address transmitter = s_transmitters[lastIdx]; - delete s_oracles[signer]; - delete s_oracles[transmitter]; - s_signers.pop(); - s_transmitters.pop(); - } - - for (uint256 i = 0; i < args.signers.length; ++i) { - // add new signer/transmitter addresses - require(s_oracles[args.signers[i]].role == Role.Unset, "repeated signer address"); - s_oracles[args.signers[i]] = Oracle(uint8(i), Role.Signer); - require(s_oracles[args.transmitters[i]].role == Role.Unset, "repeated transmitter address"); - s_oracles[args.transmitters[i]] = Oracle(uint8(i), Role.Transmitter); - s_signers.push(args.signers[i]); - s_transmitters.push(args.transmitters[i]); - } - s_configInfo.f = args.f; - uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; - s_latestConfigBlockNumber = uint32(block.number); - s_configCount += 1; - { - s_configInfo.latestConfigDigest = configDigestFromConfigData( - block.chainid, - address(this), - s_configCount, - args.signers, - args.transmitters, - args.f, - args.onchainConfig, - args.offchainConfigVersion, - args.offchainConfig - ); - } - s_configInfo.n = uint8(args.signers.length); - - emit ConfigSet( - previousConfigBlockNumber, - s_configInfo.latestConfigDigest, - s_configCount, - args.signers, - args.transmitters, - args.f, - args.onchainConfig, - args.offchainConfigVersion, - args.offchainConfig - ); - - _afterSetConfig(args.f, args.onchainConfig); - } - - function configDigestFromConfigData( - uint256 _chainId, - address _contractAddress, - uint64 _configCount, - address[] memory _signers, - address[] memory _transmitters, - uint8 _f, - bytes memory _onchainConfig, - uint64 _encodedConfigVersion, - bytes memory _encodedConfig - ) internal pure returns (bytes32) { - uint256 h = uint256( - keccak256( - abi.encode( - _chainId, - _contractAddress, - _configCount, - _signers, - _transmitters, - _f, - _onchainConfig, - _encodedConfigVersion, - _encodedConfig - ) - ) - ); - uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 - uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 - return bytes32((prefix & prefixMask) | (h & ~prefixMask)); - } - - /** - * @notice information about current offchain reporting protocol configuration - * @return configCount ordinal number of current config, out of all configs applied to this contract so far - * @return blockNumber block at which this config was set - * @return configDigest domain-separation tag for current config (see configDigestFromConfigData) - */ - function latestConfigDetails() - external - view - override - returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) - { - return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); - } - - /** - * @return list of addresses permitted to transmit reports to this contract - * @dev The list will match the order used to specify the transmitter during setConfig - */ - function transmitters() external view returns (address[] memory) { - return s_transmitters; - } - - function _beforeSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; - - function _afterSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; - - /** - * @dev hook to allow additional validation of the report by the extending contract - * @param configDigest separation tag for current config (see configDigestFromConfigData) - * @param epochAndRound 27 byte padding, 4-byte epoch and 1-byte round - * @param report serialized report - */ - function _validateReport( - bytes32 configDigest, - uint40 epochAndRound, - bytes memory report - ) internal virtual returns (bool); - - /** - * @dev hook called after the report has been fully validated - * for the extending contract to handle additional logic, such as oracle payment - * @param initialGas the amount of gas before validation - * @param transmitter the address of the account that submitted the report - * @param signers the addresses of all signing accounts - * @param report serialized report - */ - function _report( - uint256 initialGas, - address transmitter, - uint8 signerCount, - address[maxNumOracles] memory signers, - bytes calldata report - ) internal virtual; - - // The constant-length components of the msg.data sent to transmit. - // See the "If we wanted to call sam" example on for example reasoning - // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html - uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = - 4 + // function selector - 32 * - 3 + // 3 words containing reportContext - 32 + // word containing start location of abiencoded report value - 32 + // word containing location start of abiencoded rs value - 32 + // word containing start location of abiencoded ss value - 32 + // rawVs value - 32 + // word containing length of report - 32 + // word containing length rs - 32 + // word containing length of ss - 0; // placeholder - - function requireExpectedMsgDataLength( - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss - ) private pure { - // calldata will never be big enough to make this overflow - uint256 expected = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + - report.length + // one byte pure entry in _report - rs.length * - 32 + // 32 bytes per entry in _rs - ss.length * - 32 + // 32 bytes per entry in _ss - 0; // placeholder - require(msg.data.length == expected, "calldata length mismatch"); - } - - /** - * @notice transmit is called to post a new report to the contract - * @param report serialized report, which the signatures are signing. - * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries - * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries - * @param rawVs ith element is the the V component of the ith signature - */ - function transmit( - // NOTE: If these parameters are changed, expectedMsgDataLength and/or - // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly - bytes32[3] calldata reportContext, - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss, - bytes32 rawVs // signatures - ) external override { - uint256 initialGas = gasleft(); // This line must come first - - { - // reportContext consists of: - // reportContext[0]: ConfigDigest - // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round - // reportContext[2]: ExtraHash - bytes32 configDigest = reportContext[0]; - uint32 epochAndRound = uint32(uint256(reportContext[1])); - - if (!_validateReport(configDigest, epochAndRound, report)) { - revert ReportInvalid(); - } - - emit Transmitted(configDigest, uint32(epochAndRound >> 8)); - - ConfigInfo memory configInfo = s_configInfo; - require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch"); - - requireExpectedMsgDataLength(report, rs, ss); - - uint256 expectedNumSignatures; - if (i_uniqueReports) { - expectedNumSignatures = (configInfo.n + configInfo.f) / 2 + 1; - } else { - expectedNumSignatures = configInfo.f + 1; - } - - require(rs.length == expectedNumSignatures, "wrong number of signatures"); - require(rs.length == ss.length, "signatures out of registration"); - - Oracle memory transmitter = s_oracles[msg.sender]; - require( // Check that sender is authorized to report - transmitter.role == Role.Transmitter && msg.sender == s_transmitters[transmitter.index], - "unauthorized transmitter" - ); - } - - address[maxNumOracles] memory signed; - uint8 signerCount = 0; - - { - // Verify signatures attached to report - bytes32 h = keccak256(abi.encodePacked(keccak256(report), reportContext)); - - Oracle memory o; - for (uint256 i = 0; i < rs.length; ++i) { - address signer = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); - o = s_oracles[signer]; - require(o.role == Role.Signer, "address not authorized to sign"); - require(signed[o.index] == address(0), "non-unique signature"); - signed[o.index] = signer; - signerCount += 1; - } - } - - _report(initialGas, msg.sender, signerCount, signed, report); - } -} diff --git a/contracts/contracts/dev/ocr2/OCR2BaseUpgradeable.sol b/contracts/contracts/dev/ocr2/OCR2BaseUpgradeable.sol deleted file mode 100644 index 12f9611..0000000 --- a/contracts/contracts/dev/ocr2/OCR2BaseUpgradeable.sol +++ /dev/null @@ -1,388 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "../functions/ConfirmedOwnerUpgradeable.sol"; -import "@chainlink/contracts/src/v0.8/dev/ocr2/OCR2Abstract.sol"; -import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; - -/** - * @notice Onchain verification of reports from the offchain reporting protocol - * @dev THIS CONTRACT HAS NOT GONE THROUGH ANY SECURITY REVIEW. DO NOT USE IN PROD. - * @dev For details on its operation, see the offchain reporting protocol design - * doc, which refers to this contract as simply the "contract". - * @dev This contract is meant to aid rapid development of new applications based on OCR2. - * However, for actual production contracts, it is expected that most of the logic of this contract - * will be folded directly into the application contract. Inheritance prevents us from doing lots - * of juicy storage layout optimizations, leading to a substantial increase in gas cost. - */ -abstract contract OCR2BaseUpgradeable is Initializable, ConfirmedOwnerUpgradeable, OCR2Abstract { - error ReportInvalid(); - - bool internal i_uniqueReports; - - /** - * @dev Initializes the contract. - */ - function __OCR2Base_initialize(bool uniqueReports) internal onlyInitializing { - __ConfirmedOwner_initialize(msg.sender, address(0)); - i_uniqueReports = uniqueReports; - } - - uint256 private constant maxUint32 = (1 << 32) - 1; - - // Storing these fields used on the hot path in a ConfigInfo variable reduces the - // retrieval of all of them to a single SLOAD. If any further fields are - // added, make sure that storage of the struct still takes at most 32 bytes. - struct ConfigInfo { - bytes32 latestConfigDigest; - uint8 f; // TODO: could be optimized by squeezing into one slot - uint8 n; - } - ConfigInfo internal s_configInfo; - - // incremented each time a new config is posted. This count is incorporated - // into the config digest, to prevent replay attacks. - uint32 internal s_configCount; - uint32 internal s_latestConfigBlockNumber; // makes it easier for offchain systems - // to extract config from logs. - - // Used for s_oracles[a].role, where a is an address, to track the purpose - // of the address, or to indicate that the address is unset. - enum Role { - // No oracle role has been set for address a - Unset, - // Signing address for the s_oracles[a].index'th oracle. I.e., report - // signatures from this oracle should ecrecover back to address a. - Signer, - // Transmission address for the s_oracles[a].index'th oracle. I.e., if a - // report is received by OCR2Aggregator.transmit in which msg.sender is - // a, it is attributed to the s_oracles[a].index'th oracle. - Transmitter - } - - struct Oracle { - uint8 index; // Index of oracle in s_signers/s_transmitters - Role role; // Role of the address which mapped to this struct - } - - mapping(address => Oracle) /* signer OR transmitter address */ internal s_oracles; - - // s_signers contains the signing address of each oracle - address[] internal s_signers; - - // s_transmitters contains the transmission address of each oracle, - // i.e. the address the oracle actually sends transactions to the contract from - address[] internal s_transmitters; - - /* - * Config logic - */ - - // Reverts transaction if config args are invalid - modifier checkConfigValid( - uint256 _numSigners, - uint256 _numTransmitters, - uint256 _f - ) { - require(_numSigners <= maxNumOracles, "too many signers"); - require(_f > 0, "f must be positive"); - require(_numSigners == _numTransmitters, "oracle addresses out of registration"); - require(_numSigners > 3 * _f, "faulty-oracle f too high"); - _; - } - - struct SetConfigArgs { - address[] signers; - address[] transmitters; - uint8 f; - bytes onchainConfig; - uint64 offchainConfigVersion; - bytes offchainConfig; - } - - /// @inheritdoc OCR2Abstract - function latestConfigDigestAndEpoch() - external - view - virtual - override - returns (bool scanLogs, bytes32 configDigest, uint32 epoch) - { - return (true, bytes32(0), uint32(0)); - } - - /** - * @notice sets offchain reporting protocol configuration incl. participating oracles - * @param _signers addresses with which oracles sign the reports - * @param _transmitters addresses oracles use to transmit the reports - * @param _f number of faulty oracles the system can tolerate - * @param _onchainConfig encoded on-chain contract configuration - * @param _offchainConfigVersion version number for offchainEncoding schema - * @param _offchainConfig encoded off-chain oracle configuration - */ - function setConfig( - address[] memory _signers, - address[] memory _transmitters, - uint8 _f, - bytes memory _onchainConfig, - uint64 _offchainConfigVersion, - bytes memory _offchainConfig - ) external override checkConfigValid(_signers.length, _transmitters.length, _f) onlyOwner { - SetConfigArgs memory args = SetConfigArgs({ - signers: _signers, - transmitters: _transmitters, - f: _f, - onchainConfig: _onchainConfig, - offchainConfigVersion: _offchainConfigVersion, - offchainConfig: _offchainConfig - }); - - _beforeSetConfig(args.f, args.onchainConfig); - - while (s_signers.length != 0) { - // remove any old signer/transmitter addresses - uint256 lastIdx = s_signers.length - 1; - address signer = s_signers[lastIdx]; - address transmitter = s_transmitters[lastIdx]; - delete s_oracles[signer]; - delete s_oracles[transmitter]; - s_signers.pop(); - s_transmitters.pop(); - } - - for (uint256 i = 0; i < args.signers.length; ++i) { - // add new signer/transmitter addresses - require(s_oracles[args.signers[i]].role == Role.Unset, "repeated signer address"); - s_oracles[args.signers[i]] = Oracle(uint8(i), Role.Signer); - require(s_oracles[args.transmitters[i]].role == Role.Unset, "repeated transmitter address"); - s_oracles[args.transmitters[i]] = Oracle(uint8(i), Role.Transmitter); - s_signers.push(args.signers[i]); - s_transmitters.push(args.transmitters[i]); - } - s_configInfo.f = args.f; - uint32 previousConfigBlockNumber = s_latestConfigBlockNumber; - s_latestConfigBlockNumber = uint32(block.number); - s_configCount += 1; - { - s_configInfo.latestConfigDigest = configDigestFromConfigData( - block.chainid, - address(this), - s_configCount, - args.signers, - args.transmitters, - args.f, - args.onchainConfig, - args.offchainConfigVersion, - args.offchainConfig - ); - } - s_configInfo.n = uint8(args.signers.length); - - emit ConfigSet( - previousConfigBlockNumber, - s_configInfo.latestConfigDigest, - s_configCount, - args.signers, - args.transmitters, - args.f, - args.onchainConfig, - args.offchainConfigVersion, - args.offchainConfig - ); - - _afterSetConfig(args.f, args.onchainConfig); - } - - function configDigestFromConfigData( - uint256 _chainId, - address _contractAddress, - uint64 _configCount, - address[] memory _signers, - address[] memory _transmitters, - uint8 _f, - bytes memory _onchainConfig, - uint64 _encodedConfigVersion, - bytes memory _encodedConfig - ) internal pure returns (bytes32) { - uint256 h = uint256( - keccak256( - abi.encode( - _chainId, - _contractAddress, - _configCount, - _signers, - _transmitters, - _f, - _onchainConfig, - _encodedConfigVersion, - _encodedConfig - ) - ) - ); - uint256 prefixMask = type(uint256).max << (256 - 16); // 0xFFFF00..00 - uint256 prefix = 0x0001 << (256 - 16); // 0x000100..00 - return bytes32((prefix & prefixMask) | (h & ~prefixMask)); - } - - /** - * @notice information about current offchain reporting protocol configuration - * @return configCount ordinal number of current config, out of all configs applied to this contract so far - * @return blockNumber block at which this config was set - * @return configDigest domain-separation tag for current config (see configDigestFromConfigData) - */ - function latestConfigDetails() - external - view - override - returns (uint32 configCount, uint32 blockNumber, bytes32 configDigest) - { - return (s_configCount, s_latestConfigBlockNumber, s_configInfo.latestConfigDigest); - } - - /** - * @return list of addresses permitted to transmit reports to this contract - * @dev The list will match the order used to specify the transmitter during setConfig - */ - function transmitters() external view returns (address[] memory) { - return s_transmitters; - } - - function _beforeSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; - - function _afterSetConfig(uint8 _f, bytes memory _onchainConfig) internal virtual; - - /** - * @dev hook to allow additional validation of the report by the extending contract - * @param configDigest separation tag for current config (see configDigestFromConfigData) - * @param epochAndRound 27 byte padding, 4-byte epoch and 1-byte round - * @param report serialized report - */ - function _validateReport( - bytes32 configDigest, - uint40 epochAndRound, - bytes memory report - ) internal virtual returns (bool); - - /** - * @dev hook called after the report has been fully validated - * for the extending contract to handle additional logic, such as oracle payment - * @param initialGas the amount of gas before validation - * @param transmitter the address of the account that submitted the report - * @param signers the addresses of all signing accounts - * @param report serialized report - */ - function _report( - uint256 initialGas, - address transmitter, - uint8 signerCount, - address[maxNumOracles] memory signers, - bytes calldata report - ) internal virtual; - - // The constant-length components of the msg.data sent to transmit. - // See the "If we wanted to call sam" example on for example reasoning - // https://solidity.readthedocs.io/en/v0.7.2/abi-spec.html - uint16 private constant TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT = - 4 + // function selector - 32 * - 3 + // 3 words containing reportContext - 32 + // word containing start location of abiencoded report value - 32 + // word containing location start of abiencoded rs value - 32 + // word containing start location of abiencoded ss value - 32 + // rawVs value - 32 + // word containing length of report - 32 + // word containing length rs - 32 + // word containing length of ss - 0; // placeholder - - function requireExpectedMsgDataLength( - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss - ) private pure { - // calldata will never be big enough to make this overflow - uint256 expected = uint256(TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT) + - report.length + // one byte pure entry in _report - rs.length * - 32 + // 32 bytes per entry in _rs - ss.length * - 32 + // 32 bytes per entry in _ss - 0; // placeholder - require(msg.data.length == expected, "calldata length mismatch"); - } - - /** - * @notice transmit is called to post a new report to the contract - * @param report serialized report, which the signatures are signing. - * @param rs ith element is the R components of the ith signature on report. Must have at most maxNumOracles entries - * @param ss ith element is the S components of the ith signature on report. Must have at most maxNumOracles entries - * @param rawVs ith element is the the V component of the ith signature - */ - function transmit( - // NOTE: If these parameters are changed, expectedMsgDataLength and/or - // TRANSMIT_MSGDATA_CONSTANT_LENGTH_COMPONENT need to be changed accordingly - bytes32[3] calldata reportContext, - bytes calldata report, - bytes32[] calldata rs, - bytes32[] calldata ss, - bytes32 rawVs // signatures - ) external override { - uint256 initialGas = gasleft(); // This line must come first - - { - // reportContext consists of: - // reportContext[0]: ConfigDigest - // reportContext[1]: 27 byte padding, 4-byte epoch and 1-byte round - // reportContext[2]: ExtraHash - bytes32 configDigest = reportContext[0]; - uint32 epochAndRound = uint32(uint256(reportContext[1])); - - if (!_validateReport(configDigest, epochAndRound, report)) { - revert ReportInvalid(); - } - - emit Transmitted(configDigest, uint32(epochAndRound >> 8)); - - ConfigInfo memory configInfo = s_configInfo; - require(configInfo.latestConfigDigest == configDigest, "configDigest mismatch"); - - requireExpectedMsgDataLength(report, rs, ss); - - uint256 expectedNumSignatures; - if (i_uniqueReports) { - expectedNumSignatures = (configInfo.n + configInfo.f) / 2 + 1; - } else { - expectedNumSignatures = configInfo.f + 1; - } - - require(rs.length == expectedNumSignatures, "wrong number of signatures"); - require(rs.length == ss.length, "signatures out of registration"); - - Oracle memory transmitter = s_oracles[msg.sender]; - require( // Check that sender is authorized to report - transmitter.role == Role.Transmitter && msg.sender == s_transmitters[transmitter.index], - "unauthorized transmitter" - ); - } - - address[maxNumOracles] memory signed; - uint8 signerCount = 0; - - { - // Verify signatures attached to report - bytes32 h = keccak256(abi.encodePacked(keccak256(report), reportContext)); - - Oracle memory o; - for (uint256 i = 0; i < rs.length; ++i) { - address signer = ecrecover(h, uint8(rawVs[i]) + 27, rs[i], ss[i]); - o = s_oracles[signer]; - require(o.role == Role.Signer, "address not authorized to sign"); - require(signed[o.index] == address(0), "non-unique signature"); - signed[o.index] = signer; - signerCount += 1; - } - } - - _report(initialGas, msg.sender, signerCount, signed, report); - } -} diff --git a/contracts/contracts/dev/vendor/@ensdomains/buffer/0.1.0/Buffer.sol b/contracts/contracts/dev/vendor/@ensdomains/buffer/0.1.0/Buffer.sol deleted file mode 100644 index fcdbc1a..0000000 --- a/contracts/contracts/dev/vendor/@ensdomains/buffer/0.1.0/Buffer.sol +++ /dev/null @@ -1,260 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -pragma solidity ^0.8.4; - -/** - * @dev A library for working with mutable byte buffers in Solidity. - * - * Byte buffers are mutable and expandable, and provide a variety of primitives - * for appending to them. At any time you can fetch a bytes object containing the - * current contents of the buffer. The bytes object should not be stored between - * operations, as it may change due to resizing of the buffer. - */ -library Buffer { - /** - * @dev Represents a mutable buffer. Buffers have a current value (buf) and - * a capacity. The capacity may be longer than the current value, in - * which case it can be extended without the need to allocate more memory. - */ - struct buffer { - bytes buf; - uint256 capacity; - } - - /** - * @dev Initializes a buffer with an initial capacity. - * @param buf The buffer to initialize. - * @param capacity The number of bytes of space to allocate the buffer. - * @return The buffer, for chaining. - */ - function init(buffer memory buf, uint256 capacity) internal pure returns (buffer memory) { - if (capacity % 32 != 0) { - capacity += 32 - (capacity % 32); - } - // Allocate space for the buffer data - buf.capacity = capacity; - assembly { - let ptr := mload(0x40) - mstore(buf, ptr) - mstore(ptr, 0) - let fpm := add(32, add(ptr, capacity)) - if lt(fpm, ptr) { - revert(0, 0) - } - mstore(0x40, fpm) - } - return buf; - } - - /** - * @dev Initializes a new buffer from an existing bytes object. - * Changes to the buffer may mutate the original value. - * @param b The bytes object to initialize the buffer with. - * @return A new buffer. - */ - function fromBytes(bytes memory b) internal pure returns (buffer memory) { - buffer memory buf; - buf.buf = b; - buf.capacity = b.length; - return buf; - } - - function resize(buffer memory buf, uint256 capacity) private pure { - bytes memory oldbuf = buf.buf; - init(buf, capacity); - append(buf, oldbuf); - } - - /** - * @dev Sets buffer length to 0. - * @param buf The buffer to truncate. - * @return The original buffer, for chaining.. - */ - function truncate(buffer memory buf) internal pure returns (buffer memory) { - assembly { - let bufptr := mload(buf) - mstore(bufptr, 0) - } - return buf; - } - - /** - * @dev Appends len bytes of a byte string to a buffer. Resizes if doing so would exceed - * the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @param len The number of bytes to copy. - * @return The original buffer, for chaining. - */ - function append(buffer memory buf, bytes memory data, uint256 len) internal pure returns (buffer memory) { - require(len <= data.length); - - uint256 off = buf.buf.length; - uint256 newCapacity = off + len; - if (newCapacity > buf.capacity) { - resize(buf, newCapacity * 2); - } - - uint256 dest; - uint256 src; - assembly { - // Memory address of the buffer data - let bufptr := mload(buf) - // Length of existing buffer data - let buflen := mload(bufptr) - // Start address = buffer address + offset + sizeof(buffer length) - dest := add(add(bufptr, 32), off) - // Update buffer length if we're extending it - if gt(newCapacity, buflen) { - mstore(bufptr, newCapacity) - } - src := add(data, 32) - } - - // Copy word-length chunks while possible - for (; len >= 32; len -= 32) { - assembly { - mstore(dest, mload(src)) - } - dest += 32; - src += 32; - } - - // Copy remaining bytes - unchecked { - uint256 mask = (256 ** (32 - len)) - 1; - assembly { - let srcpart := and(mload(src), not(mask)) - let destpart := and(mload(dest), mask) - mstore(dest, or(destpart, srcpart)) - } - } - - return buf; - } - - /** - * @dev Appends a byte string to a buffer. Resizes if doing so would exceed - * the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @return The original buffer, for chaining. - */ - function append(buffer memory buf, bytes memory data) internal pure returns (buffer memory) { - return append(buf, data, data.length); - } - - /** - * @dev Appends a byte to the buffer. Resizes if doing so would exceed the - * capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @return The original buffer, for chaining. - */ - function appendUint8(buffer memory buf, uint8 data) internal pure returns (buffer memory) { - uint256 off = buf.buf.length; - uint256 offPlusOne = off + 1; - if (off >= buf.capacity) { - resize(buf, offPlusOne * 2); - } - - assembly { - // Memory address of the buffer data - let bufptr := mload(buf) - // Address = buffer address + sizeof(buffer length) + off - let dest := add(add(bufptr, off), 32) - mstore8(dest, data) - // Update buffer length if we extended it - if gt(offPlusOne, mload(bufptr)) { - mstore(bufptr, offPlusOne) - } - } - - return buf; - } - - /** - * @dev Appends len bytes of bytes32 to a buffer. Resizes if doing so would - * exceed the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @param len The number of bytes to write (left-aligned). - * @return The original buffer, for chaining. - */ - function append(buffer memory buf, bytes32 data, uint256 len) private pure returns (buffer memory) { - uint256 off = buf.buf.length; - uint256 newCapacity = len + off; - if (newCapacity > buf.capacity) { - resize(buf, newCapacity * 2); - } - - unchecked { - uint256 mask = (256 ** len) - 1; - // Right-align data - data = data >> (8 * (32 - len)); - assembly { - // Memory address of the buffer data - let bufptr := mload(buf) - // Address = buffer address + sizeof(buffer length) + newCapacity - let dest := add(bufptr, newCapacity) - mstore(dest, or(and(mload(dest), not(mask)), data)) - // Update buffer length if we extended it - if gt(newCapacity, mload(bufptr)) { - mstore(bufptr, newCapacity) - } - } - } - return buf; - } - - /** - * @dev Appends a bytes20 to the buffer. Resizes if doing so would exceed - * the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @return The original buffer, for chhaining. - */ - function appendBytes20(buffer memory buf, bytes20 data) internal pure returns (buffer memory) { - return append(buf, bytes32(data), 20); - } - - /** - * @dev Appends a bytes32 to the buffer. Resizes if doing so would exceed - * the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @return The original buffer, for chaining. - */ - function appendBytes32(buffer memory buf, bytes32 data) internal pure returns (buffer memory) { - return append(buf, data, 32); - } - - /** - * @dev Appends a byte to the end of the buffer. Resizes if doing so would - * exceed the capacity of the buffer. - * @param buf The buffer to append to. - * @param data The data to append. - * @param len The number of bytes to write (right-aligned). - * @return The original buffer. - */ - function appendInt(buffer memory buf, uint256 data, uint256 len) internal pure returns (buffer memory) { - uint256 off = buf.buf.length; - uint256 newCapacity = len + off; - if (newCapacity > buf.capacity) { - resize(buf, newCapacity * 2); - } - - uint256 mask = (256 ** len) - 1; - assembly { - // Memory address of the buffer data - let bufptr := mload(buf) - // Address = buffer address + sizeof(buffer length) + newCapacity - let dest := add(bufptr, newCapacity) - mstore(dest, or(and(mload(dest), not(mask)), data)) - // Update buffer length if we extended it - if gt(newCapacity, mload(bufptr)) { - mstore(bufptr, newCapacity) - } - } - return buf; - } -} diff --git a/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/security/Pausable.sol b/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/security/Pausable.sol deleted file mode 100644 index 8cc49ec..0000000 --- a/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/security/Pausable.sol +++ /dev/null @@ -1,105 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) - -pragma solidity ^0.8.0; - -import "../utils/Context.sol"; - -/** - * @dev Contract module which allows children to implement an emergency stop - * mechanism that can be triggered by an authorized account. - * - * This module is used through inheritance. It will make available the - * modifiers `whenNotPaused` and `whenPaused`, which can be applied to - * the functions of your contract. Note that they will not be pausable by - * simply including this module, only once the modifiers are put in place. - */ -abstract contract Pausable is Context { - /** - * @dev Emitted when the pause is triggered by `account`. - */ - event Paused(address account); - - /** - * @dev Emitted when the pause is lifted by `account`. - */ - event Unpaused(address account); - - bool private _paused; - - /** - * @dev Initializes the contract in unpaused state. - */ - constructor() { - _paused = false; - } - - /** - * @dev Modifier to make a function callable only when the contract is not paused. - * - * Requirements: - * - * - The contract must not be paused. - */ - modifier whenNotPaused() { - _requireNotPaused(); - _; - } - - /** - * @dev Modifier to make a function callable only when the contract is paused. - * - * Requirements: - * - * - The contract must be paused. - */ - modifier whenPaused() { - _requirePaused(); - _; - } - - /** - * @dev Returns true if the contract is paused, and false otherwise. - */ - function paused() public view virtual returns (bool) { - return _paused; - } - - /** - * @dev Throws if the contract is paused. - */ - function _requireNotPaused() internal view virtual { - require(!paused(), "Pausable: paused"); - } - - /** - * @dev Throws if the contract is not paused. - */ - function _requirePaused() internal view virtual { - require(paused(), "Pausable: not paused"); - } - - /** - * @dev Triggers stopped state. - * - * Requirements: - * - * - The contract must not be paused. - */ - function _pause() internal virtual whenNotPaused { - _paused = true; - emit Paused(_msgSender()); - } - - /** - * @dev Returns to normal state. - * - * Requirements: - * - * - The contract must be paused. - */ - function _unpause() internal virtual whenPaused { - _paused = false; - emit Unpaused(_msgSender()); - } -} diff --git a/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/Context.sol b/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/Context.sol deleted file mode 100644 index 357980f..0000000 --- a/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/Context.sol +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts v4.4.1 (utils/Context.sol) - -pragma solidity ^0.8.0; - -/** - * @dev Provides information about the current execution context, including the - * sender of the transaction and its data. While these are generally available - * via msg.sender and msg.data, they should not be accessed in such a direct - * manner, since when dealing with meta-transactions the account sending and - * paying for execution may not be the actual sender (as far as an application - * is concerned). - * - * This contract is only required for intermediate, library-like contracts. - */ -abstract contract Context { - function _msgSender() internal view virtual returns (address) { - return msg.sender; - } - - function _msgData() internal view virtual returns (bytes calldata) { - return msg.data; - } -} diff --git a/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/SafeCast.sol b/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/SafeCast.sol deleted file mode 100644 index 28c1111..0000000 --- a/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/SafeCast.sol +++ /dev/null @@ -1,1136 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) -// This file was procedurally generated from scripts/generate/templates/SafeCast.js. - -pragma solidity ^0.8.0; - -/** - * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow - * checks. - * - * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can - * easily result in undesired exploitation or bugs, since developers usually - * assume that overflows raise errors. `SafeCast` restores this intuition by - * reverting the transaction when such an operation overflows. - * - * Using this library instead of the unchecked operations eliminates an entire - * class of bugs, so it's recommended to use it always. - * - * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing - * all math on `uint256` and `int256` and then downcasting. - */ -library SafeCast { - /** - * @dev Returns the downcasted uint248 from uint256, reverting on - * overflow (when the input is greater than largest uint248). - * - * Counterpart to Solidity's `uint248` operator. - * - * Requirements: - * - * - input must fit into 248 bits - * - * _Available since v4.7._ - */ - function toUint248(uint256 value) internal pure returns (uint248) { - require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); - return uint248(value); - } - - /** - * @dev Returns the downcasted uint240 from uint256, reverting on - * overflow (when the input is greater than largest uint240). - * - * Counterpart to Solidity's `uint240` operator. - * - * Requirements: - * - * - input must fit into 240 bits - * - * _Available since v4.7._ - */ - function toUint240(uint256 value) internal pure returns (uint240) { - require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); - return uint240(value); - } - - /** - * @dev Returns the downcasted uint232 from uint256, reverting on - * overflow (when the input is greater than largest uint232). - * - * Counterpart to Solidity's `uint232` operator. - * - * Requirements: - * - * - input must fit into 232 bits - * - * _Available since v4.7._ - */ - function toUint232(uint256 value) internal pure returns (uint232) { - require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); - return uint232(value); - } - - /** - * @dev Returns the downcasted uint224 from uint256, reverting on - * overflow (when the input is greater than largest uint224). - * - * Counterpart to Solidity's `uint224` operator. - * - * Requirements: - * - * - input must fit into 224 bits - * - * _Available since v4.2._ - */ - function toUint224(uint256 value) internal pure returns (uint224) { - require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); - return uint224(value); - } - - /** - * @dev Returns the downcasted uint216 from uint256, reverting on - * overflow (when the input is greater than largest uint216). - * - * Counterpart to Solidity's `uint216` operator. - * - * Requirements: - * - * - input must fit into 216 bits - * - * _Available since v4.7._ - */ - function toUint216(uint256 value) internal pure returns (uint216) { - require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); - return uint216(value); - } - - /** - * @dev Returns the downcasted uint208 from uint256, reverting on - * overflow (when the input is greater than largest uint208). - * - * Counterpart to Solidity's `uint208` operator. - * - * Requirements: - * - * - input must fit into 208 bits - * - * _Available since v4.7._ - */ - function toUint208(uint256 value) internal pure returns (uint208) { - require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); - return uint208(value); - } - - /** - * @dev Returns the downcasted uint200 from uint256, reverting on - * overflow (when the input is greater than largest uint200). - * - * Counterpart to Solidity's `uint200` operator. - * - * Requirements: - * - * - input must fit into 200 bits - * - * _Available since v4.7._ - */ - function toUint200(uint256 value) internal pure returns (uint200) { - require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); - return uint200(value); - } - - /** - * @dev Returns the downcasted uint192 from uint256, reverting on - * overflow (when the input is greater than largest uint192). - * - * Counterpart to Solidity's `uint192` operator. - * - * Requirements: - * - * - input must fit into 192 bits - * - * _Available since v4.7._ - */ - function toUint192(uint256 value) internal pure returns (uint192) { - require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); - return uint192(value); - } - - /** - * @dev Returns the downcasted uint184 from uint256, reverting on - * overflow (when the input is greater than largest uint184). - * - * Counterpart to Solidity's `uint184` operator. - * - * Requirements: - * - * - input must fit into 184 bits - * - * _Available since v4.7._ - */ - function toUint184(uint256 value) internal pure returns (uint184) { - require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); - return uint184(value); - } - - /** - * @dev Returns the downcasted uint176 from uint256, reverting on - * overflow (when the input is greater than largest uint176). - * - * Counterpart to Solidity's `uint176` operator. - * - * Requirements: - * - * - input must fit into 176 bits - * - * _Available since v4.7._ - */ - function toUint176(uint256 value) internal pure returns (uint176) { - require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); - return uint176(value); - } - - /** - * @dev Returns the downcasted uint168 from uint256, reverting on - * overflow (when the input is greater than largest uint168). - * - * Counterpart to Solidity's `uint168` operator. - * - * Requirements: - * - * - input must fit into 168 bits - * - * _Available since v4.7._ - */ - function toUint168(uint256 value) internal pure returns (uint168) { - require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); - return uint168(value); - } - - /** - * @dev Returns the downcasted uint160 from uint256, reverting on - * overflow (when the input is greater than largest uint160). - * - * Counterpart to Solidity's `uint160` operator. - * - * Requirements: - * - * - input must fit into 160 bits - * - * _Available since v4.7._ - */ - function toUint160(uint256 value) internal pure returns (uint160) { - require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); - return uint160(value); - } - - /** - * @dev Returns the downcasted uint152 from uint256, reverting on - * overflow (when the input is greater than largest uint152). - * - * Counterpart to Solidity's `uint152` operator. - * - * Requirements: - * - * - input must fit into 152 bits - * - * _Available since v4.7._ - */ - function toUint152(uint256 value) internal pure returns (uint152) { - require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); - return uint152(value); - } - - /** - * @dev Returns the downcasted uint144 from uint256, reverting on - * overflow (when the input is greater than largest uint144). - * - * Counterpart to Solidity's `uint144` operator. - * - * Requirements: - * - * - input must fit into 144 bits - * - * _Available since v4.7._ - */ - function toUint144(uint256 value) internal pure returns (uint144) { - require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); - return uint144(value); - } - - /** - * @dev Returns the downcasted uint136 from uint256, reverting on - * overflow (when the input is greater than largest uint136). - * - * Counterpart to Solidity's `uint136` operator. - * - * Requirements: - * - * - input must fit into 136 bits - * - * _Available since v4.7._ - */ - function toUint136(uint256 value) internal pure returns (uint136) { - require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); - return uint136(value); - } - - /** - * @dev Returns the downcasted uint128 from uint256, reverting on - * overflow (when the input is greater than largest uint128). - * - * Counterpart to Solidity's `uint128` operator. - * - * Requirements: - * - * - input must fit into 128 bits - * - * _Available since v2.5._ - */ - function toUint128(uint256 value) internal pure returns (uint128) { - require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); - return uint128(value); - } - - /** - * @dev Returns the downcasted uint120 from uint256, reverting on - * overflow (when the input is greater than largest uint120). - * - * Counterpart to Solidity's `uint120` operator. - * - * Requirements: - * - * - input must fit into 120 bits - * - * _Available since v4.7._ - */ - function toUint120(uint256 value) internal pure returns (uint120) { - require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); - return uint120(value); - } - - /** - * @dev Returns the downcasted uint112 from uint256, reverting on - * overflow (when the input is greater than largest uint112). - * - * Counterpart to Solidity's `uint112` operator. - * - * Requirements: - * - * - input must fit into 112 bits - * - * _Available since v4.7._ - */ - function toUint112(uint256 value) internal pure returns (uint112) { - require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); - return uint112(value); - } - - /** - * @dev Returns the downcasted uint104 from uint256, reverting on - * overflow (when the input is greater than largest uint104). - * - * Counterpart to Solidity's `uint104` operator. - * - * Requirements: - * - * - input must fit into 104 bits - * - * _Available since v4.7._ - */ - function toUint104(uint256 value) internal pure returns (uint104) { - require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); - return uint104(value); - } - - /** - * @dev Returns the downcasted uint96 from uint256, reverting on - * overflow (when the input is greater than largest uint96). - * - * Counterpart to Solidity's `uint96` operator. - * - * Requirements: - * - * - input must fit into 96 bits - * - * _Available since v4.2._ - */ - function toUint96(uint256 value) internal pure returns (uint96) { - require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); - return uint96(value); - } - - /** - * @dev Returns the downcasted uint88 from uint256, reverting on - * overflow (when the input is greater than largest uint88). - * - * Counterpart to Solidity's `uint88` operator. - * - * Requirements: - * - * - input must fit into 88 bits - * - * _Available since v4.7._ - */ - function toUint88(uint256 value) internal pure returns (uint88) { - require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); - return uint88(value); - } - - /** - * @dev Returns the downcasted uint80 from uint256, reverting on - * overflow (when the input is greater than largest uint80). - * - * Counterpart to Solidity's `uint80` operator. - * - * Requirements: - * - * - input must fit into 80 bits - * - * _Available since v4.7._ - */ - function toUint80(uint256 value) internal pure returns (uint80) { - require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); - return uint80(value); - } - - /** - * @dev Returns the downcasted uint72 from uint256, reverting on - * overflow (when the input is greater than largest uint72). - * - * Counterpart to Solidity's `uint72` operator. - * - * Requirements: - * - * - input must fit into 72 bits - * - * _Available since v4.7._ - */ - function toUint72(uint256 value) internal pure returns (uint72) { - require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); - return uint72(value); - } - - /** - * @dev Returns the downcasted uint64 from uint256, reverting on - * overflow (when the input is greater than largest uint64). - * - * Counterpart to Solidity's `uint64` operator. - * - * Requirements: - * - * - input must fit into 64 bits - * - * _Available since v2.5._ - */ - function toUint64(uint256 value) internal pure returns (uint64) { - require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); - return uint64(value); - } - - /** - * @dev Returns the downcasted uint56 from uint256, reverting on - * overflow (when the input is greater than largest uint56). - * - * Counterpart to Solidity's `uint56` operator. - * - * Requirements: - * - * - input must fit into 56 bits - * - * _Available since v4.7._ - */ - function toUint56(uint256 value) internal pure returns (uint56) { - require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); - return uint56(value); - } - - /** - * @dev Returns the downcasted uint48 from uint256, reverting on - * overflow (when the input is greater than largest uint48). - * - * Counterpart to Solidity's `uint48` operator. - * - * Requirements: - * - * - input must fit into 48 bits - * - * _Available since v4.7._ - */ - function toUint48(uint256 value) internal pure returns (uint48) { - require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); - return uint48(value); - } - - /** - * @dev Returns the downcasted uint40 from uint256, reverting on - * overflow (when the input is greater than largest uint40). - * - * Counterpart to Solidity's `uint40` operator. - * - * Requirements: - * - * - input must fit into 40 bits - * - * _Available since v4.7._ - */ - function toUint40(uint256 value) internal pure returns (uint40) { - require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); - return uint40(value); - } - - /** - * @dev Returns the downcasted uint32 from uint256, reverting on - * overflow (when the input is greater than largest uint32). - * - * Counterpart to Solidity's `uint32` operator. - * - * Requirements: - * - * - input must fit into 32 bits - * - * _Available since v2.5._ - */ - function toUint32(uint256 value) internal pure returns (uint32) { - require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); - return uint32(value); - } - - /** - * @dev Returns the downcasted uint24 from uint256, reverting on - * overflow (when the input is greater than largest uint24). - * - * Counterpart to Solidity's `uint24` operator. - * - * Requirements: - * - * - input must fit into 24 bits - * - * _Available since v4.7._ - */ - function toUint24(uint256 value) internal pure returns (uint24) { - require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); - return uint24(value); - } - - /** - * @dev Returns the downcasted uint16 from uint256, reverting on - * overflow (when the input is greater than largest uint16). - * - * Counterpart to Solidity's `uint16` operator. - * - * Requirements: - * - * - input must fit into 16 bits - * - * _Available since v2.5._ - */ - function toUint16(uint256 value) internal pure returns (uint16) { - require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); - return uint16(value); - } - - /** - * @dev Returns the downcasted uint8 from uint256, reverting on - * overflow (when the input is greater than largest uint8). - * - * Counterpart to Solidity's `uint8` operator. - * - * Requirements: - * - * - input must fit into 8 bits - * - * _Available since v2.5._ - */ - function toUint8(uint256 value) internal pure returns (uint8) { - require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); - return uint8(value); - } - - /** - * @dev Converts a signed int256 into an unsigned uint256. - * - * Requirements: - * - * - input must be greater than or equal to 0. - * - * _Available since v3.0._ - */ - function toUint256(int256 value) internal pure returns (uint256) { - require(value >= 0, "SafeCast: value must be positive"); - return uint256(value); - } - - /** - * @dev Returns the downcasted int248 from int256, reverting on - * overflow (when the input is less than smallest int248 or - * greater than largest int248). - * - * Counterpart to Solidity's `int248` operator. - * - * Requirements: - * - * - input must fit into 248 bits - * - * _Available since v4.7._ - */ - function toInt248(int256 value) internal pure returns (int248 downcasted) { - downcasted = int248(value); - require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); - } - - /** - * @dev Returns the downcasted int240 from int256, reverting on - * overflow (when the input is less than smallest int240 or - * greater than largest int240). - * - * Counterpart to Solidity's `int240` operator. - * - * Requirements: - * - * - input must fit into 240 bits - * - * _Available since v4.7._ - */ - function toInt240(int256 value) internal pure returns (int240 downcasted) { - downcasted = int240(value); - require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); - } - - /** - * @dev Returns the downcasted int232 from int256, reverting on - * overflow (when the input is less than smallest int232 or - * greater than largest int232). - * - * Counterpart to Solidity's `int232` operator. - * - * Requirements: - * - * - input must fit into 232 bits - * - * _Available since v4.7._ - */ - function toInt232(int256 value) internal pure returns (int232 downcasted) { - downcasted = int232(value); - require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); - } - - /** - * @dev Returns the downcasted int224 from int256, reverting on - * overflow (when the input is less than smallest int224 or - * greater than largest int224). - * - * Counterpart to Solidity's `int224` operator. - * - * Requirements: - * - * - input must fit into 224 bits - * - * _Available since v4.7._ - */ - function toInt224(int256 value) internal pure returns (int224 downcasted) { - downcasted = int224(value); - require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); - } - - /** - * @dev Returns the downcasted int216 from int256, reverting on - * overflow (when the input is less than smallest int216 or - * greater than largest int216). - * - * Counterpart to Solidity's `int216` operator. - * - * Requirements: - * - * - input must fit into 216 bits - * - * _Available since v4.7._ - */ - function toInt216(int256 value) internal pure returns (int216 downcasted) { - downcasted = int216(value); - require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); - } - - /** - * @dev Returns the downcasted int208 from int256, reverting on - * overflow (when the input is less than smallest int208 or - * greater than largest int208). - * - * Counterpart to Solidity's `int208` operator. - * - * Requirements: - * - * - input must fit into 208 bits - * - * _Available since v4.7._ - */ - function toInt208(int256 value) internal pure returns (int208 downcasted) { - downcasted = int208(value); - require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); - } - - /** - * @dev Returns the downcasted int200 from int256, reverting on - * overflow (when the input is less than smallest int200 or - * greater than largest int200). - * - * Counterpart to Solidity's `int200` operator. - * - * Requirements: - * - * - input must fit into 200 bits - * - * _Available since v4.7._ - */ - function toInt200(int256 value) internal pure returns (int200 downcasted) { - downcasted = int200(value); - require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); - } - - /** - * @dev Returns the downcasted int192 from int256, reverting on - * overflow (when the input is less than smallest int192 or - * greater than largest int192). - * - * Counterpart to Solidity's `int192` operator. - * - * Requirements: - * - * - input must fit into 192 bits - * - * _Available since v4.7._ - */ - function toInt192(int256 value) internal pure returns (int192 downcasted) { - downcasted = int192(value); - require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); - } - - /** - * @dev Returns the downcasted int184 from int256, reverting on - * overflow (when the input is less than smallest int184 or - * greater than largest int184). - * - * Counterpart to Solidity's `int184` operator. - * - * Requirements: - * - * - input must fit into 184 bits - * - * _Available since v4.7._ - */ - function toInt184(int256 value) internal pure returns (int184 downcasted) { - downcasted = int184(value); - require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); - } - - /** - * @dev Returns the downcasted int176 from int256, reverting on - * overflow (when the input is less than smallest int176 or - * greater than largest int176). - * - * Counterpart to Solidity's `int176` operator. - * - * Requirements: - * - * - input must fit into 176 bits - * - * _Available since v4.7._ - */ - function toInt176(int256 value) internal pure returns (int176 downcasted) { - downcasted = int176(value); - require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); - } - - /** - * @dev Returns the downcasted int168 from int256, reverting on - * overflow (when the input is less than smallest int168 or - * greater than largest int168). - * - * Counterpart to Solidity's `int168` operator. - * - * Requirements: - * - * - input must fit into 168 bits - * - * _Available since v4.7._ - */ - function toInt168(int256 value) internal pure returns (int168 downcasted) { - downcasted = int168(value); - require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); - } - - /** - * @dev Returns the downcasted int160 from int256, reverting on - * overflow (when the input is less than smallest int160 or - * greater than largest int160). - * - * Counterpart to Solidity's `int160` operator. - * - * Requirements: - * - * - input must fit into 160 bits - * - * _Available since v4.7._ - */ - function toInt160(int256 value) internal pure returns (int160 downcasted) { - downcasted = int160(value); - require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); - } - - /** - * @dev Returns the downcasted int152 from int256, reverting on - * overflow (when the input is less than smallest int152 or - * greater than largest int152). - * - * Counterpart to Solidity's `int152` operator. - * - * Requirements: - * - * - input must fit into 152 bits - * - * _Available since v4.7._ - */ - function toInt152(int256 value) internal pure returns (int152 downcasted) { - downcasted = int152(value); - require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); - } - - /** - * @dev Returns the downcasted int144 from int256, reverting on - * overflow (when the input is less than smallest int144 or - * greater than largest int144). - * - * Counterpart to Solidity's `int144` operator. - * - * Requirements: - * - * - input must fit into 144 bits - * - * _Available since v4.7._ - */ - function toInt144(int256 value) internal pure returns (int144 downcasted) { - downcasted = int144(value); - require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); - } - - /** - * @dev Returns the downcasted int136 from int256, reverting on - * overflow (when the input is less than smallest int136 or - * greater than largest int136). - * - * Counterpart to Solidity's `int136` operator. - * - * Requirements: - * - * - input must fit into 136 bits - * - * _Available since v4.7._ - */ - function toInt136(int256 value) internal pure returns (int136 downcasted) { - downcasted = int136(value); - require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); - } - - /** - * @dev Returns the downcasted int128 from int256, reverting on - * overflow (when the input is less than smallest int128 or - * greater than largest int128). - * - * Counterpart to Solidity's `int128` operator. - * - * Requirements: - * - * - input must fit into 128 bits - * - * _Available since v3.1._ - */ - function toInt128(int256 value) internal pure returns (int128 downcasted) { - downcasted = int128(value); - require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); - } - - /** - * @dev Returns the downcasted int120 from int256, reverting on - * overflow (when the input is less than smallest int120 or - * greater than largest int120). - * - * Counterpart to Solidity's `int120` operator. - * - * Requirements: - * - * - input must fit into 120 bits - * - * _Available since v4.7._ - */ - function toInt120(int256 value) internal pure returns (int120 downcasted) { - downcasted = int120(value); - require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); - } - - /** - * @dev Returns the downcasted int112 from int256, reverting on - * overflow (when the input is less than smallest int112 or - * greater than largest int112). - * - * Counterpart to Solidity's `int112` operator. - * - * Requirements: - * - * - input must fit into 112 bits - * - * _Available since v4.7._ - */ - function toInt112(int256 value) internal pure returns (int112 downcasted) { - downcasted = int112(value); - require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); - } - - /** - * @dev Returns the downcasted int104 from int256, reverting on - * overflow (when the input is less than smallest int104 or - * greater than largest int104). - * - * Counterpart to Solidity's `int104` operator. - * - * Requirements: - * - * - input must fit into 104 bits - * - * _Available since v4.7._ - */ - function toInt104(int256 value) internal pure returns (int104 downcasted) { - downcasted = int104(value); - require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); - } - - /** - * @dev Returns the downcasted int96 from int256, reverting on - * overflow (when the input is less than smallest int96 or - * greater than largest int96). - * - * Counterpart to Solidity's `int96` operator. - * - * Requirements: - * - * - input must fit into 96 bits - * - * _Available since v4.7._ - */ - function toInt96(int256 value) internal pure returns (int96 downcasted) { - downcasted = int96(value); - require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); - } - - /** - * @dev Returns the downcasted int88 from int256, reverting on - * overflow (when the input is less than smallest int88 or - * greater than largest int88). - * - * Counterpart to Solidity's `int88` operator. - * - * Requirements: - * - * - input must fit into 88 bits - * - * _Available since v4.7._ - */ - function toInt88(int256 value) internal pure returns (int88 downcasted) { - downcasted = int88(value); - require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); - } - - /** - * @dev Returns the downcasted int80 from int256, reverting on - * overflow (when the input is less than smallest int80 or - * greater than largest int80). - * - * Counterpart to Solidity's `int80` operator. - * - * Requirements: - * - * - input must fit into 80 bits - * - * _Available since v4.7._ - */ - function toInt80(int256 value) internal pure returns (int80 downcasted) { - downcasted = int80(value); - require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); - } - - /** - * @dev Returns the downcasted int72 from int256, reverting on - * overflow (when the input is less than smallest int72 or - * greater than largest int72). - * - * Counterpart to Solidity's `int72` operator. - * - * Requirements: - * - * - input must fit into 72 bits - * - * _Available since v4.7._ - */ - function toInt72(int256 value) internal pure returns (int72 downcasted) { - downcasted = int72(value); - require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); - } - - /** - * @dev Returns the downcasted int64 from int256, reverting on - * overflow (when the input is less than smallest int64 or - * greater than largest int64). - * - * Counterpart to Solidity's `int64` operator. - * - * Requirements: - * - * - input must fit into 64 bits - * - * _Available since v3.1._ - */ - function toInt64(int256 value) internal pure returns (int64 downcasted) { - downcasted = int64(value); - require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); - } - - /** - * @dev Returns the downcasted int56 from int256, reverting on - * overflow (when the input is less than smallest int56 or - * greater than largest int56). - * - * Counterpart to Solidity's `int56` operator. - * - * Requirements: - * - * - input must fit into 56 bits - * - * _Available since v4.7._ - */ - function toInt56(int256 value) internal pure returns (int56 downcasted) { - downcasted = int56(value); - require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); - } - - /** - * @dev Returns the downcasted int48 from int256, reverting on - * overflow (when the input is less than smallest int48 or - * greater than largest int48). - * - * Counterpart to Solidity's `int48` operator. - * - * Requirements: - * - * - input must fit into 48 bits - * - * _Available since v4.7._ - */ - function toInt48(int256 value) internal pure returns (int48 downcasted) { - downcasted = int48(value); - require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); - } - - /** - * @dev Returns the downcasted int40 from int256, reverting on - * overflow (when the input is less than smallest int40 or - * greater than largest int40). - * - * Counterpart to Solidity's `int40` operator. - * - * Requirements: - * - * - input must fit into 40 bits - * - * _Available since v4.7._ - */ - function toInt40(int256 value) internal pure returns (int40 downcasted) { - downcasted = int40(value); - require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); - } - - /** - * @dev Returns the downcasted int32 from int256, reverting on - * overflow (when the input is less than smallest int32 or - * greater than largest int32). - * - * Counterpart to Solidity's `int32` operator. - * - * Requirements: - * - * - input must fit into 32 bits - * - * _Available since v3.1._ - */ - function toInt32(int256 value) internal pure returns (int32 downcasted) { - downcasted = int32(value); - require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); - } - - /** - * @dev Returns the downcasted int24 from int256, reverting on - * overflow (when the input is less than smallest int24 or - * greater than largest int24). - * - * Counterpart to Solidity's `int24` operator. - * - * Requirements: - * - * - input must fit into 24 bits - * - * _Available since v4.7._ - */ - function toInt24(int256 value) internal pure returns (int24 downcasted) { - downcasted = int24(value); - require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); - } - - /** - * @dev Returns the downcasted int16 from int256, reverting on - * overflow (when the input is less than smallest int16 or - * greater than largest int16). - * - * Counterpart to Solidity's `int16` operator. - * - * Requirements: - * - * - input must fit into 16 bits - * - * _Available since v3.1._ - */ - function toInt16(int256 value) internal pure returns (int16 downcasted) { - downcasted = int16(value); - require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); - } - - /** - * @dev Returns the downcasted int8 from int256, reverting on - * overflow (when the input is less than smallest int8 or - * greater than largest int8). - * - * Counterpart to Solidity's `int8` operator. - * - * Requirements: - * - * - input must fit into 8 bits - * - * _Available since v3.1._ - */ - function toInt8(int256 value) internal pure returns (int8 downcasted) { - downcasted = int8(value); - require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); - } - - /** - * @dev Converts an unsigned uint256 into a signed int256. - * - * Requirements: - * - * - input must be less than or equal to maxInt256. - * - * _Available since v3.0._ - */ - function toInt256(uint256 value) internal pure returns (int256) { - // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive - require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); - return int256(value); - } -} diff --git a/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/structs/EnumerableSet.sol b/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/structs/EnumerableSet.sol deleted file mode 100644 index 0bcbefe..0000000 --- a/contracts/contracts/dev/vendor/openzeppelin-solidity/v.4.8.0/contracts/utils/structs/EnumerableSet.sol +++ /dev/null @@ -1,378 +0,0 @@ -// SPDX-License-Identifier: MIT -// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) -// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. - -pragma solidity ^0.8.0; - -/** - * @dev Library for managing - * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive - * types. - * - * Sets have the following properties: - * - * - Elements are added, removed, and checked for existence in constant time - * (O(1)). - * - Elements are enumerated in O(n). No guarantees are made on the ordering. - * - * ``` - * contract Example { - * // Add the library methods - * using EnumerableSet for EnumerableSet.AddressSet; - * - * // Declare a set state variable - * EnumerableSet.AddressSet private mySet; - * } - * ``` - * - * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) - * and `uint256` (`UintSet`) are supported. - * - * [WARNING] - * ==== - * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure - * unusable. - * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. - * - * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an - * array of EnumerableSet. - * ==== - */ -library EnumerableSet { - // To implement this library for multiple types with as little code - // repetition as possible, we write it in terms of a generic Set type with - // bytes32 values. - // The Set implementation uses private functions, and user-facing - // implementations (such as AddressSet) are just wrappers around the - // underlying Set. - // This means that we can only create new EnumerableSets for types that fit - // in bytes32. - - struct Set { - // Storage of set values - bytes32[] _values; - // Position of the value in the `values` array, plus 1 because index 0 - // means a value is not in the set. - mapping(bytes32 => uint256) _indexes; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function _add(Set storage set, bytes32 value) private returns (bool) { - if (!_contains(set, value)) { - set._values.push(value); - // The value is stored at length-1, but we add 1 to all indexes - // and use 0 as a sentinel value - set._indexes[value] = set._values.length; - return true; - } else { - return false; - } - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function _remove(Set storage set, bytes32 value) private returns (bool) { - // We read and store the value's index to prevent multiple reads from the same storage slot - uint256 valueIndex = set._indexes[value]; - - if (valueIndex != 0) { - // Equivalent to contains(set, value) - // To delete an element from the _values array in O(1), we swap the element to delete with the last one in - // the array, and then remove the last element (sometimes called as 'swap and pop'). - // This modifies the order of the array, as noted in {at}. - - uint256 toDeleteIndex = valueIndex - 1; - uint256 lastIndex = set._values.length - 1; - - if (lastIndex != toDeleteIndex) { - bytes32 lastValue = set._values[lastIndex]; - - // Move the last value to the index where the value to delete is - set._values[toDeleteIndex] = lastValue; - // Update the index for the moved value - set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex - } - - // Delete the slot where the moved value was stored - set._values.pop(); - - // Delete the index for the deleted slot - delete set._indexes[value]; - - return true; - } else { - return false; - } - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function _contains(Set storage set, bytes32 value) private view returns (bool) { - return set._indexes[value] != 0; - } - - /** - * @dev Returns the number of values on the set. O(1). - */ - function _length(Set storage set) private view returns (uint256) { - return set._values.length; - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function _at(Set storage set, uint256 index) private view returns (bytes32) { - return set._values[index]; - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function _values(Set storage set) private view returns (bytes32[] memory) { - return set._values; - } - - // Bytes32Set - - struct Bytes32Set { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _add(set._inner, value); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { - return _remove(set._inner, value); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { - return _contains(set._inner, value); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(Bytes32Set storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { - return _at(set._inner, index); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { - bytes32[] memory store = _values(set._inner); - bytes32[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // AddressSet - - struct AddressSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(AddressSet storage set, address value) internal returns (bool) { - return _add(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(AddressSet storage set, address value) internal returns (bool) { - return _remove(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(AddressSet storage set, address value) internal view returns (bool) { - return _contains(set._inner, bytes32(uint256(uint160(value)))); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(AddressSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(AddressSet storage set, uint256 index) internal view returns (address) { - return address(uint160(uint256(_at(set._inner, index)))); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(AddressSet storage set) internal view returns (address[] memory) { - bytes32[] memory store = _values(set._inner); - address[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } - - // UintSet - - struct UintSet { - Set _inner; - } - - /** - * @dev Add a value to a set. O(1). - * - * Returns true if the value was added to the set, that is if it was not - * already present. - */ - function add(UintSet storage set, uint256 value) internal returns (bool) { - return _add(set._inner, bytes32(value)); - } - - /** - * @dev Removes a value from a set. O(1). - * - * Returns true if the value was removed from the set, that is if it was - * present. - */ - function remove(UintSet storage set, uint256 value) internal returns (bool) { - return _remove(set._inner, bytes32(value)); - } - - /** - * @dev Returns true if the value is in the set. O(1). - */ - function contains(UintSet storage set, uint256 value) internal view returns (bool) { - return _contains(set._inner, bytes32(value)); - } - - /** - * @dev Returns the number of values in the set. O(1). - */ - function length(UintSet storage set) internal view returns (uint256) { - return _length(set._inner); - } - - /** - * @dev Returns the value stored at position `index` in the set. O(1). - * - * Note that there are no guarantees on the ordering of values inside the - * array, and it may change when more values are added or removed. - * - * Requirements: - * - * - `index` must be strictly less than {length}. - */ - function at(UintSet storage set, uint256 index) internal view returns (uint256) { - return uint256(_at(set._inner, index)); - } - - /** - * @dev Return the entire set in an array - * - * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed - * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that - * this function has an unbounded cost, and using it as part of a state-changing function may render the function - * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. - */ - function values(UintSet storage set) internal view returns (uint256[] memory) { - bytes32[] memory store = _values(set._inner); - uint256[] memory result; - - /// @solidity memory-safe-assembly - assembly { - result := store - } - - return result; - } -} diff --git a/contracts/contracts/dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol b/contracts/contracts/dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol deleted file mode 100644 index 026f10b..0000000 --- a/contracts/contracts/dev/vendor/openzeppelin-solidity/v4.3.1/contracts/utils/Address.sol +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.0; - -/** - * @dev Collection of functions related to the address type - */ -library Address { - /** - * @dev Returns true if `account` is a contract. - * - * [IMPORTANT] - * ==== - * It is unsafe to assume that an address for which this function returns - * false is an externally-owned account (EOA) and not a contract. - * - * Among others, `isContract` will return false for the following - * types of addresses: - * - * - an externally-owned account - * - a contract in construction - * - an address where a contract will be created - * - an address where a contract lived, but was destroyed - * ==== - */ - function isContract(address account) internal view returns (bool) { - // This method relies on extcodesize, which returns 0 for contracts in - // construction, since the code is only stored at the end of the - // constructor execution. - - uint256 size; - assembly { - size := extcodesize(account) - } - return size > 0; - } - - /** - * @dev Replacement for Solidity's `transfer`: sends `amount` wei to - * `recipient`, forwarding all available gas and reverting on errors. - * - * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost - * of certain opcodes, possibly making contracts go over the 2300 gas limit - * imposed by `transfer`, making them unable to receive funds via - * `transfer`. {sendValue} removes this limitation. - * - * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. - * - * IMPORTANT: because control is transferred to `recipient`, care must be - * taken to not create reentrancy vulnerabilities. Consider using - * {ReentrancyGuard} or the - * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. - */ - function sendValue(address payable recipient, uint256 amount) internal { - require(address(this).balance >= amount, "Address: insufficient balance"); - - (bool success, ) = recipient.call{value: amount}(""); - require(success, "Address: unable to send value, recipient may have reverted"); - } - - /** - * @dev Performs a Solidity function call using a low level `call`. A - * plain `call` is an unsafe replacement for a function call: use this - * function instead. - * - * If `target` reverts with a revert reason, it is bubbled up by this - * function (like regular Solidity function calls). - * - * Returns the raw returned data. To convert to the expected return value, - * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. - * - * Requirements: - * - * - `target` must be a contract. - * - calling `target` with `data` must not revert. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data) internal returns (bytes memory) { - return functionCall(target, data, "Address: low-level call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with - * `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { - return functionCallWithValue(target, data, 0, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but also transferring `value` wei to `target`. - * - * Requirements: - * - * - the calling contract must have an ETH balance of at least `value`. - * - the called Solidity function must be `payable`. - * - * _Available since v3.1._ - */ - function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { - return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); - } - - /** - * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but - * with `errorMessage` as a fallback revert reason when `target` reverts. - * - * _Available since v3.1._ - */ - function functionCallWithValue( - address target, - bytes memory data, - uint256 value, - string memory errorMessage - ) internal returns (bytes memory) { - require(address(this).balance >= value, "Address: insufficient balance for call"); - require(isContract(target), "Address: call to non-contract"); - - (bool success, bytes memory returndata) = target.call{value: value}(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { - return functionStaticCall(target, data, "Address: low-level static call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a static call. - * - * _Available since v3.3._ - */ - function functionStaticCall( - address target, - bytes memory data, - string memory errorMessage - ) internal view returns (bytes memory) { - require(isContract(target), "Address: static call to non-contract"); - - (bool success, bytes memory returndata) = target.staticcall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { - return functionDelegateCall(target, data, "Address: low-level delegate call failed"); - } - - /** - * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], - * but performing a delegate call. - * - * _Available since v3.4._ - */ - function functionDelegateCall( - address target, - bytes memory data, - string memory errorMessage - ) internal returns (bytes memory) { - require(isContract(target), "Address: delegate call to non-contract"); - - (bool success, bytes memory returndata) = target.delegatecall(data); - return verifyCallResult(success, returndata, errorMessage); - } - - /** - * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the - * revert reason using the provided one. - * - * _Available since v4.3._ - */ - function verifyCallResult( - bool success, - bytes memory returndata, - string memory errorMessage - ) internal pure returns (bytes memory) { - if (success) { - return returndata; - } else { - // Look for revert reason and bubble it up if present - if (returndata.length > 0) { - // The easiest way to bubble the revert reason is using memory via assembly - - assembly { - let returndata_size := mload(returndata) - revert(add(32, returndata), returndata_size) - } - } else { - revert(errorMessage); - } - } - } -} diff --git a/contracts/contracts/dev/vendor/solidity-cborutils/2.0.0/CBOR.sol b/contracts/contracts/dev/vendor/solidity-cborutils/2.0.0/CBOR.sol deleted file mode 100644 index 6ba76d9..0000000 --- a/contracts/contracts/dev/vendor/solidity-cborutils/2.0.0/CBOR.sol +++ /dev/null @@ -1,210 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.4; - -import "../../@ensdomains/buffer/0.1.0/Buffer.sol"; - -/** - * @dev A library for populating CBOR encoded payload in Solidity. - * - * https://datatracker.ietf.org/doc/html/rfc7049 - * - * The library offers various write* and start* methods to encode values of different types. - * The resulted buffer can be obtained with data() method. - * Encoding of primitive types is staightforward, whereas encoding of sequences can result - * in an invalid CBOR if start/write/end flow is violated. - * For the purpose of gas saving, the library does not verify start/write/end flow internally, - * except for nested start/end pairs. - */ - -library CBOR { - using Buffer for Buffer.buffer; - - struct CBORBuffer { - Buffer.buffer buf; - uint256 depth; - } - - uint8 private constant MAJOR_TYPE_INT = 0; - uint8 private constant MAJOR_TYPE_NEGATIVE_INT = 1; - uint8 private constant MAJOR_TYPE_BYTES = 2; - uint8 private constant MAJOR_TYPE_STRING = 3; - uint8 private constant MAJOR_TYPE_ARRAY = 4; - uint8 private constant MAJOR_TYPE_MAP = 5; - uint8 private constant MAJOR_TYPE_TAG = 6; - uint8 private constant MAJOR_TYPE_CONTENT_FREE = 7; - - uint8 private constant TAG_TYPE_BIGNUM = 2; - uint8 private constant TAG_TYPE_NEGATIVE_BIGNUM = 3; - - uint8 private constant CBOR_FALSE = 20; - uint8 private constant CBOR_TRUE = 21; - uint8 private constant CBOR_NULL = 22; - uint8 private constant CBOR_UNDEFINED = 23; - - function create(uint256 capacity) internal pure returns (CBORBuffer memory cbor) { - Buffer.init(cbor.buf, capacity); - cbor.depth = 0; - return cbor; - } - - function data(CBORBuffer memory buf) internal pure returns (bytes memory) { - require(buf.depth == 0, "Invalid CBOR"); - return buf.buf.buf; - } - - function writeUInt256(CBORBuffer memory buf, uint256 value) internal pure { - buf.buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_BIGNUM)); - writeBytes(buf, abi.encode(value)); - } - - function writeInt256(CBORBuffer memory buf, int256 value) internal pure { - if (value < 0) { - buf.buf.appendUint8(uint8((MAJOR_TYPE_TAG << 5) | TAG_TYPE_NEGATIVE_BIGNUM)); - writeBytes(buf, abi.encode(uint256(-1 - value))); - } else { - writeUInt256(buf, uint256(value)); - } - } - - function writeUInt64(CBORBuffer memory buf, uint64 value) internal pure { - writeFixedNumeric(buf, MAJOR_TYPE_INT, value); - } - - function writeInt64(CBORBuffer memory buf, int64 value) internal pure { - if (value >= 0) { - writeFixedNumeric(buf, MAJOR_TYPE_INT, uint64(value)); - } else { - writeFixedNumeric(buf, MAJOR_TYPE_NEGATIVE_INT, uint64(-1 - value)); - } - } - - function writeBytes(CBORBuffer memory buf, bytes memory value) internal pure { - writeFixedNumeric(buf, MAJOR_TYPE_BYTES, uint64(value.length)); - buf.buf.append(value); - } - - function writeString(CBORBuffer memory buf, string memory value) internal pure { - writeFixedNumeric(buf, MAJOR_TYPE_STRING, uint64(bytes(value).length)); - buf.buf.append(bytes(value)); - } - - function writeBool(CBORBuffer memory buf, bool value) internal pure { - writeContentFree(buf, value ? CBOR_TRUE : CBOR_FALSE); - } - - function writeNull(CBORBuffer memory buf) internal pure { - writeContentFree(buf, CBOR_NULL); - } - - function writeUndefined(CBORBuffer memory buf) internal pure { - writeContentFree(buf, CBOR_UNDEFINED); - } - - function startArray(CBORBuffer memory buf) internal pure { - writeIndefiniteLengthType(buf, MAJOR_TYPE_ARRAY); - buf.depth += 1; - } - - function startFixedArray(CBORBuffer memory buf, uint64 length) internal pure { - writeDefiniteLengthType(buf, MAJOR_TYPE_ARRAY, length); - } - - function startMap(CBORBuffer memory buf) internal pure { - writeIndefiniteLengthType(buf, MAJOR_TYPE_MAP); - buf.depth += 1; - } - - function startFixedMap(CBORBuffer memory buf, uint64 length) internal pure { - writeDefiniteLengthType(buf, MAJOR_TYPE_MAP, length); - } - - function endSequence(CBORBuffer memory buf) internal pure { - writeIndefiniteLengthType(buf, MAJOR_TYPE_CONTENT_FREE); - buf.depth -= 1; - } - - function writeKVString(CBORBuffer memory buf, string memory key, string memory value) internal pure { - writeString(buf, key); - writeString(buf, value); - } - - function writeKVBytes(CBORBuffer memory buf, string memory key, bytes memory value) internal pure { - writeString(buf, key); - writeBytes(buf, value); - } - - function writeKVUInt256(CBORBuffer memory buf, string memory key, uint256 value) internal pure { - writeString(buf, key); - writeUInt256(buf, value); - } - - function writeKVInt256(CBORBuffer memory buf, string memory key, int256 value) internal pure { - writeString(buf, key); - writeInt256(buf, value); - } - - function writeKVUInt64(CBORBuffer memory buf, string memory key, uint64 value) internal pure { - writeString(buf, key); - writeUInt64(buf, value); - } - - function writeKVInt64(CBORBuffer memory buf, string memory key, int64 value) internal pure { - writeString(buf, key); - writeInt64(buf, value); - } - - function writeKVBool(CBORBuffer memory buf, string memory key, bool value) internal pure { - writeString(buf, key); - writeBool(buf, value); - } - - function writeKVNull(CBORBuffer memory buf, string memory key) internal pure { - writeString(buf, key); - writeNull(buf); - } - - function writeKVUndefined(CBORBuffer memory buf, string memory key) internal pure { - writeString(buf, key); - writeUndefined(buf); - } - - function writeKVMap(CBORBuffer memory buf, string memory key) internal pure { - writeString(buf, key); - startMap(buf); - } - - function writeKVArray(CBORBuffer memory buf, string memory key) internal pure { - writeString(buf, key); - startArray(buf); - } - - function writeFixedNumeric(CBORBuffer memory buf, uint8 major, uint64 value) private pure { - if (value <= 23) { - buf.buf.appendUint8(uint8((major << 5) | value)); - } else if (value <= 0xFF) { - buf.buf.appendUint8(uint8((major << 5) | 24)); - buf.buf.appendInt(value, 1); - } else if (value <= 0xFFFF) { - buf.buf.appendUint8(uint8((major << 5) | 25)); - buf.buf.appendInt(value, 2); - } else if (value <= 0xFFFFFFFF) { - buf.buf.appendUint8(uint8((major << 5) | 26)); - buf.buf.appendInt(value, 4); - } else { - buf.buf.appendUint8(uint8((major << 5) | 27)); - buf.buf.appendInt(value, 8); - } - } - - function writeIndefiniteLengthType(CBORBuffer memory buf, uint8 major) private pure { - buf.buf.appendUint8(uint8((major << 5) | 31)); - } - - function writeDefiniteLengthType(CBORBuffer memory buf, uint8 major, uint64 length) private pure { - writeFixedNumeric(buf, major, length); - } - - function writeContentFree(CBORBuffer memory buf, uint8 value) private pure { - buf.buf.appendUint8(uint8((MAJOR_TYPE_CONTENT_FREE << 5) | value)); - } -} diff --git a/contracts/contracts/test/FunctionsConsumer.sol b/contracts/contracts/test/FunctionsConsumer.sol deleted file mode 100644 index ea238b6..0000000 --- a/contracts/contracts/test/FunctionsConsumer.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.7; - -import {Functions, FunctionsClient} from "../dev/functions/FunctionsClient.sol"; -// import "@chainlink/contracts/src/v0.8/dev/functions/FunctionsClient.sol"; // Once published -import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol"; - -/** - * @title Functions Consumer contract - * @notice This contract is a demonstration of using Functions. - * @notice NOT FOR PRODUCTION USE - */ -contract FunctionsConsumer is FunctionsClient, ConfirmedOwner { - using Functions for Functions.Request; - - bytes32 public latestRequestId; - bytes public latestResponse; - bytes public latestError; - - event OCRResponse(bytes32 indexed requestId, bytes result, bytes err); - - /** - * @notice Executes once when a contract is created to initialize state variables - * - * @param oracle - The FunctionsOracle contract - */ - // https://github.com/protofire/solhint/issues/242 - // solhint-disable-next-line no-empty-blocks - constructor(address oracle) FunctionsClient(oracle) ConfirmedOwner(msg.sender) {} - - /** - * @notice Send a simple request - * - * @param source JavaScript source code - * @param secrets Encrypted secrets payload - * @param args List of arguments accessible from within the source code - * @param subscriptionId Funtions billing subscription ID - * @param gasLimit Maximum amount of gas used to call the client contract's `handleOracleFulfillment` function - * @return Functions request ID - */ - function executeRequest( - string calldata source, - bytes calldata secrets, - string[] calldata args, - uint64 subscriptionId, - uint32 gasLimit - ) public onlyOwner returns (bytes32) { - Functions.Request memory req; - req.initializeRequest(Functions.Location.Inline, Functions.CodeLanguage.JavaScript, source); - if (secrets.length > 0) { - req.addRemoteSecrets(secrets); - } - if (args.length > 0) req.addArgs(args); - - bytes32 assignedReqID = sendRequest(req, subscriptionId, gasLimit); - latestRequestId = assignedReqID; - return assignedReqID; - } - - /** - * @notice Callback that is invoked once the DON has resolved the request or hit an error - * - * @param requestId The request ID, returned by sendRequest() - * @param response Aggregated response from the user code - * @param err Aggregated error from the user code or from the execution pipeline - * Either response or error parameter will be set, but never both - */ - function fulfillRequest(bytes32 requestId, bytes memory response, bytes memory err) internal override { - latestResponse = response; - latestError = err; - emit OCRResponse(requestId, response, err); - } - - /** - * @notice Allows the Functions oracle address to be updated - * - * @param oracle New oracle address - */ - function updateOracleAddress(address oracle) public onlyOwner { - setOracle(oracle); - } - - function addSimulatedRequestId(address oracleAddress, bytes32 requestId) public onlyOwner { - addExternalRequest(oracleAddress, requestId); - } -} diff --git a/contracts/contracts/test/MockFunctionsOracle.sol b/contracts/contracts/test/MockFunctionsOracle.sol index ea6e64b..8487d81 100644 --- a/contracts/contracts/test/MockFunctionsOracle.sol +++ b/contracts/contracts/test/MockFunctionsOracle.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.6; -import {FunctionsClientInterface} from "../dev/interfaces/FunctionsClientInterface.sol"; +import {IFunctionsClient} from "@chainlink/contracts/src/v0.8/functions/dev/v1_0_0/interfaces/IFunctionsClient.sol"; contract MockFunctionsOracle { function getRegistry() external view returns (address) { @@ -16,11 +16,11 @@ contract MockFunctionsOracle { return 0; } - function sendRequest(uint64, bytes calldata, uint32) external pure returns (bytes32) { + function sendRequest(uint64, bytes calldata, uint16, uint32, bytes32) external pure returns (bytes32) { return bytes32(0); } function fulfillRequest(address client, bytes32 requestId, bytes calldata data) external { - FunctionsClientInterface(client).handleOracleFulfillment(requestId, data, ""); + IFunctionsClient(client).handleOracleFulfillment(requestId, data, ""); } } diff --git a/contracts/contracts/test/MockResultsConsumer.sol b/contracts/contracts/test/MockResultsConsumer.sol index f4a6fe3..37e3bcc 100644 --- a/contracts/contracts/test/MockResultsConsumer.sol +++ b/contracts/contracts/test/MockResultsConsumer.sol @@ -6,11 +6,11 @@ import {ResultsConsumer} from "../ResultsConsumer.sol"; contract MockResultsConsumer is ResultsConsumer { constructor( address oracle, + bytes32 donId, uint64 subscriptionId, string memory source, - bytes memory secrets, - uint32 gasLimit - ) ResultsConsumer(oracle, subscriptionId, source, secrets, gasLimit) {} + bytes memory secrets + ) ResultsConsumer(oracle, donId, subscriptionId, source, secrets) {} function requestResult(uint256 sportId, uint256 externalId) external returns (bytes32 requestId) { requestId = _requestResult(sportId, externalId); diff --git a/contracts/hardhat.config.js b/contracts/hardhat.config.js index ac7ecc8..db10f52 100644 --- a/contracts/hardhat.config.js +++ b/contracts/hardhat.config.js @@ -20,6 +20,10 @@ module.exports = { defaultNetwork: "hardhat", solidity: { compilers: [ + { + version: "0.8.19", + settings: SOLC_SETTINGS, + }, { version: "0.8.7", settings: SOLC_SETTINGS, @@ -65,10 +69,6 @@ module.exports = { outputFile: "gas-report.txt", noColors: true, }, - contractSizer: { - runOnCompile: false, - only: ["FunctionsConsumer", "AutomatedFunctionsConsumer", "FunctionsBillingRegistry"], - }, paths: { sources: "./contracts", tests: "./test", diff --git a/contracts/networks.js b/contracts/networks.js index 6dd3b28..4604b4f 100644 --- a/contracts/networks.js +++ b/contracts/networks.js @@ -7,8 +7,6 @@ require("@chainlink/env-enc").config() const DEFAULT_VERIFICATION_BLOCK_CONFIRMATIONS = 2 -const SHARED_DON_PUBLIC_KEY = - "a30264e813edc9927f73e036b7885ee25445b836979cb00ef112bc644bd16de2db866fa74648438b34f52bb196ffa386992e94e0a3dc6913cee52e2e98f1619c" const npmCommand = process.env.npm_lifecycle_event const isTestEnvironment = npmCommand == "test" || npmCommand == "test:unit" @@ -30,9 +28,8 @@ const networks = { nativeCurrencySymbol: "ETH", linkToken: "0x779877A7B0D9E8603169DdbD7836e478b4624789", linkPriceFeed: "0x42585eD362B3f1BCa95c640FdFf35Ef899212734", - functionsOracleProxy: "0x649a2C205BE7A3d5e99206CEEFF30c794f0E31EC", - functionsBillingRegistryProxy: "0x3c79f56407DCB9dc9b852D139a317246f43750Cc", - functionsPublicKey: SHARED_DON_PUBLIC_KEY, + functionsRouter: "0xb83E47C2bC239B3bf370bc41e1459A34b41238D0", + functionsDonId: "fun-ethereum-sepolia-1", }, polygonMumbai: { url: process.env.POLYGON_MUMBAI_RPC_URL || "UNSET", @@ -44,9 +41,8 @@ const networks = { nativeCurrencySymbol: "MATIC", linkToken: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB", linkPriceFeed: "0x12162c3E810393dEC01362aBf156D7ecf6159528", // LINK/MATIC - functionsOracleProxy: "0xeA6721aC65BCeD841B8ec3fc5fEdeA6141a0aDE4", - functionsBillingRegistryProxy: "0xEe9Bf52E5Ea228404bB54BCFbbDa8c21131b9039", - functionsPublicKey: SHARED_DON_PUBLIC_KEY, + functionsRouter: "0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C", + functionsDonId: "fun-polygon-mumbai-1", ccipRouter: "0x70499c328e1E2a3c41108bd3730F6670a44595D1", ccipChainSelector: "12532609583862916517", ccipTestToken: "0xf1E3A5842EeEF51F2967b3F05D45DD4f4205FF40", @@ -64,9 +60,8 @@ const networks = { nativeCurrencySymbol: "AVAX", linkToken: "0x0b9d5D9136855f6FEc3c0993feE6E9CE8a297846", linkPriceFeed: "0x79c91fd4F8b3DaBEe17d286EB11cEE4D83521775", // LINK/AVAX - functionsOracleProxy: "0xE569061eD8244643169e81293b0aA0d3335fD563", - functionsBillingRegistryProxy: "0x452C33Cef9Bc773267Ac5F8D85c1Aca2bA4bcf0C", - functionsPublicKey: SHARED_DON_PUBLIC_KEY, + functionsRouter: "0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0", + functionsDonId: "fun-avalanche-fuji-1", ccipRouter: "0x554472a2720E5E7D5D3C817529aBA05EEd5F82D8", ccipChainSelector: "14767482510784806043", ccipTestToken: "0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4", @@ -78,5 +73,4 @@ const networks = { module.exports = { networks, - SHARED_DON_PUBLIC_KEY, } diff --git a/contracts/package-lock.json b/contracts/package-lock.json index efa6304..6dba6d6 100644 --- a/contracts/package-lock.json +++ b/contracts/package-lock.json @@ -21,8 +21,9 @@ "vm2": "^3.9.18" }, "devDependencies": { - "@chainlink/contracts": "0.5.1", + "@chainlink/contracts": "0.7.1", "@chainlink/contracts-ccip": "^0.7.6", + "@chainlink/functions-toolkit": "^0.2.8", "@ethersproject/abi": "^5.7.0", "@ethersproject/providers": "^5.7.1", "@nomicfoundation/hardhat-chai-matchers": "^1.0.3", @@ -96,13 +97,14 @@ } }, "node_modules/@chainlink/contracts": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.5.1.tgz", - "integrity": "sha512-3PDBJ38Sd6Ml9h7FNK/tZQti+kTCdXUq1qzE6E59CnlzycsV9ElPvf2hTvs9Mi9C6pEx2Mmw9yhZMfBktYUInQ==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@chainlink/contracts/-/contracts-0.7.1.tgz", + "integrity": "sha512-uVzjykXDgMhHcWIb18gd87ihHDRJIqa0q+Mu1b9e/npTUQAPpfM6ENMZMazzbNRaPZ5yNqGH8SE02Q3MQbFOLA==", "dev": true, "dependencies": { "@eth-optimism/contracts": "^0.5.21", - "@openzeppelin/contracts": "^4.3.3", + "@openzeppelin/contracts": "~4.3.3", + "@openzeppelin/contracts-upgradeable-4.7.3": "npm:@openzeppelin/contracts-upgradeable@v4.7.3", "@openzeppelin/contracts-v0.7": "npm:@openzeppelin/contracts@v3.4.2" } }, @@ -124,6 +126,12 @@ "integrity": "sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g==", "dev": true }, + "node_modules/@chainlink/contracts/node_modules/@openzeppelin/contracts": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.3.3.tgz", + "integrity": "sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g==", + "dev": true + }, "node_modules/@chainlink/env-enc": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@chainlink/env-enc/-/env-enc-1.0.5.tgz", @@ -135,6 +143,33 @@ "env-enc": "dist/cli.js" } }, + "node_modules/@chainlink/functions-toolkit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@chainlink/functions-toolkit/-/functions-toolkit-0.2.8.tgz", + "integrity": "sha512-n7WicB5N9m1jZAu5p8Bh66oMVRc04L0usrr7dbuHl8ONybzraEDWc6fopXJKKyF529NEGr4B4c9nYcD5ZZt0Dw==", + "dev": true, + "dependencies": { + "axios": "^1.4.0", + "bcrypto": "^5.4.0", + "cbor": "^9.0.1", + "eth-crypto": "^2.6.0", + "ethers": "^5.7.2", + "ganache": "^7.9.1", + "uniq": "^1.0.1" + } + }, + "node_modules/@chainlink/functions-toolkit/node_modules/cbor": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.2.tgz", + "integrity": "sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==", + "dev": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@chainsafe/as-sha256": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz", @@ -2579,11 +2614,11 @@ "dev": true }, "node_modules/axios": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.5.tgz", - "integrity": "sha512-glL/PvG/E+xCWwV8S6nCHcrfg1exGx7vxyUIivIA1iL7BIh6bePylCfVHwp6k13ao7SATxB6imau2kqY+I67kw==", + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -2643,6 +2678,29 @@ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, + "node_modules/bcrypto": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/bcrypto/-/bcrypto-5.5.2.tgz", + "integrity": "sha512-k3PF755oJM0+25iOVuraNedF5XneykxRwl+oBoMeQPfYee4qX8hHQhKCsNZWLthNYgi41GH2ysopd/8sDQDhEw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "bufio": "~1.0.7", + "loady": "~0.0.5" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bcrypto/node_modules/bufio": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", + "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/bech32": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", @@ -4874,9 +4932,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", "funding": [ { "type": "individual", @@ -5008,213 +5066,4123 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" + "node_modules/ganache": { + "version": "7.9.2", + "resolved": "https://registry.npmjs.org/ganache/-/ganache-7.9.2.tgz", + "integrity": "sha512-7gsVVDpO9AhrFyDMWWl7SpMsPpqGcnAzjxz3k32LheIPNd64p2XsY9GYRdhWmKuryb60W1iaWPZWDkFKlbRWHA==", + "bundleDependencies": [ + "@trufflesuite/bigint-buffer", + "keccak", + "leveldown", + "secp256k1" + ], + "dev": true, + "hasShrinkwrap": true, + "dependencies": { + "@trufflesuite/bigint-buffer": "1.1.10", + "@trufflesuite/uws-js-unofficial": "20.30.0-unofficial.0", + "@types/bn.js": "^5.1.0", + "@types/lru-cache": "5.1.1", + "@types/seedrandom": "3.0.1", + "abstract-level": "1.0.3", + "abstract-leveldown": "7.2.0", + "async-eventemitter": "0.2.4", + "emittery": "0.10.0", + "keccak": "3.0.2", + "leveldown": "6.1.0", + "secp256k1": "4.0.3" + }, + "bin": { + "ganache": "dist/node/cli.js", + "ganache-cli": "dist/node/cli.js" + }, + "optionalDependencies": { + "bufferutil": "4.0.5", + "utf-8-validate": "5.0.7" } }, - "node_modules/get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", - "dev": true, + "node_modules/ganache/node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "extraneous": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, "engines": { - "node": "*" + "node": ">=12" } }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "dev": true, + "node_modules/ganache/node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "extraneous": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/get-port": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", - "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", - "dev": true, + "node_modules/ganache/node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "extraneous": true, "engines": { - "node": ">=4" + "node": ">=10.0.0" } }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, + "node_modules/ganache/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "extraneous": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6.0.0" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" + "node_modules/ganache/node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "extraneous": true, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/ghost-testrpc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", - "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", - "dev": true, + "node_modules/ganache/node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "extraneous": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ganache/node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "extraneous": true, "dependencies": { - "chalk": "^2.4.2", - "node-emoji": "^1.10.0" + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/ganache/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.17", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", + "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "extraneous": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/ganache/node_modules/@microsoft/api-extractor": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.20.1.tgz", + "integrity": "sha512-T7cqcK+JpvHGOj7cD2ZCCWS7Xgru1uOqZwrV/FSUdyKVs5fopZcbBSuetwD/akst3O7Ypryg3UOLP54S/vnVmA==", + "extraneous": true, + "dependencies": { + "@microsoft/api-extractor-model": "7.16.0", + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "~0.15.2", + "@rushstack/node-core-library": "3.45.1", + "@rushstack/rig-package": "0.3.8", + "@rushstack/ts-command-line": "4.10.7", + "colors": "~1.2.1", + "lodash": "~4.17.15", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "source-map": "~0.6.1", + "typescript": "~4.5.2" }, "bin": { - "testrpc-sc": "index.js" + "api-extractor": "bin/api-extractor" } }, - "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, + "node_modules/ganache/node_modules/@microsoft/api-extractor-model": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.16.0.tgz", + "integrity": "sha512-0FOrbNIny8mzBrzQnSIkEjAXk0JMSnPmWYxt3ZDTPVg9S8xIPzB6lfgTg9+Mimu0RKCpGKBpd+v2WcR5vGzyUQ==", + "extraneous": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "@microsoft/tsdoc": "0.13.2", + "@microsoft/tsdoc-config": "~0.15.2", + "@rushstack/node-core-library": "3.45.1" + } + }, + "node_modules/ganache/node_modules/@microsoft/api-extractor/node_modules/typescript": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", + "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "extraneous": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4.2.0" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, + "node_modules/ganache/node_modules/@microsoft/tsdoc": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz", + "integrity": "sha512-WrHvO8PDL8wd8T2+zBGKrMwVL5IyzR3ryWUsl0PXgEV0QHup4mTLi0QcATefGI6Gx9Anu7vthPyyyLpY0EpiQg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@microsoft/tsdoc-config": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.15.2.tgz", + "integrity": "sha512-mK19b2wJHSdNf8znXSMYVShAHktVr/ib0Ck2FA3lsVBSEhSI/TfXT7DJQkAYgcztTuwazGcg58ZjYdk0hTCVrA==", + "extraneous": true, "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "@microsoft/tsdoc": "0.13.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" } }, - "node_modules/global-modules": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", - "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", - "dev": true, + "node_modules/ganache/node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "extraneous": true, "dependencies": { - "global-prefix": "^3.0.0" - }, - "engines": { - "node": ">=6" + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" } }, - "node_modules/global-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", - "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "node_modules/ganache/node_modules/@rushstack/node-core-library": { + "version": "3.45.1", + "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.45.1.tgz", + "integrity": "sha512-BwdssTNe007DNjDBxJgInHg8ePytIPyT0La7ZZSQZF9+rSkT42AygXPGvbGsyFfEntjr4X37zZSJI7yGzL16cQ==", + "extraneous": true, + "dependencies": { + "@types/node": "12.20.24", + "colors": "~1.2.1", + "fs-extra": "~7.0.1", + "import-lazy": "~4.0.0", + "jju": "~1.4.0", + "resolve": "~1.17.0", + "semver": "~7.3.0", + "timsort": "~0.3.0", + "z-schema": "~5.0.2" + } + }, + "node_modules/ganache/node_modules/@rushstack/node-core-library/node_modules/@types/node": { + "version": "12.20.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.24.tgz", + "integrity": "sha512-yxDeaQIAJlMav7fH5AQqPH1u8YIuhYJXYBzxaQ4PifsU0GDO38MSdmEDeRlIxrKbC6NbEaaEHDanWb+y30U8SQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@rushstack/rig-package": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.3.8.tgz", + "integrity": "sha512-MDWg1xovea99PWloSiYMjFcCLsrdjFtYt6aOyHNs5ojn5mxrzR6U9F83hvbQjTWnKPMvZtr0vcek+4n+OQOp3Q==", + "extraneous": true, + "dependencies": { + "resolve": "~1.17.0", + "strip-json-comments": "~3.1.1" + } + }, + "node_modules/ganache/node_modules/@rushstack/ts-command-line": { + "version": "4.10.7", + "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.10.7.tgz", + "integrity": "sha512-CjS+DfNXUSO5Ab2wD1GBGtUTnB02OglRWGqfaTcac9Jn45V5MeUOsq/wA8wEeS5Y/3TZ2P1k+IWdVDiuOFP9Og==", + "extraneous": true, + "dependencies": { + "@types/argparse": "1.0.38", + "argparse": "~1.0.9", + "colors": "~1.2.1", + "string-argv": "~0.3.1" + } + }, + "node_modules/ganache/node_modules/@trufflesuite/bigint-buffer": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@trufflesuite/bigint-buffer/-/bigint-buffer-1.1.10.tgz", + "integrity": "sha512-pYIQC5EcMmID74t26GCC67946mgTJFiLXOT/BYozgrd4UEY2JHEGLhWi9cMiQCt5BSqFEvKkCHNnoj82SRjiEw==", "dev": true, + "hasInstallScript": true, + "inBundle": true, + "license": "Apache-2.0", "dependencies": { - "ini": "^1.3.5", - "kind-of": "^6.0.2", - "which": "^1.3.1" + "node-gyp-build": "4.4.0" }, "engines": { - "node": ">=6" + "node": ">= 14.0.0" } }, - "node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/ganache/node_modules/@trufflesuite/bigint-buffer/node_modules/node-gyp-build": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.4.0.tgz", + "integrity": "sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ==", "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, + "inBundle": true, + "license": "MIT", "bin": { - "which": "bin/which" + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/ganache/node_modules/@trufflesuite/uws-js-unofficial": { + "version": "20.30.0-unofficial.0", + "resolved": "https://registry.npmjs.org/@trufflesuite/uws-js-unofficial/-/uws-js-unofficial-20.30.0-unofficial.0.tgz", + "integrity": "sha512-r5X0aOQcuT6pLwTRLD+mPnAM/nlKtvIK4Z+My++A8tTOR0qTjNRx8UB8jzRj3D+p9PMAp5LnpCUUGmz7/TppwA==", "dev": true, "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" + "ws": "8.13.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "bufferutil": "4.0.7", + "utf-8-validate": "6.0.3" } }, - "node_modules/globby": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", - "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "node_modules/ganache/node_modules/@trufflesuite/uws-js-unofficial/node_modules/bufferutil": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", + "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", "dev": true, + "hasInstallScript": true, + "optional": true, "dependencies": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" + "node-gyp-build": "^4.3.0" }, "engines": { - "node": ">=8" + "node": ">=6.14.2" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/ganache/node_modules/@trufflesuite/uws-js-unofficial/node_modules/utf-8-validate": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz", + "integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==", "dev": true, + "hasInstallScript": true, + "optional": true, "dependencies": { - "get-intrinsic": "^1.1.3" + "node-gyp-build": "^4.3.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6.14.2" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "node_modules/ganache/node_modules/@trufflesuite/uws-js-unofficial/node_modules/ws": { + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", "dev": true, "engines": { - "node": ">=4.x" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/ganache/node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/abstract-leveldown": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz", + "integrity": "sha512-q5veSX6zjUy/DlDhR4Y4cU0k2Ar+DT2LUraP00T19WLmTO6Se1djepCCaqU6nQrwcJ5Hyo/CWqxTzrrFg8eqbQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/argparse": { + "version": "1.0.38", + "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", + "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/bn.js": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.0.tgz", + "integrity": "sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ganache/node_modules/@types/eslint": { + "version": "8.4.10", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", + "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "extraneous": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/ganache/node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "extraneous": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/ganache/node_modules/@types/estree": { + "version": "0.0.50", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz", + "integrity": "sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==", + "dev": true + }, + "node_modules/ganache/node_modules/@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@types/node": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.0.tgz", + "integrity": "sha512-eMhwJXc931Ihh4tkU+Y7GiLzT/y/DBNpNtr4yU9O2w3SYBsr9NaOPhQlLKRmoWtI54uNwuo0IOUFQjVOTZYRvw==", + "dev": true + }, + "node_modules/ganache/node_modules/@types/seedrandom": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/seedrandom/-/seedrandom-3.0.1.tgz", + "integrity": "sha512-giB9gzDeiCeloIXDgzFBCgjj1k4WxcDrZtGl6h1IqmUPlxF+Nx8Ve+96QCyDZ/HseB/uvDsKbpib9hU5cU53pw==", + "dev": true + }, + "node_modules/ganache/node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "extraneous": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "extraneous": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/ganache/node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "extraneous": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/ganache/node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "extraneous": true, + "dependencies": { + "envinfo": "^7.7.3" + } + }, + "node_modules/ganache/node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/abstract-level": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", + "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/abstract-level/node_modules/level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/abstract-leveldown": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-7.2.0.tgz", + "integrity": "sha512-DnhQwcFEaYsvYDnACLZhMmCWd3rkOeEvglpa4q5i/5Jlm3UIsWaxVzuXvDLFCSCWRO3yy2/+V/G7FusFgejnfQ==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.0.0", + "is-buffer": "^2.0.5", + "level-concat-iterator": "^3.0.0", + "level-supports": "^2.0.1", + "queue-microtask": "^1.2.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "extraneous": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ganache/node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "extraneous": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ganache/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "extraneous": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/ganache/node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "extraneous": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "extraneous": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ganache/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "extraneous": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/ganache/node_modules/asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ganache/node_modules/asn1.js/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/assert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.0.0.tgz", + "integrity": "sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==", + "extraneous": true, + "dependencies": { + "es6-object-assign": "^1.1.0", + "is-nan": "^1.2.1", + "object-is": "^1.0.1", + "util": "^0.12.0" + } + }, + "node_modules/ganache/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/ganache/node_modules/async-eventemitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", + "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", + "dev": true, + "dependencies": { + "async": "^2.4.0" + } + }, + "node_modules/ganache/node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "extraneous": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "extraneous": true, + "engines": { + "node": "*" + } + }, + "node_modules/ganache/node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "extraneous": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/ganache/node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "extraneous": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "extraneous": true, + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ganache/node_modules/browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "extraneous": true, + "dependencies": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "node_modules/ganache/node_modules/browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "extraneous": true, + "dependencies": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/ganache/node_modules/browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "extraneous": true, + "dependencies": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "node_modules/ganache/node_modules/browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "extraneous": true, + "dependencies": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "node_modules/ganache/node_modules/browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "extraneous": true, + "dependencies": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/ganache/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/ganache/node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/bufferutil": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", + "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/ganache/node_modules/builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "extraneous": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "node_modules/ganache/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/caniuse-lite": { + "version": "1.0.30001435", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001435.tgz", + "integrity": "sha512-kdCkUTjR+v4YAJelyiDTqiu82BDr4W4CP5sgTA0ZBmqn30XfS2ZghPLMowik9TPhS+psWJiUNxsqLyurDbmutA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "extraneous": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "extraneous": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/chokidar": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "extraneous": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ganache/node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "extraneous": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ganache/node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/ganache/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "extraneous": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/ganache/node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "extraneous": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "extraneous": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ganache/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/colors": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.5.tgz", + "integrity": "sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg==", + "extraneous": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/ganache/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "node_modules/ganache/node_modules/create-ecdh/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "extraneous": true, + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/ganache/node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "extraneous": true, + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/ganache/node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "extraneous": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/ganache/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "extraneous": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ganache/node_modules/crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "extraneous": true, + "dependencies": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ganache/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "extraneous": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/ganache/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "extraneous": true + }, + "node_modules/ganache/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "extraneous": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/ganache/node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "extraneous": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ganache/node_modules/diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "node_modules/ganache/node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/ganache/node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/emittery": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.0.tgz", + "integrity": "sha512-AGvFfs+d0JKCJQ4o01ASQLGPmSCxgfU9RFXvzPvZdjKK8oscynksuJhWrSTSw7j7Ep/sZct5b5ZhYCi8S/t0HQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "extraneous": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ganache/node_modules/enhanced-resolve": { + "version": "5.12.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", + "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "extraneous": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "extraneous": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ganache/node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/es6-object-assign": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", + "integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "extraneous": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ganache/node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "extraneous": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ganache/node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "extraneous": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ganache/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "extraneous": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/ganache/node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "extraneous": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/ganache/node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "extraneous": true, + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ganache/node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "extraneous": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "extraneous": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/ganache/node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "extraneous": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "extraneous": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "extraneous": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/ganache/node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "extraneous": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/ganache/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "extraneous": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/ganache/node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "extraneous": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/ganache/node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "extraneous": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/ganache/node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "extraneous": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "node_modules/ganache/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "extraneous": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ganache/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "extraneous": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ganache/node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "extraneous": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + } + }, + "node_modules/ganache/node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "extraneous": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/ganache/node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "extraneous": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/ganache/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "extraneous": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + } + }, + "node_modules/ganache/node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "extraneous": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "extraneous": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ganache/node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/ganache/node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "extraneous": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/ganache/node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/ganache/node_modules/https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "extraneous": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ganache/node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/ganache/node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "extraneous": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "extraneous": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/ganache/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/ganache/node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "extraneous": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "extraneous": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "extraneous": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/ganache/node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "extraneous": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "extraneous": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/ganache/node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "extraneous": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "extraneous": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", + "extraneous": true, + "dependencies": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "extraneous": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/ganache/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "extraneous": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "extraneous": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "extraneous": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/ganache/node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "extraneous": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/ganache/node_modules/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "extraneous": true + }, + "node_modules/ganache/node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "extraneous": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "extraneous": true, + "dependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/ganache/node_modules/keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "dev": true, + "hasInstallScript": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ganache/node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/level-concat-iterator": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/level-concat-iterator/-/level-concat-iterator-3.1.0.tgz", + "integrity": "sha512-BWRCMHBxbIqPxJ8vHOvKUsaO0v1sLYZtjN3K2iZJsRBYtp+ONsY6Jfi6hy9K3+zolgQRryhIn2NRZjZnWJ9NmQ==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "catering": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/level-js": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/level-js/-/level-js-6.1.0.tgz", + "integrity": "sha512-i7mPtkZm68aewfv0FnIUWvFUFfoyzIvVKnUmuQGrelEkP72vSPTaA1SGneWWoCV5KZJG4wlzbJLp1WxVNGuc6A==", + "extraneous": true, + "dependencies": { + "abstract-leveldown": "^7.2.0", + "buffer": "^6.0.3", + "inherits": "^2.0.3", + "ltgt": "^2.1.2", + "run-parallel-limit": "^1.1.0" + } + }, + "node_modules/ganache/node_modules/level-supports": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-2.1.0.tgz", + "integrity": "sha512-E486g1NCjW5cF78KGPrMDRBYzPuueMZ6VBXHT6gC7A8UYWGiM14fGgp+s/L1oFfDWSPV/+SFkYCmZ0SiESkRKA==", + "dev": true, + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/level-transcoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "dev": true, + "dependencies": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/ganache/node_modules/leveldown": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/leveldown/-/leveldown-6.1.0.tgz", + "integrity": "sha512-8C7oJDT44JXxh04aSSsfcMI8YiaGRhOFI9/pMEL7nWJLVsWajDPTRxsSHTM2WcTVY5nXM+SuRHzPPi0GbnDX+w==", + "dev": true, + "hasInstallScript": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "abstract-leveldown": "^7.2.0", + "napi-macros": "~2.0.0", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/ganache/node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "extraneous": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/ganache/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "extraneous": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/ganache/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "extraneous": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/ganache/node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "extraneous": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "extraneous": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/mcl-wasm": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.9.0.tgz", + "integrity": "sha512-rvU7L/68ZrDk4dpPZteiEqvK9nB/1XbbHmuLK6qIvc4xuuJb/iv1p5X3KEyq6AYatLnc+zbdSlLXTlKgTnCRZQ==", + "extraneous": true, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/ganache/node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "extraneous": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/ganache/node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "extraneous": true + }, + "node_modules/ganache/node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "extraneous": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ganache/node_modules/miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "bin": { + "miller-rabin": "bin/miller-rabin" + } + }, + "node_modules/ganache/node_modules/miller-rabin/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "extraneous": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ganache/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "extraneous": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ganache/node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true, + "inBundle": true, + "license": "ISC" + }, + "node_modules/ganache/node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "extraneous": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ganache/node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "extraneous": true + }, + "node_modules/ganache/node_modules/mocha": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz", + "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==", + "extraneous": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.2", + "debug": "4.3.2", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.7", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.25", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.1.5", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/ganache/node_modules/module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/nanoid": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz", + "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==", + "extraneous": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/ganache/node_modules/napi-macros": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.0.0.tgz", + "integrity": "sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "dev": true, + "inBundle": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/ganache/node_modules/node-loader": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/node-loader/-/node-loader-1.0.2.tgz", + "integrity": "sha512-myxAxpyMR7knjA4Uzwf3gjxaMtxSWj2vpm9o6AYWWxQ1S3XMBNeG2vzYcp/5eW03cBGfgSxyP+wntP8qhBJNhQ==", + "extraneous": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/ganache/node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "extraneous": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "extraneous": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "extraneous": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "extraneous": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/ganache/node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "extraneous": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "extraneous": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "extraneous": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "extraneous": true, + "dependencies": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/ganache/node_modules/path-browserify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", + "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", + "extraneous": true + }, + "node_modules/ganache/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "extraneous": true, + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/ganache/node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "extraneous": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ganache/node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "extraneous": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "extraneous": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "extraneous": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "extraneous": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "extraneous": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "extraneous": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/ganache/node_modules/public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "extraneous": true, + "dependencies": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/ganache/node_modules/public-encrypt/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "extraneous": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/ganache/node_modules/randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "extraneous": true, + "dependencies": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "node_modules/ganache/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ganache/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "extraneous": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/ganache/node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "extraneous": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "extraneous": true, + "dependencies": { + "path-parse": "^1.0.6" + } + }, + "node_modules/ganache/node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "extraneous": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "extraneous": true, + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/ganache/node_modules/run-parallel-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", + "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", + "extraneous": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/ganache/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "extraneous": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/ganache/node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "dev": true, + "hasInstallScript": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ganache/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "extraneous": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "extraneous": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/ganache/node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/ganache/node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "extraneous": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "extraneous": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/shebang-loader": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/shebang-loader/-/shebang-loader-0.0.1.tgz", + "integrity": "sha512-nQvhUHvKyzGK5aqPxHfHB5nlAN2EZ2U61S2G0YrxAuCRU5iGhFcxxRiaAdb18UoRS1zVMhRz4gdQ1xFEg3AOyA==", + "extraneous": true + }, + "node_modules/ganache/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "extraneous": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/ganache/node_modules/shx": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.3.tgz", + "integrity": "sha512-nZJ3HFWVoTSyyB+evEKjJ1STiixGztlqwKLTUNV5KqMWtGey9fTd4KU1gdZ1X9BV6215pswQ/Jew9NsuS/fNDA==", + "extraneous": true, + "dependencies": { + "minimist": "^1.2.3", + "shelljs": "^0.8.4" + }, + "bin": { + "shx": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "extraneous": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ganache/node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "extraneous": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/ganache/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "extraneous": true + }, + "node_modules/ganache/node_modules/stream-browserify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", + "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", + "extraneous": true, + "dependencies": { + "inherits": "~2.0.4", + "readable-stream": "^3.5.0" + } + }, + "node_modules/ganache/node_modules/stream-http": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-3.2.0.tgz", + "integrity": "sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==", + "extraneous": true, + "dependencies": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "xtend": "^4.0.2" + } + }, + "node_modules/ganache/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "inBundle": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/ganache/node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "extraneous": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/ganache/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "extraneous": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "extraneous": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "extraneous": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ganache/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "extraneous": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/terser": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", + "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "extraneous": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/terser-webpack-plugin": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.5.tgz", + "integrity": "sha512-3luOVHku5l0QBeYS8r4CdHYWEGMmIj3H1U64jgkdZzECcSOJAyJ9TjuqcQZvw1Y+4AOBN9SeYJPJmFn2cM4/2g==", + "extraneous": true, + "dependencies": { + "jest-worker": "^27.0.6", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "source-map": "^0.6.1", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/ganache/node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "extraneous": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ganache/node_modules/ts-loader": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.1.tgz", + "integrity": "sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw==", + "extraneous": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ganache/node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "extraneous": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + } + }, + "node_modules/ganache/node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "extraneous": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ganache/node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "extraneous": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ganache/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "extraneous": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/ganache/node_modules/update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "extraneous": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + } + }, + "node_modules/ganache/node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "extraneous": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/ganache/node_modules/utf-8-validate": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", + "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "node_modules/ganache/node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "extraneous": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/ganache/node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true, + "inBundle": true, + "license": "MIT" + }, + "node_modules/ganache/node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "extraneous": true + }, + "node_modules/ganache/node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "extraneous": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "extraneous": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/webpack": { + "version": "5.65.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.65.0.tgz", + "integrity": "sha512-Q5or2o6EKs7+oKmJo7LaqZaMOlDWQse9Tm5l1WAfU/ujLGN5Pb0SqGeVkN/4bpPmEqEP5RnVhiqsOtWtUVwGRw==", + "extraneous": true, + "dependencies": { + "@types/eslint-scope": "^3.7.0", + "@types/estree": "^0.0.50", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.4.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.8.3", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.4", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.3.1", + "webpack-sources": "^3.2.2" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/webpack-cli": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.1.tgz", + "integrity": "sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==", + "extraneous": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.1.0", + "@webpack-cli/info": "^1.4.0", + "@webpack-cli/serve": "^1.6.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "execa": "^5.0.0", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/webpack-cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "extraneous": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/ganache/node_modules/webpack-cli/node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "extraneous": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/webpack-cli/node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "extraneous": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/ganache/node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "extraneous": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ganache/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "extraneous": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ganache/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "extraneous": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ganache/node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "extraneous": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ganache/node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/workerpool": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", + "extraneous": true + }, + "node_modules/ganache/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "extraneous": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "extraneous": true + }, + "node_modules/ganache/node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "extraneous": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/ganache/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "extraneous": true + }, + "node_modules/ganache/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "extraneous": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "extraneous": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "extraneous": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ganache/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "extraneous": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ganache/node_modules/z-schema": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.4.tgz", + "integrity": "sha512-gm/lx3hDzJNcLwseIeQVm1UcwhWIKpSB4NqH89pTBtFns4k/HDHudsICtvG05Bvw/Mv3jMyk700y5dadueLHdA==", + "extraneous": true, + "dependencies": { + "commander": "^2.20.3", + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/ghost-testrpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", + "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2", + "node-emoji": "^1.10.0" + }, + "bin": { + "testrpc-sc": "index.js" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" } }, "node_modules/handlebars": { @@ -6321,6 +10289,15 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, + "node_modules/loady": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/loady/-/loady-0.0.5.tgz", + "integrity": "sha512-uxKD2HIj042/HBx77NBcmEPsD+hxCgAtjEWlYNScuUjIsh/62Uyu39GOR68TBR68v+jqDL9zfftCWoUo4y03sQ==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -9860,6 +13837,12 @@ "node": ">=12.18" } }, + "node_modules/uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", + "integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==", + "dev": true + }, "node_modules/universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", diff --git a/contracts/package.json b/contracts/package.json index 258ff5c..e2257e5 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -5,11 +5,7 @@ "description": "Tooling for interacting with Chainlink Functions", "scripts": { "compile": "hardhat compile", - "test": "npm run test:unit", - "test:unit": "hardhat test test/unit/*.spec.js", - "test:integration": "hardhat test test/integration/*.spec.js", - "functions-simulate-javascript": "node scripts/simulateFunctionsJavaScript.js", - "functions-gen-keys": "node scripts/generateKeypair.js", + "test": "hardhat test", "lint": "npm run lint:contracts && npm run format:check", "lint:fix": "solhint 'contracts/**/*.sol' --fix", "lint:contracts": "solhint 'contracts/*.sol'", @@ -18,8 +14,9 @@ "format:fix": "prettier --write ." }, "devDependencies": { - "@chainlink/contracts": "0.5.1", + "@chainlink/contracts": "0.7.1", "@chainlink/contracts-ccip": "^0.7.6", + "@chainlink/functions-toolkit": "^0.2.8", "@ethersproject/abi": "^5.7.0", "@ethersproject/providers": "^5.7.1", "@nomicfoundation/hardhat-chai-matchers": "^1.0.3", diff --git a/contracts/scripts/generateKeypair.js b/contracts/scripts/generateKeypair.js deleted file mode 100644 index 55a9b81..0000000 --- a/contracts/scripts/generateKeypair.js +++ /dev/null @@ -1,4 +0,0 @@ -const ethCrypto = require("eth-crypto") - -const { publicKey, privateKey } = ethCrypto.createIdentity() -console.log(`\nNewly generated Keys\nPublic Key: ${publicKey}\nPrivate Key: ${privateKey}`) diff --git a/contracts/scripts/simulateFunctionsJavaScript.js b/contracts/scripts/simulateFunctionsJavaScript.js deleted file mode 100644 index 0f310da..0000000 --- a/contracts/scripts/simulateFunctionsJavaScript.js +++ /dev/null @@ -1,39 +0,0 @@ -const { simulateRequest, getDecodedResultLog, getRequestConfig } = require("../FunctionsSandboxLibrary") - -const runSimulation = async (requestConfig) => { - const { resultLog, result, success } = await simulateRequest(requestConfig) - - console.log(`\n${resultLog}`) - if (success) { - console.log(`Value returned from source code: ${result}\n${getDecodedResultLog(requestConfig, result)}`) - return - } -} - -;(async () => { - const unvalidatedRequestConfig = require("../Functions-request-config.js") - const requestConfig = getRequestConfig(unvalidatedRequestConfig) - - if (requestConfig.secretsLocation === 1) { - requestConfig.secrets = {} - if (requestConfig.globalSecrets && Object.keys(requestConfig.globalSecrets).length !== 0) { - requestConfig.secrets = requestConfig.globalSecrets - console.log("\n__SIMULATING JAVASCRIPT WITH DEFAULT SECRETS__") - await runSimulation(requestConfig) - } - - if (!requestConfig.perNodeSecrets) { - return - } - - for (let i = 0; i < requestConfig.perNodeSecrets.length; i++) { - requestConfig.secrets = requestConfig.perNodeSecrets[i] - console.log(`\n__SIMULATING JAVASCRIPT WITH SECRETS ASSIGNED TO NODE ${i}__`) - await runSimulation(requestConfig) - } - return - } - - console.log("\n__Simulating JavaScript__") - await runSimulation(requestConfig) -})() diff --git a/contracts/tasks/Functions-billing/accept.js b/contracts/tasks/Functions-billing/accept.js deleted file mode 100644 index 7198bed..0000000 --- a/contracts/tasks/Functions-billing/accept.js +++ /dev/null @@ -1,54 +0,0 @@ -const { networks } = require("../../networks") - -task("functions-sub-accept", "Accepts ownership of an Functions subscription after a transfer is requested") - .addParam("subid", "Subscription ID") - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error("This command cannot be used on a local hardhat chain. Specify a valid network.") - } - - const subscriptionId = taskArgs.subid - - const RegistryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // Check that the subscription is valid - let preSubInfo - try { - preSubInfo = await registry.getSubscription(subscriptionId) - } catch (error) { - if (error.errorName === "InvalidSubscription") { - throw Error(`Subscription ID "${subscriptionId}" is invalid or does not exist`) - } - throw error - } - - // Accept subscription ownership (only works if a transfer has been requested by the previous owner) - try { - console.log(`Accepting ownership of subscription ${subscriptionId}`) - const acceptTx = await registry.acceptSubscriptionOwnerTransfer(subscriptionId) - console.log( - `Waiting ${networks[network.name].confirmations} blocks for transaction ${acceptTx.hash} to be confirmed...` - ) - await acceptTx.wait(networks[network.name].confirmations) - } catch (error) { - console.log( - `\nFailed to accept ownership. Ensure that a transfer has been requested by the previous owner ${preSubInfo[1]}` - ) - throw error - } - - const signerAddr = (await ethers.getSigners())[0].address - - console.log(`Ownership of subscription ${subscriptionId} transferred to ${signerAddr}`) - - // Print information about the accepted subscription - let postSubInfo = await registry.getSubscription(subscriptionId) - - console.log(`\nSubscription ${subscriptionId} owner: ${postSubInfo[1]}`) - console.log(`Balance: ${ethers.utils.formatEther(postSubInfo[0])} LINK`) - console.log(`${postSubInfo[2].length} authorized consumer contract${postSubInfo[2].length === 1 ? "" : "s"}:`) - console.log(postSubInfo[2]) - }) diff --git a/contracts/tasks/Functions-billing/add.js b/contracts/tasks/Functions-billing/add.js deleted file mode 100644 index 8443684..0000000 --- a/contracts/tasks/Functions-billing/add.js +++ /dev/null @@ -1,62 +0,0 @@ -const { networks } = require("../../networks") - -task("functions-sub-add", "Adds a client contract to the Functions billing subscription") - .addParam("subid", "Subscription ID") - .addParam("contract", "Address of the Functions client contract to authorize for billing") - .setAction(async (taskArgs) => { - if (network.name == "hardhat") { - throw Error("This command cannot be used on a local hardhat chain. Specify a valid network.") - } - - const subscriptionId = taskArgs.subid - const consumer = taskArgs.contract - - await addClientConsumerToSubscription(subscriptionId, consumer) - }) - -const addClientConsumerToSubscription = async (subscriptionId, consumer) => { - const RegistryFactory = await ethers.getContractFactory("FunctionsBillingRegistry") - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // Check that the subscription is valid - let preSubInfo - try { - preSubInfo = await registry.getSubscription(subscriptionId) - } catch (error) { - if (error.errorName === "InvalidSubscription") { - throw Error(`Subscription ID "${subscriptionId}" is invalid or does not exist`) - } - throw error - } - - // Check that the requesting wallet is the owner of the subscription - const accounts = await ethers.getSigners() - const signer = accounts[0] - if (preSubInfo[1] !== signer.address) { - throw Error("The current wallet is not the owner of the subscription") - } - - // Check that the consumer is not already authorized (for convenience) - const existingConsumers = preSubInfo[2].map((addr) => addr.toLowerCase()) - if (existingConsumers.includes(consumer.toLowerCase())) { - throw Error(`Consumer ${consumer} is already authorized to use subscription ${subscriptionId}`) - } - - console.log(`Adding consumer contract address ${consumer} to subscription ${subscriptionId}`) - const addTx = await registry.addConsumer(subscriptionId, consumer) - - console.log(`Waiting ${networks[network.name].confirmations} blocks for transaction ${addTx.hash} to be confirmed...`) - await addTx.wait(networks[network.name].confirmations) - console.log(`\nAdded consumer contract address ${consumer} to subscription ${subscriptionId}`) - - // Print information about the subscription - const postSubInfo = await registry.getSubscription(subscriptionId) - console.log( - `${postSubInfo[2].length} authorized consumer contract${ - postSubInfo[2].length === 1 ? "" : "s" - } for subscription ${subscriptionId}:` - ) - console.log(postSubInfo[2]) -} - -module.exports.addClientConsumerToSubscription = addClientConsumerToSubscription diff --git a/contracts/tasks/Functions-billing/cancel.js b/contracts/tasks/Functions-billing/cancel.js deleted file mode 100644 index 52fd5ae..0000000 --- a/contracts/tasks/Functions-billing/cancel.js +++ /dev/null @@ -1,55 +0,0 @@ -const { networks } = require("../../networks") - -task( - "functions-sub-cancel", - "Cancels Functions billing subscription and refunds unused balance. Cancellation is only possible if there are no pending requests" -) - .addParam("subid", "Subscription ID to cancel") - .addOptionalParam( - "refundaddress", - "Address where the remaining subscription balance is sent (defaults to caller's address)" - ) - .setAction(async (taskArgs) => { - if (network.name == "hardhat") { - throw Error("This command cannot be used on a local hardhat chain. Specify a valid network.") - } - - const subscriptionId = taskArgs.subid - const refundAddress = taskArgs.refundAddress ?? (await ethers.getSigners())[0].address - - const RegistryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // Check that the subscription is valid - let preSubInfo - try { - preSubInfo = await registry.getSubscription(subscriptionId) - } catch (error) { - if (error.errorName === "InvalidSubscription") { - throw Error(`Subscription ID "${subscriptionId}" is invalid or does not exist`) - } - console.log("Cancellation failed. Ensure there are no pending requests or requests which must be timed out.") - throw error - } - - // Check that the requesting wallet is the owner of the subscription - const accounts = await ethers.getSigners() - const signer = accounts[0] - if (preSubInfo[1] !== signer.address) { - throw Error("The current wallet is not the owner of the subscription") - } - - // TODO: This script should check for any pending requests and return an error. - // It should also time out any expired pending requests automatically. - - console.log(`Canceling subscription ${subscriptionId}`) - const cancelTx = await registry.cancelSubscription(subscriptionId, refundAddress) - - console.log( - `Waiting ${networks[network.name].confirmations} blocks for transaction ${cancelTx.hash} to be confirmed...` - ) - await cancelTx.wait(networks[network.name].confirmations) - console.log(`\nSubscription ${subscriptionId} cancelled.`) - }) diff --git a/contracts/tasks/Functions-billing/create.js b/contracts/tasks/Functions-billing/create.js deleted file mode 100644 index ec3a959..0000000 --- a/contracts/tasks/Functions-billing/create.js +++ /dev/null @@ -1,97 +0,0 @@ -const { networks } = require("../../networks") - -task("functions-sub-create", "Creates a new billing subscription for Functions consumer contracts") - .addOptionalParam("amount", "Initial amount used to fund the subscription in LINK") - .addOptionalParam("contract", "Address of the client contract address authorized to use the new billing subscription") - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Specify a valid network or simulate a request locally with "npx hardhat functions-simulate".' - ) - } - - const linkAmount = taskArgs.amount - const consumer = taskArgs.contract - - const RegistryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // TODO: Remove the following 6 lines on open access - const Oracle = await ethers.getContractFactory("contracts/dev/functions/FunctionsOracle.sol:FunctionsOracle") - const oracle = await Oracle.attach(networks[network.name]["functionsOracleProxy"]) - const isWalletAllowed = await oracle.isAuthorizedSender((await ethers.getSigner()).address) - - if (!isWalletAllowed) - return console.log( - "\nChainlink Functions is currently in a closed testing phase.\nFor access sign up here:\nhttps://functions.chain.link" - ) - - console.log("Creating Functions billing subscription") - const createSubscriptionTx = await registry.createSubscription() - - // If a consumer or linkAmount was also specified, wait 1 block instead of networks[network.name].confirmations blocks - const createWaitBlockConfirmations = consumer || linkAmount ? 1 : networks[network.name].confirmations - console.log( - `Waiting ${createWaitBlockConfirmations} blocks for transaction ${createSubscriptionTx.hash} to be confirmed...` - ) - const createSubscriptionReceipt = await createSubscriptionTx.wait(createWaitBlockConfirmations) - - const subscriptionId = createSubscriptionReceipt.events[0].args["subscriptionId"].toNumber() - - console.log(`Subscription created with ID: ${subscriptionId}`) - - if (linkAmount) { - // Fund subscription - const juelsAmount = ethers.utils.parseUnits(linkAmount) - - const LinkTokenFactory = await ethers.getContractFactory("LinkToken") - const linkToken = await LinkTokenFactory.attach(networks[network.name]["linkToken"]) - - const accounts = await ethers.getSigners() - const signer = accounts[0] - - // Check for a sufficent LINK balance to fund the subscription - const balance = await linkToken.balanceOf(signer.address) - if (juelsAmount.gt(balance)) { - throw Error( - `Insufficent LINK balance. Trying to fund subscription with ${ethers.utils.formatEther( - juelsAmount - )} LINK, but only have ${ethers.utils.formatEther(balance)}.` - ) - } - - console.log(`Funding with ${ethers.utils.formatEther(juelsAmount)} LINK`) - const fundTx = await linkToken.transferAndCall( - networks[network.name]["functionsBillingRegistryProxy"], - juelsAmount, - ethers.utils.defaultAbiCoder.encode(["uint64"], [subscriptionId]) - ) - // If a consumer was also specified, wait 1 block instead of networks[network.name].confirmations blocks - const fundWaitBlockConfirmations = !!consumer ? 1 : networks[network.name].confirmations - console.log(`Waiting ${fundWaitBlockConfirmations} blocks for transaction ${fundTx.hash} to be confirmed...`) - await fundTx.wait(fundWaitBlockConfirmations) - - console.log(`Subscription ${subscriptionId} funded with ${ethers.utils.formatEther(juelsAmount)} LINK`) - } - - if (consumer) { - // Add consumer - console.log(`Adding consumer contract address ${consumer} to subscription ${subscriptionId}`) - const addTx = await registry.addConsumer(subscriptionId, consumer) - console.log( - `Waiting ${networks[network.name].confirmations} blocks for transaction ${addTx.hash} to be confirmed...` - ) - await addTx.wait(networks[network.name].confirmations) - - console.log(`Authorized consumer contract: ${consumer}`) - } - - const subInfo = await registry.getSubscription(subscriptionId) - console.log(`\nCreated subscription with ID: ${subscriptionId}`) - console.log(`Owner: ${subInfo[1]}`) - console.log(`Balance: ${ethers.utils.formatEther(subInfo[0])} LINK`) - console.log(`${subInfo[2].length} authorized consumer contract${subInfo[2].length === 1 ? "" : "s"}:`) - console.log(subInfo[2]) - }) diff --git a/contracts/tasks/Functions-billing/fund.js b/contracts/tasks/Functions-billing/fund.js deleted file mode 100644 index 17035e6..0000000 --- a/contracts/tasks/Functions-billing/fund.js +++ /dev/null @@ -1,67 +0,0 @@ -const { networks } = require("../../networks") - -task("functions-sub-fund", "Funds a billing subscription for Functions consumer contracts") - .addParam("amount", "Amount to fund subscription in LINK") - .addParam("subid", "Subscription ID to fund") - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error("This command cannot be used on a local hardhat chain. Specify a valid network.") - } - - const subscriptionId = taskArgs.subid - const linkAmount = taskArgs.amount - - const RegistryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // Check that the subscription is valid - let preSubInfo - try { - preSubInfo = await registry.getSubscription(subscriptionId) - } catch (error) { - if (error.errorName === "InvalidSubscription") { - throw Error(`Subscription ID "${subscriptionId}" is invalid or does not exist`) - } - throw error - } - - // Convert LINK to Juels - const juelsAmount = ethers.utils.parseUnits(linkAmount) - console.log(`Funding subscription ${subscriptionId} with ${ethers.utils.formatEther(juelsAmount)} LINK`) - - const LinkTokenFactory = await ethers.getContractFactory("LinkToken") - const linkToken = await LinkTokenFactory.attach(networks[network.name].linkToken) - - const accounts = await ethers.getSigners() - const signer = accounts[0] - - // Ensure sufficient balance - const balance = await linkToken.balanceOf(signer.address) - if (juelsAmount.gt(balance)) { - throw Error( - `Insufficient LINK balance. Trying to fund subscription with ${ethers.utils.formatEther( - juelsAmount - )} LINK, but wallet only has ${ethers.utils.formatEther(balance)}.` - ) - } - - // Fund the subscription with LINK - const fundTx = await linkToken.transferAndCall( - networks[network.name]["functionsBillingRegistryProxy"], - juelsAmount, - ethers.utils.defaultAbiCoder.encode(["uint64"], [subscriptionId]) - ) - - console.log( - `Waiting ${networks[network.name].confirmations} blocks for transaction ${fundTx.hash} to be confirmed...` - ) - await fundTx.wait(networks[network.name].confirmations) - - const postSubInfo = await registry.getSubscription(subscriptionId) - - console.log( - `\nSubscription ${subscriptionId} has a total balance of ${ethers.utils.formatEther(postSubInfo[0])} LINK` - ) - }) diff --git a/contracts/tasks/Functions-billing/index.js b/contracts/tasks/Functions-billing/index.js deleted file mode 100644 index 491646d..0000000 --- a/contracts/tasks/Functions-billing/index.js +++ /dev/null @@ -1,9 +0,0 @@ -exports.create = require("./create") -exports.fund = require("./fund") -exports.info = require("./info") -exports.add = require("./add") -exports.remove = require("./remove") -exports.cancel = require("./cancel") -exports.transfer = require("./transfer") -exports.accept = require("./accept") -exports.timeoutRequests = require("./timeoutRequest") diff --git a/contracts/tasks/Functions-billing/info.js b/contracts/tasks/Functions-billing/info.js deleted file mode 100644 index 6526c86..0000000 --- a/contracts/tasks/Functions-billing/info.js +++ /dev/null @@ -1,35 +0,0 @@ -const { networks } = require("../../networks") - -task( - "functions-sub-info", - "Gets the Functions billing subscription balance, owner, and list of authorized consumer contract addresses" -) - .addParam("subid", "Subscription ID") - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error("This command cannot be used on a local hardhat chain. Specify a valid network.") - } - - const subscriptionId = taskArgs.subid - - const RegistryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // Check that the subscription is valid - let subInfo - try { - subInfo = await registry.getSubscription(subscriptionId) - } catch (error) { - if (error.errorName === "InvalidSubscription") { - throw Error(`Subscription ID ${subscriptionId} is invalid or does not exist`) - } - throw error - } - - console.log(`\nSubscription ${subscriptionId} owner: ${subInfo[1]}`) - console.log(`Balance: ${ethers.utils.formatEther(subInfo[0])} LINK`) - console.log(`${subInfo[2].length} authorized consumer contract${subInfo[2].length === 1 ? "" : "s"}:`) - console.log(subInfo[2]) - }) diff --git a/contracts/tasks/Functions-billing/remove.js b/contracts/tasks/Functions-billing/remove.js deleted file mode 100644 index 2686c85..0000000 --- a/contracts/tasks/Functions-billing/remove.js +++ /dev/null @@ -1,61 +0,0 @@ -const { networks } = require("../../networks") - -task("functions-sub-remove", "Removes a client contract from an Functions billing subscription") - .addParam("subid", "Subscription ID") - .addParam("contract", "Address of the client contract to remove from billing subscription") - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Please specify a valid network or simulate an FunctionsConsumer request locally with "npx hardhat functions-simulate".' - ) - } - - const subscriptionId = taskArgs.subid - const consumer = taskArgs.contract - - const RegistryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // Check that the subscription is valid - let preSubInfo - try { - preSubInfo = await registry.getSubscription(subscriptionId) - } catch (error) { - if (error.errorName === "InvalidSubscription") { - throw Error(`Subscription ID "${subscriptionId}" is invalid or does not exist`) - } - throw error - } - - // Check that the requesting wallet is the owner of the subscription - const accounts = await ethers.getSigners() - const signer = accounts[0] - if (preSubInfo[1] !== signer.address) { - throw Error("The current wallet is not the owner of the subscription") - } - - // Check that the consumer is currently authorized before attempting to remove - const existingConsumers = preSubInfo[2].map((addr) => addr.toLowerCase()) - if (!existingConsumers.includes(consumer.toLowerCase())) { - throw Error(`Consumer address ${consumer} is not registered to use subscription ${subscriptionId}`) - } - - console.log(`Removing consumer contract address ${consumer} to subscription ${subscriptionId}`) - const rmTx = await registry.removeConsumer(subscriptionId, consumer) - - console.log( - `Waiting ${networks[network.name].confirmations} blocks for transaction ${rmTx.hash} to be confirmed...` - ) - await rmTx.wait(networks[network.name].confirmations) - console.log(`\nRemoved consumer contract address ${consumer} from subscription ${subscriptionId}`) - - const postSubInfo = await registry.getSubscription(subscriptionId) - console.log( - `${postSubInfo[2].length} authorized consumer contract${ - postSubInfo[2].length === 1 ? "" : "s" - } for subscription ${subscriptionId}:` - ) - console.log(postSubInfo[2]) - }) diff --git a/contracts/tasks/Functions-billing/timeoutRequest.js b/contracts/tasks/Functions-billing/timeoutRequest.js deleted file mode 100644 index 9a58a76..0000000 --- a/contracts/tasks/Functions-billing/timeoutRequest.js +++ /dev/null @@ -1,36 +0,0 @@ -const { networks } = require("../../networks") - -task("functions-timeout-requests", "Times out expired requests") - .addParam("requestids", "1 or more request IDs to timeout separated by commas") - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error("This command cannot be used on a local hardhat chain. Specify a valid network.") - } - - const requestIdsToTimeout = taskArgs.requestids.split(",") - - console.log(`Timing out requests ${requestIdsToTimeout} on ${network.name}`) - - const RegistryFactory = await ethers.getContractFactory("FunctionsBillingRegistry") - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // Listen for timed-out request events - registry.on("RequestTimedOut", (timedOutRequestId) => { - // Filter in case multiple users attempt to time out requests simultaneously - if (requestIdsToTimeout.includes(timedOutRequestId)) { - console.log(`Request ${timedOutRequestId} successfully timed out`) - } - }) - - const timeoutTx = await registry.timeoutRequests(requestIdsToTimeout, { gasLimit: 10_000_000 }) - - console.log( - `Waiting ${networks[network.name].confirmations} blocks for transaction ${timeoutTx.hash} to be confirmed...` - ) - await timeoutTx.wait(networks[network.name].confirmations) - - // Close the event listener - await registry.removeAllListeners() - - console.log("\nTimeout requests transaction complete") - }) diff --git a/contracts/tasks/Functions-billing/transfer.js b/contracts/tasks/Functions-billing/transfer.js deleted file mode 100644 index e37d402..0000000 --- a/contracts/tasks/Functions-billing/transfer.js +++ /dev/null @@ -1,55 +0,0 @@ -const { networks } = require("../../networks") -const utils = require("../utils") - -task("functions-sub-transfer", "Request ownership of an Functions subscription be transferred to a new address") - .addParam("subid", "Subscription ID") - .addParam("newowner", "Address of the new owner") - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Please specify a valid network or simulate an FunctionsConsumer request locally with "npx hardhat functions-simulate".' - ) - } - - utils.prompt( - "\nTransferring the subscription to a new owner will require generating a new signature for encrypted secrets.\nAny previous encrypted secrets will no longer work with this subscription ID and must be regenerated by the new owner.\nDo you still want to continue? (y) Yes / (n) No\n" - ) - - const subscriptionId = taskArgs.subid - const newOwner = taskArgs.newowner - - const RegistryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registry = await RegistryFactory.attach(networks[network.name]["functionsBillingRegistryProxy"]) - - // Check that the subscription is valid - let subInfo - try { - subInfo = await registry.getSubscription(subscriptionId) - } catch (error) { - if (error.errorName === "InvalidSubscription") { - throw Error(`Subscription ID ${subscriptionId} is invalid or does not exist`) - } - throw error - } - - // Check that the requesting wallet is the owner of the subscription - const accounts = await ethers.getSigners() - const signer = accounts[0] - if (subInfo[1] !== signer.address) { - throw Error("The current wallet is not the owner of the subscription") - } - - console.log(`Requesting ownership transfer of subscription ${subscriptionId} to new owner ${newOwner}`) - const transferTx = await registry.requestSubscriptionOwnerTransfer(subscriptionId, newOwner) - - console.log( - `Waiting ${networks[network.name].confirmations} blocks for transaction ${transferTx.hash} to be confirmed...` - ) - await transferTx.wait(networks[network.name].confirmations) - - console.log( - `\nOwnership transfer to ${newOwner} requested for subscription ${subscriptionId}. The new owner must now accept the transfer request.` - ) - }) diff --git a/contracts/tasks/Functions-client/buildOffchainSecrets.js b/contracts/tasks/Functions-client/buildOffchainSecrets.js deleted file mode 100644 index 6b9deea..0000000 --- a/contracts/tasks/Functions-client/buildOffchainSecrets.js +++ /dev/null @@ -1,47 +0,0 @@ -const { networks } = require("../../networks") -const fs = require("fs") -const { generateOffchainSecrets } = require("../utils/generateOffchainSecrets") -const path = require("path") -const process = require("process") - -task( - "functions-build-offchain-secrets", - "Builds an off-chain secrets object that can be uploaded and referenced via URL" -) - .addOptionalParam("output", "Output file name (defaults to offchain-secrets.json)") - .addOptionalParam( - "configpath", - "Path to Functions request config file", - `${__dirname}/../../Functions-request-config.js`, - types.string - ) - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error("This command cannot be used on a local hardhat chain.") - } - - const requestConfig = require(path.isAbsolute(taskArgs.configpath) - ? taskArgs.configpath - : path.join(process.cwd(), taskArgs.configpath)) - - console.log( - `Using public keys from FunctionsOracle contract ${networks[network.name]["functionsOracleProxy"]} on network ${ - network.name - }` - ) - const OracleFactory = await ethers.getContractFactory("contracts/dev/functions/FunctionsOracle.sol:FunctionsOracle") - const oracleContract = await OracleFactory.attach(networks[network.name]["functionsOracleProxy"]) - const [nodeAddresses, perNodePublicKeys] = await oracleContract.getAllNodePublicKeys() - const DONPublicKey = await oracleContract.getDONPublicKey() - - const offchainSecrets = await generateOffchainSecrets( - requestConfig, - process.env.PRIVATE_KEY, - DONPublicKey, - nodeAddresses, - perNodePublicKeys - ) - - fs.writeFileSync(taskArgs.output ?? "offchain-secrets.json", JSON.stringify(offchainSecrets)) - console.log(`\nWrote offchain secrets file to ${taskArgs.output ?? "offchain-secrets.json"}`) - }) diff --git a/contracts/tasks/Functions-client/buildRequestJSON.js b/contracts/tasks/Functions-client/buildRequestJSON.js deleted file mode 100644 index e7981d8..0000000 --- a/contracts/tasks/Functions-client/buildRequestJSON.js +++ /dev/null @@ -1,144 +0,0 @@ -const { simulateRequest, buildRequest, getRequestConfig } = require("../../FunctionsSandboxLibrary") -const { generateOffchainSecrets } = require("../utils/generateOffchainSecrets") -const { networks } = require("../../networks") -const utils = require("../utils") -const axios = require("axios") -const fs = require("fs") -const { createGist } = require("../utils/github") -const path = require("path") -const process = require("process") - -task("functions-build-request", "Creates a JSON file with Functions request parameters") - .addOptionalParam("output", "Output file name (defaults to Functions-request.json)") - .addOptionalParam( - "simulate", - "Flag indicating if simulation should be run before making an on-chain request", - true, - types.boolean - ) - .addOptionalParam( - "configpath", - "Path to Functions request config file", - `${__dirname}/../../Functions-request-config.js`, - types.string - ) - .setAction(async (taskArgs, hre) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local development chain. Specify a valid network or simulate an Functions request locally with "npx hardhat functions-simulate".' - ) - } - - const unvalidatedRequestConfig = require(path.isAbsolute(taskArgs.configpath) - ? taskArgs.configpath - : path.join(process.cwd(), taskArgs.configpath)) - const requestConfig = getRequestConfig(unvalidatedRequestConfig) - - const request = await generateRequest(requestConfig, taskArgs) - - fs.writeFileSync(taskArgs.output ?? "Functions-request.json", JSON.stringify(request)) - console.log(`Wrote request to ${taskArgs.output ?? "Functions-request.json"}`) - }) - -const verifyOffchainSecrets = async (secretsURLs, nodeAddresses) => { - const offchainSecretsResponses = [] - for (const url of secretsURLs) { - try { - const response = await axios.request({ - url, - timeout: 3000, - responseType: "json", - maxContentLength: 1000000, - }) - offchainSecretsResponses.push({ - url, - secrets: response.data, - }) - } catch (error) { - throw Error(`Failed to fetch off-chain secrets from ${url}\n${error}`) - } - } - - for (const { secrets, url } of offchainSecretsResponses) { - if (JSON.stringify(secrets) !== JSON.stringify(offchainSecretsResponses[0].secrets)) { - throw Error( - `Off-chain secrets URLs ${url} and ${offchainSecretsResponses[0].url} do not contain the same JSON object. All secrets URLs must have an identical JSON object.` - ) - } - - for (const nodeAddress of nodeAddresses) { - if (!secrets[nodeAddress.toLowerCase()]) { - if (!secrets["0x0"]) { - throw Error(`No secrets specified for node ${nodeAddress.toLowerCase()} and no default secrets found.`) - } - if (Object.keys(secrets) > 1) { - console.log( - `WARNING: No secrets found for node ${nodeAddress.toLowerCase()}. That node will use default secrets specified by the "0x0" entry.` - ) - } - } - } - } -} - -const generateRequest = async (requestConfig, taskArgs) => { - if (taskArgs.simulate !== false) { - console.log("Simulating Functions request locally...") - - if (!requestConfig.secrets || Object.keys(requestConfig.secrets).length === 0) { - if (requestConfig.perNodeSecrets && requestConfig.perNodeSecrets[0]) { - requestConfig.secrets = requestConfig.perNodeSecrets[0] - } - } - - const { success, resultLog } = await simulateRequest(requestConfig) - console.log(`\n${resultLog}`) - - // If the simulated JavaScript source code contains an error, confirm the user still wants to continue - if (!success) { - await utils.prompt("There was an error when running the JavaScript source code for the request.") - } - } - - const OracleFactory = await ethers.getContractFactory("contracts/dev/functions/FunctionsOracle.sol:FunctionsOracle") - const oracle = await OracleFactory.attach(taskArgs.oracle || networks[network.name]["functionsOracleProxy"]) - const [nodeAddresses, perNodePublicKeys] = await oracle.getAllNodePublicKeys() - const DONPublicKey = await oracle.getDONPublicKey() - - if ( - (requestConfig.secrets && Object.keys(requestConfig.secrets).length > 0) || - (requestConfig.perNodeSecrets && Object.keys(requestConfig.perNodeSecrets).length > 0) - ) { - if (!requestConfig.secretsURLs || requestConfig.secretsURLs.length === 0) { - // If their are secrets (or per-node secrets) and no secretsURLs are provided, create and upload an off-chain secrets Gist - const offchainSecrets = await generateOffchainSecrets( - requestConfig, - process.env["PRIVATE_KEY"], - DONPublicKey, - nodeAddresses, - perNodePublicKeys - ) - - if (!process.env["GITHUB_API_TOKEN"] || process.env["GITHUB_API_TOKEN"] === "") { - throw Error("GITHUB_API_TOKEN environment variable not set") - } - - const secretsURL = await createGist(process.env["GITHUB_API_TOKEN"], offchainSecrets) - console.log(`Successfully created encrypted secrets Gist: ${secretsURL}`) - requestConfig.secretsURLs = [`${secretsURL}/raw`] - } else { - // Else, verify the provided off-chain secrets URLs are valid - await verifyOffchainSecrets(requestConfig.secretsURLs, nodeAddresses) - } - } - - // Remove the preceding 0x from the DON public key - requestConfig.DONPublicKey = DONPublicKey.slice(2) - // Build the parameters to make a request from the client contract - const request = await buildRequest(requestConfig) - request.secretsURLs = requestConfig.secretsURLs - return request -} - -exports.generateRequest = generateRequest -exports.verifyOffchainSecrets = verifyOffchainSecrets diff --git a/contracts/tasks/Functions-client/checkUpkeep.js b/contracts/tasks/Functions-client/checkUpkeep.js deleted file mode 100644 index e4da558..0000000 --- a/contracts/tasks/Functions-client/checkUpkeep.js +++ /dev/null @@ -1,25 +0,0 @@ -task("functions-check-upkeep", "Checks if checkUpkeep returns true for an Automation compatible contract") - .addParam("contract", "Address of the contract to check") - .addOptionalParam( - "data", - "Hex string representing bytes that are passed to the checkUpkeep function (defaults to empty bytes)" - ) - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Specify a valid network or simulate an FunctionsConsumer request locally with "npx hardhat functions-simulate".' - ) - } - - const checkData = taskArgs.data ?? [] - - console.log( - `Checking if upkeep is required for Automation client contract ${taskArgs.contract} on network ${network.name}` - ) - const autoClientContractFactory = await ethers.getContractFactory("AutomatedFunctionsConsumer") - const autoClientContract = await autoClientContractFactory.attach(taskArgs.contract) - - const checkUpkeep = await autoClientContract.checkUpkeep(checkData) - - console.log(`\nUpkeep needed: ${checkUpkeep[0]}\nPerform data: ${checkUpkeep[1]}`) - }) diff --git a/contracts/tasks/Functions-client/clearGists.js b/contracts/tasks/Functions-client/clearGists.js deleted file mode 100644 index be27527..0000000 --- a/contracts/tasks/Functions-client/clearGists.js +++ /dev/null @@ -1,50 +0,0 @@ -const axios = require("axios") -const { deleteGist } = require("../utils/github") -const { RequestStore } = require("../utils/artifact") - -task( - "functions-clear-gists", - "Deletes long-lived GitHub Gists that have been created for use in Automated Functions requests" -) - .addParam("contract", "Address of the contract") - .setAction(async (taskArgs, hre) => { - if (network.name === "hardhat") { - throw Error("This command cannot be used on a local hardhat chain. Specify a valid network.") - } - - const store = new RequestStore(hre.network.config.chainId, network.name, "automatedConsumer") - - let artifact - try { - artifact = await store.read(taskArgs.contract) - } catch { - return console.log(`Automated Consumer ${taskArgs.contract} not found.`) - } - - if (!artifact.activeManagedSecretsURLs || artifact.secretsURLs.length < 1) { - return console.log(`Automated Consumer ${taskArgs.contract} does not have active secret Gists.`) - } - - let success = true - await Promise.all( - artifact.secretsURLs.map(async (url) => { - if (!url.includes("github")) return console.log(`\n${url} is not a GitHub Gist - skipping`) - const exists = axios.get(url) - if (exists) { - // Gist URLs end with '/raw', remove this - const urlNoRaw = url.slice(0, -4) - const succeeded = await deleteGist(process.env["GITHUB_API_TOKEN"], urlNoRaw) - if (!succeeded) success = succeeded - } - }) - ) - - if (!success) - return console.log( - `\nSome off-chain secret Gists for Automated Consumer ${taskArgs.contract} could not be deleted. Please re-try or delete manually.` - ) - - await store.update(taskArgs.contract, { activeManagedSecretsURLs: false }) - - console.log(`\nAll off-chain secret Gists for Automated Consumer ${taskArgs.contract} have been deleted.`) - }) diff --git a/contracts/tasks/Functions-client/deployAutoClient.js b/contracts/tasks/Functions-client/deployAutoClient.js deleted file mode 100644 index bb1e5a9..0000000 --- a/contracts/tasks/Functions-client/deployAutoClient.js +++ /dev/null @@ -1,92 +0,0 @@ -const { types } = require("hardhat/config") -const { networks } = require("../../networks") -const { addClientConsumerToSubscription } = require("../Functions-billing/add") -const { setAutoRequest } = require("./setAutoRequest") - -task("functions-deploy-auto-client", "Deploys the AutomatedFunctionsConsumer contract") - .addParam("subid", "Billing subscription ID used to pay for Functions requests") - .addOptionalParam("interval", "Update interval in seconds for Automation to call performUpkeep", 300, types.int) - .addOptionalParam("verify", "Set to true to verify client contract", false, types.boolean) - .addOptionalParam( - "gaslimit", - "Maximum amount of gas that can be used to call fulfillRequest in the client contract", - 250000, - types.int - ) - .addOptionalParam( - "simulate", - "Flag indicating if simulation should be run before making an on-chain request", - true, - types.boolean - ) - .addOptionalParam( - "configpath", - "Path to Functions request config file", - `${__dirname}/../../Functions-request-config.js`, - types.string - ) - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Specify a valid network or simulate an FunctionsConsumer request locally with "npx hardhat functions-simulate".' - ) - } - - if (taskArgs.gaslimit > 300000) { - throw Error("Gas limit must be less than or equal to 300,000") - } - - console.log(`Deploying AutomatedFunctionsConsumer contract to ${network.name}`) - - console.log("\n__Compiling Contracts__") - await run("compile") - - const autoClientContractFactory = await ethers.getContractFactory("AutomatedFunctionsConsumer") - const autoClientContract = await autoClientContractFactory.deploy( - networks[network.name]["functionsOracleProxy"], - taskArgs.subid, - taskArgs.gaslimit, - taskArgs.interval - ) - - console.log(`\nWaiting 1 block for transaction ${autoClientContract.deployTransaction.hash} to be confirmed...`) - await autoClientContract.deployTransaction.wait(1) - - await addClientConsumerToSubscription(taskArgs.subid, autoClientContract.address) - - taskArgs.contract = autoClientContract.address - - await setAutoRequest(autoClientContract.address, taskArgs) - - const verifyContract = taskArgs.verify - - if (verifyContract && !!networks[network.name].verifyApiKey && networks[network.name].verifyApiKey !== "UNSET") { - try { - console.log("\nVerifying contract...") - await autoClientContract.deployTransaction.wait(Math.max(6 - networks[network.name].confirmations, 0)) - await run("verify:verify", { - address: autoClientContract.address, - constructorArguments: [ - networks[network.name]["functionsOracleProxy"], - taskArgs.subid, - taskArgs.gaslimit, - taskArgs.interval, - ], - }) - console.log("Contract verified") - } catch (error) { - if (!error.message.includes("Already Verified")) { - console.log("Error verifying contract. Delete the build folder and try again.") - console.log(error) - } else { - console.log("Contract already verified") - } - } - } else if (verifyContract) { - console.log( - "\nPOLYGONSCAN_API_KEY, ETHERSCAN_API_KEY or SNOWTRACE_API_KEY is missing. Skipping contract verification..." - ) - } - - console.log(`\nAutomatedFunctionsConsumer contract deployed to ${autoClientContract.address} on ${network.name}`) - }) diff --git a/contracts/tasks/Functions-client/deployClient.js b/contracts/tasks/Functions-client/deployClient.js deleted file mode 100644 index c5179fc..0000000 --- a/contracts/tasks/Functions-client/deployClient.js +++ /dev/null @@ -1,56 +0,0 @@ -const { types } = require("hardhat/config") -const { networks } = require("../../networks") - -task("functions-deploy-client", "Deploys the FunctionsConsumer contract") - .addOptionalParam("verify", "Set to true to verify client contract", false, types.boolean) - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Specify a valid network or simulate an FunctionsConsumer request locally with "npx hardhat functions-simulate".' - ) - } - - console.log(`Deploying FunctionsConsumer contract to ${network.name}`) - - const oracleAddress = networks[network.name]["functionsOracleProxy"] - - console.log("\n__Compiling Contracts__") - await run("compile") - - const clientContractFactory = await ethers.getContractFactory("FunctionsConsumer") - const clientContract = await clientContractFactory.deploy(oracleAddress) - - console.log( - `\nWaiting ${networks[network.name].confirmations} blocks for transaction ${ - clientContract.deployTransaction.hash - } to be confirmed...` - ) - await clientContract.deployTransaction.wait(networks[network.name].confirmations) - - const verifyContract = taskArgs.verify - - if (verifyContract && !!networks[network.name].verifyApiKey && networks[network.name].verifyApiKey !== "UNSET") { - try { - console.log("\nVerifying contract...") - await clientContract.deployTransaction.wait(Math.max(6 - networks[network.name].confirmations, 0)) - await run("verify:verify", { - address: clientContract.address, - constructorArguments: [oracleAddress], - }) - console.log("Contract verified") - } catch (error) { - if (!error.message.includes("Already Verified")) { - console.log("Error verifying contract. Delete the build folder and try again.") - console.log(error) - } else { - console.log("Contract already verified") - } - } - } else if (verifyContract) { - console.log( - "\nPOLYGONSCAN_API_KEY, ETHERSCAN_API_KEY or SNOWTRACE_API_KEY is missing. Skipping contract verification..." - ) - } - - console.log(`\nFunctionsConsumer contract deployed to ${clientContract.address} on ${network.name}`) - }) diff --git a/contracts/tasks/Functions-client/index.js b/contracts/tasks/Functions-client/index.js deleted file mode 100644 index 8384fdc..0000000 --- a/contracts/tasks/Functions-client/index.js +++ /dev/null @@ -1,12 +0,0 @@ -exports.readResultAndError = require("./readResultAndError.js") -exports.requestData = require("./request.js") -exports.simulate = require("./simulate.js") -exports.deployClient = require("./deployClient.js") -exports.deployAutoClient = require("./deployAutoClient.js") -exports.setOracleAddr = require("./setOracleAddr.js") -exports.buildOffchainSecrets = require("./buildOffchainSecrets.js") -exports.buildRequestJSON = require("./buildRequestJSON.js") -exports.checkUpkeep = require("./checkUpkeep.js") -exports.performUpkeep = require("./performManualUpkeep.js") -exports.setAutoRequest = require("./setAutoRequest.js") -exports.clearGists = require("./clearGists.js") diff --git a/contracts/tasks/Functions-client/performManualUpkeep.js b/contracts/tasks/Functions-client/performManualUpkeep.js deleted file mode 100644 index b1eba2a..0000000 --- a/contracts/tasks/Functions-client/performManualUpkeep.js +++ /dev/null @@ -1,40 +0,0 @@ -const { networks } = require("../../networks") - -task("functions-perform-upkeep", "Manually call performUpkeep in an Automation compatible contract") - .addParam("contract", "Address of the contract to call") - .addOptionalParam( - "data", - "Hex string representing bytes that are passed to the performUpkeep function (defaults to empty bytes)" - ) - .setAction(async (taskArgs) => { - // A manual gas limit is required as the gas limit estimated by Ethers is not always accurate - const overrides = { - gasLimit: 1000000, - gasPrice: networks[network.name].gasPrice, - } - - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Specify a valid network or simulate an FunctionsConsumer request locally with "npx hardhat functions-simulate".' - ) - } - - const performData = taskArgs.data ?? [] - - console.log( - `Calling performUpkeep for Automation client contract ${taskArgs.contract} on network ${network.name}${ - taskArgs.data ? ` with data ${performData}` : "" - }` - ) - const autoClientContractFactory = await ethers.getContractFactory("AutomatedFunctionsConsumer") - const autoClientContract = await autoClientContractFactory.attach(taskArgs.contract) - - const checkUpkeep = await autoClientContract.performUpkeep(performData, overrides) - - console.log( - `Waiting ${networks[network.name].confirmations} blocks for transaction ${checkUpkeep.hash} to be confirmed...` - ) - await checkUpkeep.wait(networks[network.name].confirmations) - - console.log(`\nSuccessfully called performUpkeep`) - }) diff --git a/contracts/tasks/Functions-client/readResultAndError.js b/contracts/tasks/Functions-client/readResultAndError.js deleted file mode 100644 index ceee253..0000000 --- a/contracts/tasks/Functions-client/readResultAndError.js +++ /dev/null @@ -1,45 +0,0 @@ -const { getDecodedResultLog } = require("../../FunctionsSandboxLibrary") -const path = require("path") -const process = require("process") - -task( - "functions-read", - "Reads the latest response (or error) returned to a FunctionsConsumer or AutomatedFunctionsConsumer client contract" -) - .addParam("contract", "Address of the client contract to read") - .addOptionalParam( - "configpath", - "Path to Functions request config file", - `${__dirname}/../../Functions-request-config.js`, - types.string - ) - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Specify a valid network or simulate an FunctionsConsumer request locally with "npx hardhat functions-simulate".' - ) - } - - console.log(`Reading data from Functions client contract ${taskArgs.contract} on network ${network.name}`) - const clientContractFactory = await ethers.getContractFactory("FunctionsConsumer") - const clientContract = await clientContractFactory.attach(taskArgs.contract) - - let latestError = await clientContract.latestError() - if (latestError.length > 0 && latestError !== "0x") { - const errorString = Buffer.from(latestError.slice(2), "hex").toString() - console.log(`\nOn-chain error message: ${Buffer.from(latestError.slice(2), "hex").toString()}`) - } - - let latestResponse = await clientContract.latestResponse() - if (latestResponse.length > 0 && latestResponse !== "0x") { - const requestConfig = require(path.isAbsolute(taskArgs.configpath) - ? taskArgs.configpath - : path.join(process.cwd(), taskArgs.configpath)) - console.log( - `\nOn-chain response represented as a hex string: ${latestResponse}\n${getDecodedResultLog( - requestConfig, - latestResponse - )}` - ) - } - }) diff --git a/contracts/tasks/Functions-client/request.js b/contracts/tasks/Functions-client/request.js deleted file mode 100644 index cc75b1e..0000000 --- a/contracts/tasks/Functions-client/request.js +++ /dev/null @@ -1,308 +0,0 @@ -const { getDecodedResultLog, getRequestConfig } = require("../../FunctionsSandboxLibrary") -const { generateRequest } = require("./buildRequestJSON") -const { networks } = require("../../networks") -const utils = require("../utils") -const chalk = require("chalk") -const { deleteGist } = require("../utils/github") -const { RequestStore } = require("../utils/artifact") -const path = require("path") -const process = require("process") - -task("functions-request", "Initiates a request from a Functions client contract") - .addParam("contract", "Address of the client contract to call") - .addParam("subid", "Billing subscription ID used to pay for the request") - .addOptionalParam( - "simulate", - "Flag indicating if simulation should be run before making an on-chain request", - true, - types.boolean - ) - .addOptionalParam( - "gaslimit", - "Maximum amount of gas that can be used to call fulfillRequest in the client contract", - 100000, - types.int - ) - .addOptionalParam("requestgas", "Gas limit for calling the executeRequest function", 1_500_000, types.int) - .addOptionalParam( - "configpath", - "Path to Functions request config file", - `${__dirname}/../../Functions-request-config.js`, - types.string - ) - .setAction(async (taskArgs, hre) => { - // A manual gas limit is required as the gas limit estimated by Ethers is not always accurate - const overrides = { - gasLimit: taskArgs.requestgas, - gasPrice: networks[network.name].gasPrice, - } - - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local development chain. Specify a valid network or simulate an Functions request locally with "npx hardhat functions-simulate".' - ) - } - - // Get the required parameters - const contractAddr = taskArgs.contract - const subscriptionId = taskArgs.subid - const gasLimit = taskArgs.gaslimit - if (gasLimit > 300000) { - throw Error("Gas limit must be less than or equal to 300,000") - } - - // Attach to the required contracts - const clientContractFactory = await ethers.getContractFactory("FunctionsConsumer") - const clientContract = clientContractFactory.attach(contractAddr) - const OracleFactory = await ethers.getContractFactory("contracts/dev/functions/FunctionsOracle.sol:FunctionsOracle") - const oracle = await OracleFactory.attach(networks[network.name]["functionsOracleProxy"]) - const registryAddress = await oracle.getRegistry() - const RegistryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registry = await RegistryFactory.attach(registryAddress) - - // Check that the subscription is valid - let subInfo - try { - subInfo = await registry.getSubscription(subscriptionId) - } catch (error) { - if (error.errorName === "InvalidSubscription") { - throw Error(`Subscription ID "${subscriptionId}" is invalid or does not exist`) - } - throw error - } - // Validate the client contract has been authorized to use the subscription - const existingConsumers = subInfo[2].map((addr) => addr.toLowerCase()) - if (!existingConsumers.includes(contractAddr.toLowerCase())) { - throw Error(`Consumer contract ${contractAddr} is not registered to use subscription ${subscriptionId}`) - } - - const unvalidatedRequestConfig = require(path.isAbsolute(taskArgs.configpath) - ? taskArgs.configpath - : path.join(process.cwd(), taskArgs.configpath)) - const requestConfig = getRequestConfig(unvalidatedRequestConfig) - - const simulatedSecretsURLBytes = `0x${Buffer.from( - "https://exampleSecretsURL.com/f09fa0db8d1c8fab8861ec97b1d7fdf1/raw/d49bbd20dc562f035bdf8832399886baefa970c9/encrypted-functions-request-data-1679941580875.json" - ).toString("hex")}` - - // Estimate the cost of the request - const { lastBaseFeePerGas, maxPriorityFeePerGas } = await hre.ethers.provider.getFeeData() - const estimatedCostJuels = await clientContract.estimateCost( - [ - requestConfig.codeLocation, - 1, // SecretsLocation: Remote - requestConfig.codeLanguage, - requestConfig.source, - requestConfig.secrets && Object.keys(requestConfig.secrets).length > 0 ? simulatedSecretsURLBytes : [], - requestConfig.args ?? [], - ], - subscriptionId, - gasLimit, - maxPriorityFeePerGas.add(lastBaseFeePerGas) - ) - const estimatedCostLink = hre.ethers.utils.formatUnits(estimatedCostJuels, 18) - - // Ensure that the subscription has a sufficient balance - const subBalanceInJules = subInfo[0] - const linkBalance = hre.ethers.utils.formatUnits(subBalanceInJules, 18) - - if (subBalanceInJules.lt(estimatedCostJuels)) { - throw Error( - `Subscription ${subscriptionId} does not have sufficient funds. The estimated cost is ${estimatedCostLink} LINK, but the subscription only has a balance of ${linkBalance} LINK` - ) - } - - const transactionEstimateGas = await clientContract.estimateGas.executeRequest( - requestConfig.source, - requestConfig.secrets && Object.keys(requestConfig.secrets).length > 0 ? simulatedSecretsURLBytes : [], - requestConfig.args ?? [], - subscriptionId, - gasLimit, - overrides - ) - - await utils.promptTxCost(transactionEstimateGas, hre, true) - - // Print the estimated cost of the request - // Ask for confirmation before initiating the request on-chain - await utils.prompt( - `If the request's callback uses all ${utils.numberWithCommas( - gasLimit - )} gas, this request will charge the subscription:\n${chalk.blue(estimatedCostLink + " LINK")}` - ) - // TODO: add cost of this LINK in USD - - // doGistCleanup indicates if an encrypted secrets Gist was created automatically and should be cleaned up once the request is complete - let doGistCleanup = !(requestConfig.secretsURLs && requestConfig.secretsURLs.length > 0) - const request = await generateRequest(requestConfig, taskArgs) - doGistCleanup = doGistCleanup && request.secrets - - const store = new RequestStore(hre.network.config.chainId, network.name, "consumer") - - const spinner = utils.spin({ - text: `Submitting transaction for FunctionsConsumer contract ${contractAddr} on network ${network.name}`, - }) - - // Use a promise to wait & listen for the fulfillment event before returning - await new Promise(async (resolve, reject) => { - let requestId - - let cleanupInProgress = false - const cleanup = async () => { - spinner.stop() - if (doGistCleanup) { - if (!cleanupInProgress) { - cleanupInProgress = true - const success = await deleteGist(process.env["GITHUB_API_TOKEN"], request.secretsURLs[0].slice(0, -4)) - if (success) { - await store.update(requestId, { activeManagedSecretsURLs: false }) - } - return resolve() - } - return - } - return resolve() - } - - // Initiate the listeners before making the request - // Listen for fulfillment errors - oracle.on("UserCallbackError", async (eventRequestId, msg) => { - if (requestId == eventRequestId) { - spinner.fail( - "Error encountered when calling fulfillRequest in client contract.\n" + - "Ensure the fulfillRequest function in the client contract is correct and the --gaslimit is sufficient." - ) - console.log(`${msg}\n`) - await store.update(requestId, { status: "failed", error: msg }) - await cleanup() - } - }) - oracle.on("UserCallbackRawError", async (eventRequestId, msg) => { - if (requestId == eventRequestId) { - spinner.fail("Raw error in contract request fulfillment. Please contact Chainlink support.") - console.log(Buffer.from(msg, "hex").toString()) - await store.update(requestId, { status: "failed", error: msg }) - await cleanup() - } - }) - // Listen for successful fulfillment, both must be true to be finished - let billingEndEventReceived = false - let ocrResponseEventReceived = false - clientContract.on("OCRResponse", async (eventRequestId, result, err) => { - // Ensure the fulfilled requestId matches the initiated requestId to prevent logging a response for an unrelated requestId - if (eventRequestId !== requestId) { - return - } - - spinner.succeed(`Request ${requestId} fulfilled! Data has been written on-chain.\n`) - if (result !== "0x") { - console.log( - `Response returned to client contract represented as a hex string: ${result}\n${getDecodedResultLog( - requestConfig, - result - )}` - ) - } - if (err !== "0x") { - console.log(`Error message returned to client contract: "${Buffer.from(err.slice(2), "hex")}"\n`) - } - ocrResponseEventReceived = true - await store.update(requestId, { status: "complete", result }) - - if (billingEndEventReceived) { - await cleanup() - } - }) - // Listen for the BillingEnd event, log cost breakdown & resolve - registry.on( - "BillingEnd", - async ( - eventRequestId, - eventSubscriptionId, - eventSignerPayment, - eventTransmitterPayment, - eventTotalCost, - eventSuccess - ) => { - if (requestId == eventRequestId) { - const baseFee = eventTotalCost.sub(eventTransmitterPayment) - spinner.stop() - console.log(`Actual amount billed to subscription #${subscriptionId}:`) - const costBreakdownData = [ - { - Type: "Transmission cost:", - Amount: `${hre.ethers.utils.formatUnits(eventTransmitterPayment, 18)} LINK`, - }, - { Type: "Base fee:", Amount: `${hre.ethers.utils.formatUnits(baseFee, 18)} LINK` }, - { Type: "", Amount: "" }, - { Type: "Total cost:", Amount: `${hre.ethers.utils.formatUnits(eventTotalCost, 18)} LINK` }, - ] - utils.logger.table(costBreakdownData) - - // Check for a successful request - billingEndEventReceived = true - if (ocrResponseEventReceived) { - await cleanup() - } - } - } - ) - - let requestTx - try { - // Initiate the on-chain request after all listeners are initialized - requestTx = await clientContract.executeRequest( - request.source, - request.secrets ?? [], - request.args ?? [], - subscriptionId, - gasLimit, - overrides - ) - } catch (error) { - // If the request fails, ensure the encrypted secrets Gist is deleted - if (doGistCleanup) { - await deleteGist(process.env["GITHUB_API_TOKEN"], request.secretsURLs[0].slice(0, -4)) - } - throw error - } - spinner.start("Waiting 2 blocks for transaction to be confirmed...") - const requestTxReceipt = await requestTx.wait(2) - spinner.info( - `Transaction confirmed, see ${ - utils.getEtherscanURL(network.config.chainId) + "tx/" + requestTx.hash - } for more details.` - ) - spinner.stop() - requestId = requestTxReceipt.events[2].args.id - spinner.start( - `Request ${requestId} has been initiated. Waiting for fulfillment from the Decentralized Oracle Network...\n` - ) - await store.create({ - type: "consumer", - requestId, - transactionReceipt: requestTxReceipt, - taskArgs, - codeLocation: requestConfig.codeLocation, - codeLanguage: requestConfig.codeLanguage, - source: requestConfig.source, - secrets: requestConfig.secrets, - perNodeSecrets: requestConfig.perNodeSecrets, - secretsURLs: request.secretsURLs, - activeManagedSecretsURLs: doGistCleanup, - args: requestConfig.args, - expectedReturnType: requestConfig.expectedReturnType, - DONPublicKey: requestConfig.DONPublicKey, - }) - // If a response is not received in time, the request has exceeded the Service Level Agreement - setTimeout(async () => { - spinner.fail( - "A response has not been received within 5 minutes of the request being initiated and has been canceled. Your subscription was not charged. Please make a new request." - ) - await store.update(requestId, { status: "pending_timed_out" }) - reject() - }, 300_000) // TODO: use registry timeout seconds - }) - }) diff --git a/contracts/tasks/Functions-client/setAutoRequest.js b/contracts/tasks/Functions-client/setAutoRequest.js deleted file mode 100644 index a10c115..0000000 --- a/contracts/tasks/Functions-client/setAutoRequest.js +++ /dev/null @@ -1,135 +0,0 @@ -const { types } = require("hardhat/config") -const { networks } = require("../../networks") -const { getRequestConfig } = require("../../FunctionsSandboxLibrary") -const { generateRequest } = require("./buildRequestJSON") -const { RequestStore } = require("../utils/artifact") -const { deleteGist } = require("../utils/github") -const path = require("path") -const process = require("process") - -task("functions-set-auto-request", "Updates the Functions request in a deployed AutomatedFunctionsConsumer contract") - .addParam("contract", "Address of the client contract") - .addParam("subid", "Billing subscription ID used to pay for Functions requests", undefined, types.int) - .addOptionalParam("interval", "Update interval in seconds for Automation to call performUpkeep", 300, types.int) - .addOptionalParam( - "gaslimit", - "Maximum amount of gas that can be used to call fulfillRequest in the client contract", - 250000, - types.int - ) - .addOptionalParam( - "simulate", - "Flag indicating if simulation should be run before making an on-chain request", - true, - types.boolean - ) - .addOptionalParam( - "configpath", - "Path to Functions request config file", - `${__dirname}/../../Functions-request-config.js`, - types.string - ) - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error( - 'This command cannot be used on a local hardhat chain. Specify a valid network or simulate an FunctionsConsumer request locally with "npx hardhat functions-simulate".' - ) - } - - await setAutoRequest(taskArgs.contract, taskArgs) - }) - -const setAutoRequest = async (contract, taskArgs) => { - if (taskArgs.gaslimit > 300000) { - throw Error("Gas limit must be less than or equal to 300,000") - } - - console.log(`Setting the Functions request in AutomatedFunctionsConsumer contract ${contract} on ${network.name}`) - - const autoClientContractFactory = await ethers.getContractFactory("AutomatedFunctionsConsumer") - const autoClientContract = await autoClientContractFactory.attach(contract) - - const unvalidatedRequestConfig = require(path.isAbsolute(taskArgs.configpath) - ? taskArgs.configpath - : path.join(process.cwd(), taskArgs.configpath)) - const requestConfig = getRequestConfig(unvalidatedRequestConfig) - - // doGistCleanup indicates if an encrypted secrets Gist was created automatically and should be cleaned up by the user after use - let doGistCleanup = !(requestConfig.secretsURLs && requestConfig.secretsURLs.length > 0) - const request = await generateRequest(requestConfig, taskArgs) - - if (doGistCleanup && request.secrets) { - console.log( - `Be sure to delete the Gist ${request.secretsURLs[0].slice(0, -4)} once encrypted secrets are no longer in use!\n` - ) - } - - const functionsRequestBytes = await autoClientContract.generateRequest( - request.source, - request.secrets ?? [], - request.args ?? [] - ) - - const store = new RequestStore(hre.network.config.chainId, network.name, "automatedConsumer") - const previousSecretURLs = [] - try { - const artifact = await store.read(taskArgs.contract) - if (artifact.activeManagedSecretsURLs) previousSecretURLs = artifact.secretsURLs - } catch { - /* new request, continue */ - } - - console.log("Setting Functions request") - const setRequestTx = await autoClientContract.setRequest( - taskArgs.subid, - taskArgs.gaslimit, - taskArgs.interval, - functionsRequestBytes - ) - - console.log( - `\nWaiting ${networks[network.name].confirmations} block for transaction ${setRequestTx.hash} to be confirmed...` - ) - await setRequestTx.wait(networks[network.name].confirmations) - - const create = await store.upsert(taskArgs.contract, { - type: "automatedConsumer", - automatedConsumerContractAddress: taskArgs.contract, - transactionReceipt: setRequestTx, - taskArgs, - codeLocation: requestConfig.codeLocation, - codeLanguage: requestConfig.codeLanguage, - source: requestConfig.source, - secrets: requestConfig.secrets, - perNodeSecrets: requestConfig.perNodeSecrets, - secretsURLs: request.secretsURLs, - activeManagedSecretsURLs: doGistCleanup, - args: requestConfig.args, - expectedReturnType: requestConfig.expectedReturnType, - DONPublicKey: requestConfig.DONPublicKey, - }) - - // Clean up previous secretsURLs - if (!create) { - console.log(`Attempting to clean up previous GitHub Gist secrets`) - await Promise.all( - previousSecretURLs.map(async (url) => { - if (!url.includes("github")) return console.log(`\n${url} is not a GitHub Gist - skipping`) - const exists = axios.get(url) - if (exists) { - // Gist URLs end with '/raw', remove this - const urlNoRaw = url.slice(0, -4) - await deleteGist(process.env["GITHUB_API_TOKEN"], urlNoRaw) - } - }) - ) - } - - console.log( - `\n${create ? "Created new" : "Updated"} Functions request in AutomatedFunctionsConsumer contract ${ - autoClientContract.address - } on ${network.name}` - ) -} - -exports.setAutoRequest = setAutoRequest diff --git a/contracts/tasks/Functions-client/setOracleAddr.js b/contracts/tasks/Functions-client/setOracleAddr.js deleted file mode 100644 index fc9ac4d..0000000 --- a/contracts/tasks/Functions-client/setOracleAddr.js +++ /dev/null @@ -1,33 +0,0 @@ -const { networks } = require("../../networks") - -task( - "functions-set-oracle-addr", - "Updates the oracle address for a FunctionsConsumer client contract using the FunctionsOracle address from `network-config.js`" -) - .addParam("contract", "Address of the client contract to update") - .setAction(async (taskArgs) => { - if (network.name === "hardhat") { - throw Error("This command cannot be used on a local hardhat chain. Specify a valid network.") - } - - console.log( - `Setting oracle address to ${networks[network.name]["functionsOracleProxy"]} Functions client contract ${ - taskArgs.contract - } on ${network.name}` - ) - const clientContractFactory = await ethers.getContractFactory("FunctionsConsumer") - const clientContract = await clientContractFactory.attach(taskArgs.contract) - - const updateTx = await clientContract.updateOracleAddress(networks[network.name]["functionsOracleProxy"]) - - console.log( - `\nWaiting ${networks[network.name].confirmations} blocks for transaction ${updateTx.hash} to be confirmed...` - ) - await updateTx.wait(networks[network.name].confirmations) - - console.log( - `\nUpdated oracle address to ${networks[network.name]["functionsOracleProxy"]} for Functions client contract ${ - taskArgs.contract - } on ${network.name}` - ) - }) diff --git a/contracts/tasks/Functions-client/simulate.js b/contracts/tasks/Functions-client/simulate.js deleted file mode 100644 index f9600ec..0000000 --- a/contracts/tasks/Functions-client/simulate.js +++ /dev/null @@ -1,246 +0,0 @@ -const { - simulateRequest, - buildRequest, - getDecodedResultLog, - getRequestConfig, -} = require("../../FunctionsSandboxLibrary") -const { networks, SHARED_DON_PUBLIC_KEY } = require("../../networks") -const path = require("path") -const process = require("process") - -task("functions-simulate", "Simulates an end-to-end fulfillment locally for the FunctionsConsumer contract") - .addOptionalParam( - "gaslimit", - "Maximum amount of gas that can be used to call fulfillRequest in the client contract (defaults to 100,000)" - ) - .addOptionalParam( - "configpath", - "Path to Functions request config file", - `${__dirname}/../../Functions-request-config.js`, - types.string - ) - .setAction(async (taskArgs, hre) => { - // Simulation can only be conducted on a local fork of the blockchain - if (network.name !== "hardhat") { - throw Error('Simulated requests can only be conducted using --network "hardhat"') - } - - // Check to see if the maximum gas limit has been exceeded - const gasLimit = parseInt(taskArgs.gaslimit ?? "100000") - if (gasLimit > 300000) { - throw Error("Gas limit must be less than or equal to 300,000") - } - - // Recompile the latest version of the contracts - console.log("\n__Compiling Contracts__") - await run("compile") - - // Deploy a mock oracle & registry contract to simulate a fulfillment - const { oracle, registry, linkToken } = await deployMockOracle() - // Deploy the client contract - const clientFactory = await ethers.getContractFactory("FunctionsConsumer") - const client = await clientFactory.deploy(oracle.address) - await client.deployTransaction.wait(1) - - const accounts = await ethers.getSigners() - const deployer = accounts[0] - // Add the wallet initiating the request to the oracle allowlist to authorize a simulated fulfillment - const allowlistTx = await oracle.addAuthorizedSenders([deployer.address]) - await allowlistTx.wait(1) - - // Create & fund a subscription - const createSubscriptionTx = await registry.createSubscription() - const createSubscriptionReceipt = await createSubscriptionTx.wait(1) - const subscriptionId = createSubscriptionReceipt.events[0].args["subscriptionId"].toNumber() - const juelsAmount = ethers.utils.parseUnits("10") - await linkToken.transferAndCall( - registry.address, - juelsAmount, - ethers.utils.defaultAbiCoder.encode(["uint64"], [subscriptionId]) - ) - // Authorize the client contract to use the subscription - await registry.addConsumer(subscriptionId, client.address) - - // Build the parameters to make a request from the client contract - const unvalidatedRequestConfig = require(path.isAbsolute(taskArgs.configpath) - ? taskArgs.configpath - : path.join(process.cwd(), taskArgs.configpath)) - const requestConfig = getRequestConfig(unvalidatedRequestConfig) - // Fetch the mock DON public key - const DONPublicKey = await oracle.getDONPublicKey() - // Remove the preceding 0x from the DON public key - requestConfig.DONPublicKey = DONPublicKey.slice(2) - const request = await buildRequest(requestConfig) - - // Make a request & simulate a fulfillment - await new Promise(async (resolve) => { - // Initiate the request from the client contract - const clientContract = await clientFactory.attach(client.address) - const requestTx = await clientContract.executeRequest( - request.source, - request.secrets ?? [], - request.args ?? [], - subscriptionId, - gasLimit - ) - const requestTxReceipt = await requestTx.wait(1) - const requestId = requestTxReceipt.events[2].args.id - const requestGasUsed = requestTxReceipt.gasUsed.toString() - - // Simulating the JavaScript code locally - console.log("\nExecuting JavaScript request source code locally...") - - const { success, result, resultLog } = await simulateRequest(requestConfig) - console.log(`\n${resultLog}`) - - // Simulate a request fulfillment - const accounts = await ethers.getSigners() - const dummyTransmitter = accounts[0].address - const dummySigners = Array(31).fill(dummyTransmitter) - let i = 0 - try { - const fulfillTx = await registry.fulfillAndBill( - requestId, - success ? result : "0x", - success ? "0x" : result, - dummyTransmitter, - dummySigners, - 4, - 100_000, - 500_000, - { - gasLimit: 500_000, - } - ) - await fulfillTx.wait(1) - } catch (fulfillError) { - // Catch & report any unexpected fulfillment errors - console.log("\nUnexpected error encountered when calling fulfillRequest in client contract.") - console.log(fulfillError) - resolve() - } - - // Listen for the OCRResponse event & log the simulated response returned to the client contract - client.on("OCRResponse", async (eventRequestId, result, err) => { - console.log("__Simulated On-Chain Response__") - if (eventRequestId !== requestId) { - throw new Error(`${eventRequestId} is not equal to ${requestId}`) - } - // Check for & log a successful request - if (result !== "0x") { - console.log( - `Response returned to client contract represented as a hex string: ${result}\n${getDecodedResultLog( - requestConfig, - result - )}` - ) - } - // Check for & log a request that returned an error message - if (err !== "0x") { - console.log(`Error message returned to client contract: "${Buffer.from(err.slice(2), "hex")}"\n`) - } - }) - - // Listen for the BillingEnd event & log the estimated billing data - registry.on( - "BillingEnd", - async ( - eventRequestId, - eventSubscriptionId, - eventSignerPayment, - eventTransmitterPayment, - eventTotalCost, - eventSuccess - ) => { - if (requestId == eventRequestId) { - // Check for a successful request & log a message if the fulfillment was not successful - if (!eventSuccess) { - console.log( - "\nError encountered when calling fulfillRequest in client contract.\n" + - "Ensure the fulfillRequest function in the client contract is correct and the --gaslimit is sufficient.\n" - ) - } - - const fulfillGasUsed = await getGasUsedForFulfillRequest(success, result) - console.log(`Gas used by sendRequest: ${requestGasUsed}`) - console.log(`Gas used by client callback function: ${fulfillGasUsed}`) - return resolve() - } - } - ) - }) - }) - -const getGasUsedForFulfillRequest = async (success, result) => { - const accounts = await ethers.getSigners() - const deployer = accounts[0] - const simulatedRequestId = "0x0000000000000000000000000000000000000000000000000000000000000001" - - const clientFactory = await ethers.getContractFactory("FunctionsConsumer") - const client = await clientFactory.deploy(deployer.address) - client.addSimulatedRequestId(deployer.address, simulatedRequestId) - await client.deployTransaction.wait(1) - - let txReceipt - if (success) { - txReceipt = await client.handleOracleFulfillment(simulatedRequestId, result, []) - } else { - txReceipt = await client.handleOracleFulfillment(simulatedRequestId, [], result) - } - const txResult = await txReceipt.wait(1) - - return txResult.gasUsed.toString() -} - -const deployMockOracle = async () => { - // Deploy mocks: LINK token & LINK/ETH price feed - const linkTokenFactory = await ethers.getContractFactory("LinkToken") - const linkPriceFeedFactory = await ethers.getContractFactory("MockV3Aggregator") - const linkToken = await linkTokenFactory.deploy() - const linkPriceFeed = await linkPriceFeedFactory.deploy(0, ethers.BigNumber.from(5021530000000000)) - // Deploy proxy admin - await upgrades.deployProxyAdmin() - // Deploy the oracle contract - const oracleFactory = await ethers.getContractFactory("contracts/dev/functions/FunctionsOracle.sol:FunctionsOracle") - const oracleProxy = await upgrades.deployProxy(oracleFactory, [], { - kind: "transparent", - }) - await oracleProxy.deployTransaction.wait(1) - // Set the secrets encryption public DON key in the mock oracle contract - await oracleProxy.setDONPublicKey("0x" + SHARED_DON_PUBLIC_KEY) - // Deploy the mock registry billing contract - const registryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registryProxy = await upgrades.deployProxy( - registryFactory, - [linkToken.address, linkPriceFeed.address, oracleProxy.address], - { - kind: "transparent", - } - ) - await registryProxy.deployTransaction.wait(1) - // Set registry configuration - const config = { - maxGasLimit: 300_000, - stalenessSeconds: 86_400, - gasAfterPaymentCalculation: 39_173, - weiPerUnitLink: ethers.BigNumber.from("5000000000000000"), - gasOverhead: 519_719, - requestTimeoutSeconds: 300, - } - await registryProxy.setConfig( - config.maxGasLimit, - config.stalenessSeconds, - config.gasAfterPaymentCalculation, - config.weiPerUnitLink, - config.gasOverhead, - config.requestTimeoutSeconds - ) - // Set the current account as an authorized sender in the mock registry to allow for simulated local fulfillments - const accounts = await ethers.getSigners() - const deployer = accounts[0] - await registryProxy.setAuthorizedSenders([oracleProxy.address, deployer.address]) - await oracleProxy.setRegistry(registryProxy.address) - return { oracle: oracleProxy, registry: registryProxy, linkToken } -} diff --git a/contracts/tasks/deploy-game.js b/contracts/tasks/deploy-game.js index 5c83e88..b09578f 100644 --- a/contracts/tasks/deploy-game.js +++ b/contracts/tasks/deploy-game.js @@ -1,26 +1,55 @@ -const { networks } = require("../networks") -const { addClientConsumerToSubscription } = require("./Functions-billing/add") -const { getRequestConfig } = require("../FunctionsSandboxLibrary") -const { generateRequest } = require("./Functions-client/buildRequestJSON") const path = require("path") const process = require("process") +const { networks } = require("../networks") +const { SubscriptionManager, SecretsManager, createGist } = require("@chainlink/functions-toolkit") + +const generateEncryptedGist = async (secrets, githubApiToken, networkConfig) => { + const provider = new ethers.providers.JsonRpcProvider(networkConfig.url) + const signer = new ethers.Wallet(networkConfig.accounts[0], provider) + + const secretsManager = new SecretsManager({ + signer: signer, + functionsRouterAddress: networkConfig.functionsRouter, + donId: networkConfig.functionsDonId, + }) + await secretsManager.initialize() + + const encryptedSecretsObj = await secretsManager.encryptSecrets(secrets) + + console.log(`Creating gist...`) + if (!githubApiToken) throw new Error("githubApiToken not provided - check your environment variables") + + const gistURL = await createGist(githubApiToken, JSON.stringify(encryptedSecretsObj)) + console.log(`Gist created ${gistURL}`) + const encryptedSecretsUrls = await secretsManager.encryptSecretsUrls([gistURL]) + + return encryptedSecretsUrls +} + +const addConsumerToSubscription = async (subscriptionId, consumerAddress, networkConfig) => { + const provider = new ethers.providers.JsonRpcProvider(networkConfig.url) + const signer = new ethers.Wallet(networkConfig.accounts[0], provider) + + const subscriptionManager = new SubscriptionManager({ + signer, + linkTokenAddress: networkConfig.linkToken, + functionsRouterAddress: networkConfig.functionsRouter, + }) + await subscriptionManager.initialize() + + const addConsumerTxReceipt = await subscriptionManager.addConsumer({ + subscriptionId, + consumerAddress, + }) + console.log(`\nConsumer added to Functions subscription ${subscriptionId}`) + + return addConsumerTxReceipt +} task("deploy-game", "Deploys the SportsPredictionGame contract") .addParam("subid", "Billing subscription ID used to pay for Functions requests") .addParam("destination", "Destination chain for winnings transfer", "avalancheFuji") .addOptionalParam("verify", "Set to true to verify client contract", false, types.boolean) - .addOptionalParam( - "gaslimit", - "Maximum amount of gas that can be used to call fulfillRequest in the client contract", - 250000, - types.int - ) - .addOptionalParam( - "simulate", - "Flag indicating if simulation should be run before making an on-chain request", - true, - types.boolean - ) .addOptionalParam( "configpath", "Path to Functions request config file", @@ -34,10 +63,6 @@ task("deploy-game", "Deploys the SportsPredictionGame contract") ) } - if (taskArgs.gaslimit > 300000) { - throw Error("Gas limit must be less than or equal to 300,000") - } - console.log(`Deploying SportsPredictionGame contract to ${network.name}`) console.log("\n__Compiling Contracts__") @@ -47,14 +72,21 @@ task("deploy-game", "Deploys the SportsPredictionGame contract") const networkConfig = networks[network.name] const destinationChainConfig = networks[destinationChain] - const unvalidatedRequestConfig = require(path.isAbsolute(taskArgs.configpath) + const requestConfig = require(path.isAbsolute(taskArgs.configpath) ? taskArgs.configpath : path.join(process.cwd(), taskArgs.configpath)) - const requestConfig = getRequestConfig(unvalidatedRequestConfig) - const request = await generateRequest(requestConfig, taskArgs) + + const encryptedSecrets = await generateEncryptedGist( + requestConfig.secrets, + process.env.GITHUB_API_TOKEN, + networkConfig + ) + + const donIdBytes32 = ethers.utils.formatBytes32String(networkConfig.functionsDonId) const deployParams = { - oracle: networkConfig["functionsOracleProxy"], + oracle: networkConfig.functionsRouter, + donId: donIdBytes32, ccipRouter: networkConfig.ccipRouter, link: networkConfig.linkToken, weth9Token: networkConfig.weth9, @@ -62,17 +94,16 @@ task("deploy-game", "Deploys the SportsPredictionGame contract") uniswapV3Router: networkConfig.uniswapV3Router, destinationChainSelector: destinationChainConfig.ccipChainSelector, subscriptionId: taskArgs.subid, - gasLimit: taskArgs.gaslimit, - secrets: request.secrets, - source: request.source, + secrets: encryptedSecrets, + source: requestConfig.source, } const gameContractFactory = await ethers.getContractFactory("SportsPredictionGame") const gameContract = await gameContractFactory.deploy(deployParams) - console.log(`\SportsPredictionGame contract deployed to ${gameContract.address} on ${network.name}`) + console.log(`\nSportsPredictionGame contract deployed to ${gameContract.address} on ${network.name}`) - await addClientConsumerToSubscription(taskArgs.subid, gameContract.address) + await addConsumerToSubscription(taskArgs.subid, gameContract.address, networkConfig) console.log(`\nDeploying NativeTokenReceiver contract to ${destinationChain}`) diff --git a/contracts/tasks/index.js b/contracts/tasks/index.js index 8a5a816..a8dcaf0 100644 --- a/contracts/tasks/index.js +++ b/contracts/tasks/index.js @@ -1,7 +1,5 @@ -//exports.keepers = require('./automation') -exports.FunctionsClient = require("./Functions-client") -exports.FunctionsBilling = require("./Functions-billing") exports.accounts = require("./accounts") exports.balance = require("./balance") exports.blockNumber = require("./block-number") +exports.simulate = require("./simulate") exports.deployGame = require("./deploy-game") diff --git a/contracts/tasks/simulate.js b/contracts/tasks/simulate.js new file mode 100644 index 0000000..3935325 --- /dev/null +++ b/contracts/tasks/simulate.js @@ -0,0 +1,29 @@ +const path = require("path") +const { simulateScript, decodeResult } = require("@chainlink/functions-toolkit") + +task("functions-simulate", "Executes the JavaScript source code locally") + .addOptionalParam( + "configpath", + "Path to Functions request config file", + `${__dirname}/../Functions-request-config.js`, + types.string + ) + .setAction(async (taskArgs) => { + const requestConfig = require(path.isAbsolute(taskArgs.configpath) + ? taskArgs.configpath + : path.join(process.cwd(), taskArgs.configpath)) + + const { responseBytesHexstring, errorString, capturedTerminalOutput } = await simulateScript(requestConfig) + console.log(`${capturedTerminalOutput}\n`) + if (responseBytesHexstring) { + console.log( + `Response returned by script during local simulation: ${decodeResult( + responseBytesHexstring, + requestConfig.expectedReturnType + ).toString()}\n` + ) + } + if (errorString) { + console.log(`Error returned by simulated script:\n${errorString}\n`) + } + }) diff --git a/contracts/tasks/utils/artifact.js b/contracts/tasks/utils/artifact.js deleted file mode 100644 index 1cc1dd9..0000000 --- a/contracts/tasks/utils/artifact.js +++ /dev/null @@ -1,203 +0,0 @@ -const path = require("node:path") -const fs = require("node:fs/promises") - -const currentVersion = "0" - -// export interface RequestData { -// type: 'consumer' | 'automatedConsumer' -// automatedConsumerContractAddress?: string; -// requestId?: string; -// taskArgs: { }; -// codeLocation: number; -// codeLanguage: number; -// source: string; -// secrets: { }; -// perNodeSecrets: []; -// secretsURLs: []; -// activeManagedSecretsURLs: boolean; -// args: string[]; -// expectedReturnType: string; -// DONPublicKey: string; -// transactionReceipt: ethers.TransactionReceipt; -// } - -// export interface RequestArtifact extends RequestData { -// createdAt: number; -// lastUpdatedAt: number; -// artifactVersion: string; -// status: 'pending' | 'complete' | 'failed' | 'timed_out' | 'pending_timed_out' -// result: string -// } - -// export interface RequestArtifactUpdateable { -// status: 'pending' | 'complete' | 'failed' | 'timed_out' -// result: string -// error: string -// } - -const DEFAULT_DIRECTORY = ".chainlink_functions" -const REQUEST_TYPE_TO_ID_KEY = { - consumer: "requestId", - automatedConsumer: "automatedConsumerContractAddress", -} - -class RequestStore { - chainId // number; - chainName // string; - requestType // string; - idKey // string; - path // string; - - constructor( - chainId /*: number */, - chainName /*: string */, - requestType /*: string */, - directory = DEFAULT_DIRECTORY - ) { - this.chainId = chainId - this.chainName = chainName - const network = `${chainId}-${chainName}` - if (requestType !== "consumer" && requestType !== "automatedConsumer") - throw new Error("Unsupported request type, must be one of: consumer, automatedConsumer") - this.requestType = requestType - this.idKey = REQUEST_TYPE_TO_ID_KEY[requestType] - this.path = path.join(directory, network, requestType) - } - - async create(data /*: RequestData*/) /*: Promise */ { - validateDataVersion0(data) - if (await this.exists(data[this.idKey])) { - throw new Error(`Request ${data[this.idKey]} already exists on chain ${this.chainId}`) - } - const contents = toRequestArtifact(data) - await this.writeFile(data[this.idKey], contents) - } - - async read(id /*: string*/) /*: Promise*/ { - const data = JSON.parse(await this.readFile(id)) - return validateRequestArtifactVersion(data) - } - - async update(id /*: string*/, data /*: Fragment*/) /*: Promise */ { - if (!(await this.exists(id))) { - throw new Error(`Request ${id} not found on chain ${this.chainId}`) - } - const previousData = await this.read(id) - // NOTE: This is a shallow merge - const mergedData = { ...previousData, ...data, lastUpdatedAt: Date.now() } - await this.writeFile(id, mergedData) - } - - async upsert(id /*: string*/, data /*: Fragment*/) /*: Promise */ { - let created = false - let previousData = {} - let newData = data - try { - previousData = await this.read(id) - } catch { - created = true - newData = toRequestArtifact(data) - } - // NOTE: This is a shallow merge - const mergedData = { ...previousData, ...newData, lastUpdatedAt: Date.now() } - validateDataVersion0(mergedData) - await this.writeFile(id, mergedData) - return created - } - - async delete(id /*: string*/) /*: Promise */ { - if (!(await this.exists(id))) { - throw new Error(`Request ${id} not found on chain ${this.chainId}`) - } - await this.deleteFile(id) - } - - /* private */ async exists(name, fileType = ".json") /*: Promise */ { - const location = path.join(this.path, `${name}${fileType}`) - try { - await fs.access(location) - return true - } catch (e) { - return false - } - } - - /* private */ async readFile(name /*: string*/, fileType = ".json") /*: Promise*/ { - const location = path.join(this.path, `${name}${fileType}`) - return await fs.readFile(location, "utf8") - } - - /* private */ async writeFile(name, data, fileType = ".json") /*: Promise*/ { - await this.validatePath() - const location = path.join(this.path, `${name}${fileType}`) - const contents = JSON.stringify(data, null, 2) + "\n" - await fs.writeFile(location, contents) - } - - /* private */ async deleteFile(name, fileType = ".json") /*: Promise*/ { - const location = path.join(this.path, `${name}${fileType}`) - await fs.unlink(location) - } - - /* private */ async findLatestRequest() /*: Promise*/ { - const files = await orderReccentFiles(this.path) - return files.length ? files[0] : undefined - } - - /* private */ async validatePath() /*: Promise*/ { - // Set up folders along the path if they don't already exist - await fs.mkdir(this.path, { recursive: true }, (err) => { - if (err) throw err - }) - } -} - -async function orderReccentFiles(directory /*: string*/) { - return await fs - .readdir(directory) - .filter((f) => fs.lstat(f).isFile()) - .map((file) => ({ file, mtime: fs.lstat(file).mtime })) - .sort((a, b) => b.mtime.getTime() - a.mtime.getTime()) -} - -function toRequestArtifact(data /*: RequestData*/) /*: RequestArtifact*/ { - const secrets = data.secrets ?? {} - const secretsNoValues = Object.fromEntries(Object.entries(secrets).map(([key, value]) => [key, "[REDACTED]"])) - return { - createdAt: Date.now(), - lastUpdatedAt: Date.now(), - artifactVersion: currentVersion, - status: "pending", - result: null, - error: null, - ...data, - secrets: secretsNoValues, - } -} - -function validateDataVersion0(data /*: RequestData*/) /*: void*/ { - if (typeof data !== "object") throw new Error("Request data must be an object") - if (data.type !== "consumer" && data.type !== "automatedConsumer") - throw new Error("Request data type must be one of: consumer, automatedConsumer") - if (data.type == "consumer" && !data.requestId) throw new Error("Must include field: requestId") - if (data.type == "automatedConsumer" && !data.automatedConsumerContractAddress) - throw new Error("Must include field: automatedConsumerContractAddress") -} - -function validateRequestArtifactVersion(data /*: RequestArtifact*/) { - if (typeof data.artifactVersion !== "string") throw new Error("Required field 'artifactVersion' is missing") - if (Number(data.artifactVersion) < Number(currentVersion)) return migrateRequestArtifactVersion(data) - if (data.artifactVersion === currentVersion) return data - throw new Error(`Unknown version number ${data.artifactVersion}. The latest version is ${currentVersion}`) -} - -function migrateRequestArtifactVersion(data /*: RequestArtifact*/) { - switch (data.artifactVersion) { - case "0": - break - default: - throw new Error(`Unknown version number ${data.artifactVersion}`) - } -} - -module.exports = { RequestStore } diff --git a/contracts/tasks/utils/generateOffchainSecrets.js b/contracts/tasks/utils/generateOffchainSecrets.js deleted file mode 100644 index faa87dc..0000000 --- a/contracts/tasks/utils/generateOffchainSecrets.js +++ /dev/null @@ -1,118 +0,0 @@ -const { encryptWithSignature } = require("../../FunctionsSandboxLibrary/encryptSecrets") - -const generateOffchainSecrets = async (requestConfig, privateKey, DONPublicKey, nodeAddresses, perNodePublicKeys) => { - validateRequestConfig(requestConfig) - - if ( - requestConfig.perNodeSecrets && - requestConfig.perNodeSecrets.length !== nodeAddresses.length && - requestConfig.perNodeSecrets.length !== 0 - ) { - throw Error( - `The number of per-node secrets must match the number of nodes. Length of perNodeSecrets: ${requestConfig.perNodeSecrets.length} Number of nodes: ${nodeAddresses.length}` - ) - } - - const offchainSecrets = {} - - if (requestConfig.perNodeSecrets && Object.keys(requestConfig.perNodeSecrets).length > 0) { - for (let i = 0; i < nodeAddresses.length; i++) { - offchainSecrets[nodeAddresses[i].toLowerCase()] = Buffer.from( - await encryptWithSignature( - process.env.PRIVATE_KEY, - perNodePublicKeys[i].slice(2), - JSON.stringify(requestConfig.perNodeSecrets[i]) - ), - "hex" - ).toString("base64") - } - } - - // if secrets is specified in the config, use those as the default secrets under the 0x0 entry - if (requestConfig.secrets && Object.keys(requestConfig.secrets).length > 0) { - offchainSecrets["0x0"] = Buffer.from( - await encryptWithSignature(privateKey, DONPublicKey.slice(2), JSON.stringify(requestConfig.secrets)), - "hex" - ).toString("base64") - } - - return offchainSecrets -} - -const validateRequestConfig = (requestConfig) => { - // Verify that perNodeSecrets and/or secrets is correctly specified in the config - if (requestConfig.perNodeSecrets && !Array.isArray(requestConfig.perNodeSecrets)) { - throw Error("perNodeSecrets is not correctly specified in config file. It must be an array of objects.") - } - - if (requestConfig.secrets && typeof requestConfig.secrets !== "object") { - throw Error("secrets object is not correctly specified in config file. It must be an object.") - } - - if ( - (!requestConfig.perNodeSecrets || requestConfig.perNodeSecrets.length === 0) && - (!requestConfig.secrets || Object.keys(requestConfig.secrets).length === 0) - ) { - throw Error("Neither perNodeSecrets nor secrets is specified") - } - - const secretsObjectValues = Object.values(requestConfig.secrets) - - if ( - !secretsObjectValues.every((s) => { - return typeof s === "string" - }) - ) { - throw Error("Only string values are supported in secrets objects.") - } - - const globalSecretsObjectKeys = requestConfig.secrets ? Object.keys(requestConfig.secrets).sort() : undefined - - if (!requestConfig.secrets || globalSecretsObjectKeys.length === 0) { - console.log( - "\nWARNING: No global secrets provided. If DON membership changes, the new node will not be able to process requests.\n" - ) - } - - if (requestConfig.perNodeSecrets && requestConfig.perNodeSecrets.length > 0) { - const firstperNodeSecretsKeys = Object.keys(requestConfig.perNodeSecrets[0]).sort() - - for (const assignedSecrets of requestConfig.perNodeSecrets) { - if (typeof assignedSecrets !== "object") { - throw Error("perNodeSecrets is not correctly specified in config file. It must be an array of objects.") - } - - if ( - !Object.values(assignedSecrets).every((s) => { - return typeof s === "string" - }) - ) { - throw Error("Only string values are supported in secrets objects.") - } - - if (Object.keys(assignedSecrets).length === 0) { - throw Error("In the config file, perNodeSecrets contains an empty object.") - } - - const assignedSecretsObjectKeys = Object.keys(assignedSecrets).sort() - - if ( - requestConfig.secrets && - globalSecretsObjectKeys.length > 0 && - JSON.stringify(globalSecretsObjectKeys) !== JSON.stringify(assignedSecretsObjectKeys) - ) { - throw Error( - "In the config file, not all objects in `perNodeSecrets` have the same object keys as `globalSecrets`. (The values can be different, but the keys should be the same between all objects.)" - ) - } - - if (JSON.stringify(firstperNodeSecretsKeys) !== JSON.stringify(assignedSecretsObjectKeys)) { - throw Error( - "In the config file, not all objects in `perNodeSecrets` have the same object keys. (The values can be different, but the keys should be the same between all objects.)" - ) - } - } - } -} - -module.exports = { generateOffchainSecrets } diff --git a/contracts/tasks/utils/github.js b/contracts/tasks/utils/github.js deleted file mode 100644 index e930567..0000000 --- a/contracts/tasks/utils/github.js +++ /dev/null @@ -1,83 +0,0 @@ -const axios = require("axios") - -const createGist = async (githubApiToken, encryptedOffchainSecrets) => { - await checkTokenGistScope(githubApiToken) - - const content = JSON.stringify(encryptedOffchainSecrets) - - const headers = { - Authorization: `token ${githubApiToken}`, - } - - // construct the API endpoint for creating a Gist - const url = "https://api.github.com/gists" - const body = { - public: false, - files: { - [`encrypted-functions-request-data-${Date.now()}.json`]: { - content, - }, - }, - } - - try { - const response = await axios.post(url, body, { headers }) - const gistUrl = response.data.html_url - return gistUrl - } catch (error) { - console.error("Failed to create Gist", error) - throw new Error("Failed to create Gist") - } -} - -const checkTokenGistScope = async (githubApiToken) => { - const headers = { - Authorization: `Bearer ${githubApiToken}`, - } - - const response = await axios.get("https://api.github.com/user", { headers }) - - if (response.status !== 200) { - throw new Error(`Failed to get user data: ${response.status} ${response.statusText}`) - } - // Github's newly-added fine-grained token do not currently allow for verifying that the token scope is restricted to Gists. - // This verification feature only works with classic Github tokens and is otherwise ignored - const scopes = response.headers["x-oauth-scopes"]?.split(", ") - - if (scopes && scopes?.[0] !== "gist") { - throw Error("The provided Github API token does not have permissions to read and write Gists") - } - - if (scopes && scopes.length > 1) { - console.log("WARNING: The provided Github API token has additional permissions beyond reading and writing to Gists") - } - - return true -} - -const deleteGist = async (githubApiToken, gistURL) => { - const headers = { - Authorization: `Bearer ${githubApiToken}`, - } - - const gistId = gistURL.match(/\/([a-fA-F0-9]+)$/)[1] - - try { - const response = await axios.delete(`https://api.github.com/gists/${gistId}`, { headers }) - - if (response.status !== 204) { - throw new Error(`Failed to delete Gist: ${response.status} ${response.statusText}`) - } - - console.log(`\nOff-chain secrets Gist ${gistURL} deleted successfully`) - return true - } catch (error) { - console.error(`Error deleting Gist ${gistURL}`, error.response) - return false - } -} - -module.exports = { - createGist, - deleteGist, -} diff --git a/contracts/tasks/utils/index.js b/contracts/tasks/utils/index.js deleted file mode 100644 index 93975c1..0000000 --- a/contracts/tasks/utils/index.js +++ /dev/null @@ -1,7 +0,0 @@ -module.exports = { - ...require("./network.js"), - ...require("./price.js"), - ...require("./prompt.js"), - ...require("./spin.js"), - ...require("./logger.js"), -} diff --git a/contracts/tasks/utils/logger.js b/contracts/tasks/utils/logger.js deleted file mode 100644 index 28c71f6..0000000 --- a/contracts/tasks/utils/logger.js +++ /dev/null @@ -1,30 +0,0 @@ -const { Console } = require("console") -const { Transform } = require("stream") - -function table(input) { - // @see https://stackoverflow.com/a/67859384 - const ts = new Transform({ - transform(chunk, enc, cb) { - cb(null, chunk) - }, - }) - const logger = new Console({ stdout: ts }) - logger.table(input) - const table = (ts.read() || "").toString() - let result = "" - for (let row of table.split(/[\r\n]+/)) { - let r = row.replace(/[^┬]*┬/, "┌") - r = r.replace(/^├─*┼/, "├") - r = r.replace(/│[^│]*/, "") - r = r.replace(/^└─*┴/, "└") - r = r.replace(/'/g, " ") - result += `${r}\n` - } - console.log(result) -} - -const logger = { table } - -module.exports = { - logger, -} diff --git a/contracts/tasks/utils/network.js b/contracts/tasks/utils/network.js deleted file mode 100644 index 40fe212..0000000 --- a/contracts/tasks/utils/network.js +++ /dev/null @@ -1,25 +0,0 @@ -const BASE_URLS = { - 1: "https://etherscan.io/", - 137: "https://polygonscan.com/", - 43114: "https://snowtrace.io/", - 80001: "https://mumbai.polygonscan.com/", - 11155111: "https://sepolia.etherscan.io/", - 43113: "https://testnet.snowtrace.io/", -} - -/** - * Returns the Etherscan API domain for a given chainId. - * - * @param chainId Ethereum chain ID - */ -function getEtherscanURL(chainId) { - const idNotFound = !Object.keys(BASE_URLS).includes(chainId.toString()) - if (idNotFound) { - throw new Error("Invalid chain Id") - } - return BASE_URLS[chainId] -} - -module.exports = { - getEtherscanURL, -} diff --git a/contracts/tasks/utils/price.js b/contracts/tasks/utils/price.js deleted file mode 100644 index 4a8f178..0000000 --- a/contracts/tasks/utils/price.js +++ /dev/null @@ -1,40 +0,0 @@ -function numberWithCommas(x) { - return x.toString().replace(/\B(? - rl.question(query, (ans) => { - rl.close() - resolve(ans) - }) - ) -} - -async function prompt(query) { - if (!process.env.SKIP_PROMPTS) { - if (query) console.log(`${query}\n`) - const reply = await ask(`${chalk.green("Continue?")} Enter (y) Yes / (n) No\n`) - if (reply.toLowerCase() !== "y" && reply.toLowerCase() !== "yes") { - console.log("Aborted.") - process.exit(1) - } - } -} - -async function promptTxCost(gasEstimate, hre, skipPrompt = false) { - const { lastBaseFeePerGas, maxPriorityFeePerGas } = await hre.ethers.provider.getFeeData() - - const transactionEstimateNative = hre.ethers.utils.formatUnits( - gasEstimate.mul(maxPriorityFeePerGas.add(lastBaseFeePerGas)), - network.config.nativeCurrencyDecimals - ) - const signer = await hre.ethers.getSigner() - const nativePriceUSD = await getPriceUSD(network.config.linkPriceFeed, hre.ethers) - const transactionEstimateUSD = transactionEstimateNative * nativePriceUSD - - console.log(`Estimating cost if the current gas price remains the same...\n`) - - console.log(`The transaction to initiate this request will charge the wallet (${signer.address}):`) - console.log( - `${chalk.blue(transactionEstimateNative + " " + network.config.nativeCurrencySymbol)}, which ${ - network.config.mainnet ? "" : "(using mainnet value) " - }is $${transactionEstimateUSD}\n` - ) - - if (skipPrompt) return - - await prompt() -} - -module.exports = { - ask, - prompt, - promptTxCost, -} diff --git a/contracts/tasks/utils/spin.js b/contracts/tasks/utils/spin.js deleted file mode 100644 index ce3f732..0000000 --- a/contracts/tasks/utils/spin.js +++ /dev/null @@ -1,11 +0,0 @@ -const ora = require("ora") - -function spin(config = {}) { - const spinner = ora({ spinner: "dots2", ...config }) - spinner.start() - return spinner -} - -module.exports = { - spin, -} diff --git a/contracts/test/integration/ResultsConsumer.spec.js b/contracts/test/integration/ResultsConsumer.spec.js deleted file mode 100644 index d9ba575..0000000 --- a/contracts/test/integration/ResultsConsumer.spec.js +++ /dev/null @@ -1,149 +0,0 @@ -const { ethers } = require("hardhat") -const { expect } = require("chai") -const { simulateRequest, getRequestConfig } = require("../../FunctionsSandboxLibrary") -const { generateRequest } = require("../../tasks/Functions-client/buildRequestJSON") -const { SHARED_DON_PUBLIC_KEY } = require("../../networks") -const requestConfigBase = require("../../Functions-request-config") - -describe("ResultsConsumer Integration Tests", async function () { - let mockResultsConsumer, registry, accounts, deployer - - beforeEach(async function () { - accounts = await ethers.getSigners() - deployer = accounts[0] - - // Deploy a mock oracle & registry contract to simulate a fulfillment - const chainlink = await deployMockOracle() - const { oracle, linkToken } = chainlink - registry = chainlink.registry - - // Add the wallet initiating the request to the oracle allowlist to authorize a simulated fulfillment - const allowlistTx = await oracle.addAuthorizedSenders([deployer.address]) - await allowlistTx.wait(1) - - // Create & fund a subscription - const createSubscriptionTx = await registry.createSubscription() - const createSubscriptionReceipt = await createSubscriptionTx.wait(1) - const subscriptionId = createSubscriptionReceipt.events[0].args["subscriptionId"].toNumber() - const juelsAmount = ethers.utils.parseUnits("10") - await linkToken.transferAndCall( - registry.address, - juelsAmount, - ethers.utils.defaultAbiCoder.encode(["uint64"], [subscriptionId]) - ) - - // Generate source & secrets from the request config - const requestConfig = getRequestConfig(requestConfigBase) - const request = await generateRequest(requestConfig, { oracle: oracle.address, simulate: false }) - - // Deploy the client contract - const gasLimit = 300_000 - const mockResultsConsumerFactory = await ethers.getContractFactory("MockResultsConsumer") - mockResultsConsumer = await mockResultsConsumerFactory.deploy( - oracle.address, - subscriptionId, - request.source, - request.secrets, - gasLimit - ) - await mockResultsConsumer.deployTransaction.wait(1) - - // Authorize the client contract to use the subscription - await registry.addConsumer(subscriptionId, mockResultsConsumer.address) - }) - - it("should deploy mock", async function () { - expect(mockResultsConsumer.address).to.be.properAddress - }) - - it("should request and fulfill result", async function () { - const sportId = requestConfigBase.args[0] - const externalId = requestConfigBase.args[1] - const response = ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32) - - const requestTx = await mockResultsConsumer.requestResult(sportId, externalId) - const requestTxReceipt = await requestTx.wait(1) - const requestId = requestTxReceipt.events[2].args.id - - expect(requestTx).to.emit(mockResultsConsumer, "RequestedResult").withArgs(sportId, externalId, requestId) - - expect(await simulateRequestAndFulfill(registry, requestId, requestConfigBase)) - .to.emit(mockResultsConsumer, "ResultProcessed") - .withArgs(sportId, externalId, response) - }) -}) - -const simulateRequestAndFulfill = async (registry, requestId, config) => { - const requestConfig = getRequestConfig(config) - const { success, result } = await simulateRequest(requestConfig) - // Simulate a request fulfillment - const accounts = await ethers.getSigners() - const dummyTransmitter = accounts[0].address - const dummySigners = Array(31).fill(dummyTransmitter) - await registry.fulfillAndBill( - requestId, - success ? result : "0x", - success ? "0x" : result, - dummyTransmitter, - dummySigners, - 4, - 100_000, - 500_000, - { - gasLimit: 500_000, - } - ) -} - -const deployMockOracle = async () => { - // Deploy mocks: LINK token & LINK/ETH price feed - const linkTokenFactory = await ethers.getContractFactory("LinkToken") - const linkPriceFeedFactory = await ethers.getContractFactory("MockV3Aggregator") - const linkToken = await linkTokenFactory.deploy() - const linkPriceFeed = await linkPriceFeedFactory.deploy(0, ethers.BigNumber.from(5021530000000000)) - // Deploy proxy admin - await upgrades.deployProxyAdmin() - // Deploy the oracle contract - const oracleFactory = await ethers.getContractFactory("contracts/dev/functions/FunctionsOracle.sol:FunctionsOracle") - const oracleProxy = await upgrades.deployProxy(oracleFactory, [], { - kind: "transparent", - }) - await oracleProxy.deployTransaction.wait(1) - // Set the secrets encryption public DON key in the mock oracle contract - await oracleProxy.setDONPublicKey("0x" + SHARED_DON_PUBLIC_KEY) - // Deploy the mock registry billing contract - const registryFactory = await ethers.getContractFactory( - "contracts/dev/functions/FunctionsBillingRegistry.sol:FunctionsBillingRegistry" - ) - const registryProxy = await upgrades.deployProxy( - registryFactory, - [linkToken.address, linkPriceFeed.address, oracleProxy.address], - { - kind: "transparent", - } - ) - await registryProxy.deployTransaction.wait(1) - // Set registry configuration - const config = { - maxGasLimit: 300_000, - stalenessSeconds: 86_400, - gasAfterPaymentCalculation: 39_173, - weiPerUnitLink: ethers.BigNumber.from("5000000000000000"), - gasOverhead: 519_719, - requestTimeoutSeconds: 300, - } - await registryProxy.setConfig( - config.maxGasLimit, - config.stalenessSeconds, - config.gasAfterPaymentCalculation, - config.weiPerUnitLink, - config.gasOverhead, - config.requestTimeoutSeconds - ) - // Set the current account as an authorized sender in the mock registry to allow for simulated local fulfillments - const accounts = await ethers.getSigners() - const deployer = accounts[0] - await registryProxy.setAuthorizedSenders([oracleProxy.address, deployer.address]) - await oracleProxy.setRegistry(registryProxy.address) - return { oracle: oracleProxy, registry: registryProxy, linkToken } -} diff --git a/contracts/test/unit/SportsPredictionGame.spec.js b/contracts/test/unit/SportsPredictionGame.spec.js index a988a68..019a876 100644 --- a/contracts/test/unit/SportsPredictionGame.spec.js +++ b/contracts/test/unit/SportsPredictionGame.spec.js @@ -63,7 +63,7 @@ describe("SportsPredictionGame Unit Tests", async function () { uniswapV3Router: mockSwapRouter.address, subscriptionId: 123, destinationChainSelector, - gasLimit: 3000000, + donId: ethers.utils.hexZeroPad(ethers.utils.hexlify(1), 32), secrets: ethers.constants.HashZero, source: "...", }) From a787502df5ea3ff2b7a9e9b59713feb7ebe8413b Mon Sep 17 00:00:00 2001 From: imollov Date: Wed, 7 Feb 2024 15:44:34 +0200 Subject: [PATCH 2/5] fix: upgrade deprecated ccip dependencies --- contracts/contracts/ccip/ProgrammableTokenReceiver.sol | 2 +- contracts/contracts/ccip/ProgrammableTokenSender.sol | 4 ++-- contracts/networks.js | 8 ++++---- contracts/package-lock.json | 8 ++++---- contracts/package.json | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/contracts/contracts/ccip/ProgrammableTokenReceiver.sol b/contracts/contracts/ccip/ProgrammableTokenReceiver.sol index 44bdfd1..885df2a 100644 --- a/contracts/contracts/ccip/ProgrammableTokenReceiver.sol +++ b/contracts/contracts/ccip/ProgrammableTokenReceiver.sol @@ -5,7 +5,7 @@ import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/ import {OwnerIsCreator} from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol"; import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; import {CCIPReceiver} from "@chainlink/contracts-ccip/src/v0.8/ccip/applications/CCIPReceiver.sol"; -import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/token/ERC20/IERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @title ProgrammableTokenReceiver abstract contract ProgrammableTokenReceiver is CCIPReceiver, OwnerIsCreator { diff --git a/contracts/contracts/ccip/ProgrammableTokenSender.sol b/contracts/contracts/ccip/ProgrammableTokenSender.sol index 6bf5372..fcfc465 100644 --- a/contracts/contracts/ccip/ProgrammableTokenSender.sol +++ b/contracts/contracts/ccip/ProgrammableTokenSender.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import {IRouterClient} from "@chainlink/contracts-ccip/src/v0.8/ccip/interfaces/IRouterClient.sol"; import {OwnerIsCreator} from "@chainlink/contracts-ccip/src/v0.8/shared/access/OwnerIsCreator.sol"; import {Client} from "@chainlink/contracts-ccip/src/v0.8/ccip/libraries/Client.sol"; -import {IERC20} from "@chainlink/contracts-ccip/src/v0.8/vendor/openzeppelin-solidity/v4.8.0/token/ERC20/IERC20.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"; /// @title ProgrammableTokenSender @@ -137,7 +137,7 @@ abstract contract ProgrammableTokenSender is OwnerIsCreator { tokenAmounts: tokenAmounts, // The amount and type of token being transferred extraArgs: Client._argsToBytes( // Additional arguments, setting gas limit and non-strict sequencing mode - Client.EVMExtraArgsV1({gasLimit: 500_000, strict: false}) + Client.EVMExtraArgsV1({gasLimit: 500_000}) ), // Set the feeToken to a feeTokenAddress, indicating specific asset will be used for fees feeToken: _feeTokenAddress diff --git a/contracts/networks.js b/contracts/networks.js index 4604b4f..52905e0 100644 --- a/contracts/networks.js +++ b/contracts/networks.js @@ -41,9 +41,9 @@ const networks = { nativeCurrencySymbol: "MATIC", linkToken: "0x326C977E6efc84E512bB9C30f76E30c160eD06FB", linkPriceFeed: "0x12162c3E810393dEC01362aBf156D7ecf6159528", // LINK/MATIC - functionsRouter: "0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C", - functionsDonId: "fun-polygon-mumbai-1", - ccipRouter: "0x70499c328e1E2a3c41108bd3730F6670a44595D1", + functionsRouter: '0x6E2dc0F9DB014aE19888F539E59285D2Ea04244C', + functionsDonId: 'fun-polygon-mumbai-1', + ccipRouter: "0x1035CabC275068e0F4b745A29CEDf38E13aF41b1", ccipChainSelector: "12532609583862916517", ccipTestToken: "0xf1E3A5842EeEF51F2967b3F05D45DD4f4205FF40", uniswapV3Router: "0xE592427A0AEce92De3Edee1F18E0157C05861564", @@ -62,7 +62,7 @@ const networks = { linkPriceFeed: "0x79c91fd4F8b3DaBEe17d286EB11cEE4D83521775", // LINK/AVAX functionsRouter: "0xA9d587a00A31A52Ed70D6026794a8FC5E2F5dCb0", functionsDonId: "fun-avalanche-fuji-1", - ccipRouter: "0x554472a2720E5E7D5D3C817529aBA05EEd5F82D8", + ccipRouter: "0xF694E193200268f9a4868e4Aa017A0118C9a8177", ccipChainSelector: "14767482510784806043", ccipTestToken: "0xD21341536c5cF5EB1bcb58f6723cE26e8D8E90e4", uniswapV3Router: "0x6EE6e170636Aee203a4079498361936984ea64B3", diff --git a/contracts/package-lock.json b/contracts/package-lock.json index 6dba6d6..dd824d0 100644 --- a/contracts/package-lock.json +++ b/contracts/package-lock.json @@ -22,7 +22,7 @@ }, "devDependencies": { "@chainlink/contracts": "0.7.1", - "@chainlink/contracts-ccip": "^0.7.6", + "@chainlink/contracts-ccip": "^1.2.1", "@chainlink/functions-toolkit": "^0.2.8", "@ethersproject/abi": "^5.7.0", "@ethersproject/providers": "^5.7.1", @@ -109,9 +109,9 @@ } }, "node_modules/@chainlink/contracts-ccip": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/@chainlink/contracts-ccip/-/contracts-ccip-0.7.6.tgz", - "integrity": "sha512-yNbCBFpLs3R+ALymto9dQYKz3vatnjqYGu1pnMD0i2fHEMthiXe0+otaNCGNht6n8k7ruNaA0DNpz3F+2jHQXw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@chainlink/contracts-ccip/-/contracts-ccip-1.2.1.tgz", + "integrity": "sha512-8lVod5Gclx25ZSLqX40zzhMwN7unnvj9AMKOE/LYIP5DjyiTDs/3BeXTw6GakeIkQF5v3FILnMIz8emF5FdSpQ==", "dev": true, "dependencies": { "@eth-optimism/contracts": "^0.5.21", diff --git a/contracts/package.json b/contracts/package.json index e2257e5..77ab365 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@chainlink/contracts": "0.7.1", - "@chainlink/contracts-ccip": "^0.7.6", + "@chainlink/contracts-ccip": "^1.2.1", "@chainlink/functions-toolkit": "^0.2.8", "@ethersproject/abi": "^5.7.0", "@ethersproject/providers": "^5.7.1", From 1c1d3c392752222488c486945792b4a1165e459b Mon Sep 17 00:00:00 2001 From: imollov Date: Wed, 7 Feb 2024 16:13:31 +0200 Subject: [PATCH 3/5] docs(readme): add deployment info --- contracts/README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/contracts/README.md b/contracts/README.md index a0085bb..ba05ecf 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -45,7 +45,16 @@ All required configuration for supported networks is located in the [`networks.j - Your wallet has a sufficient native token balance on both networks for the deployment and transaction fees. - Yoyr wallet LINK balance is sufficient to fund the contract so it can pay the CCIP transfer fees. The amount is specified in the `networks.js` file under each network `fundAmount` property. - `_API_KEY` is set if using `--verify true`, depending on which network is used.

-8. In order for the registered games in the contract to be updated with results from the sports API automatically, the game contract must be registered as an upkeep. Follow the steps at https://automation.chain.link/new.
**Note**: Make sure the gas limit is set to 1,000,000.

+8. In order for the registered games in the contract to be updated with results from the sports API automatically, the game contract must be registered as an upkeep. Follow the steps at [https://automation.chain.link](https://automation.chain.link).
**Note**: Make sure the gas limit is set to 1,000,000.

+ +## Deployments + +Polygon Mumbai + +- Game contract: [`0x837acF842c9D99004A4b4fa1C250Fe3ca0c3ce63`](https://mumbai.polygonscan.com/address/0x837acF842c9D99004A4b4fa1C250Fe3ca0c3ce63) +- Functions subscription: [`1328`](https://functions.chain.link/mumbai/1328) +- Automation upkeep: [`108769264368126231420985110258970292714464487200997462556194899554304873592241`](https://automation.chain.link/mumbai/108769264368126231420985110258970292714464487200997462556194899554304873592241) +- CCIP Token Receiver contract on Avalanche Fuji: [`0x812F700f90348E4b297ED8818CBda1ddCf7F25eC`](https://testnet.snowtrace.io/address/0x812F700f90348E4b297ED8818CBda1ddCf7F25eC) --- From 6cc33e4b9e34aa3f60747c92a3b7eec5ed282fed Mon Sep 17 00:00:00 2001 From: imollov Date: Fri, 9 Feb 2024 14:44:41 +0200 Subject: [PATCH 4/5] fix(app): test mode game register --- app/src/components/place-bet-button.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/components/place-bet-button.tsx b/app/src/components/place-bet-button.tsx index 4cfda0a..eca386b 100644 --- a/app/src/components/place-bet-button.tsx +++ b/app/src/components/place-bet-button.tsx @@ -1,6 +1,7 @@ 'use client' import { useState } from 'react' +import { useSearchParams } from 'next/navigation' import { formatEther, parseEther } from 'viem' import { useAccount, useBalance } from 'wagmi' import { @@ -28,6 +29,9 @@ export default function PlaceBetButton({ const { data } = useBalance({ address }) const { predictions, setPredictions } = useLocalStateContext() + const searchParams = useSearchParams() + const testMode = searchParams.get('mode') === 'test' + const placePredictions = async () => { setError(null) if (predictions.some((p) => p.wager === undefined || p.wager <= 0)) { @@ -79,6 +83,10 @@ export default function PlaceBetButton({ args: [gameId], }) if (game.externalId === BigInt(0)) { + // Bypassing the registration guard for test mode + const timestamp = testMode + ? BigInt(Math.floor(Date.now() / 1000) + 300) + : BigInt(prediction.game.timestamp) const config = await prepareWriteContract({ address: contractAddress, abi: sportsPredictionGameABI, @@ -86,7 +94,7 @@ export default function PlaceBetButton({ args: [ BigInt(prediction.game.sportId), BigInt(prediction.game.id), - BigInt(prediction.game.timestamp), + timestamp, winnerToResult[prediction.predictedWinner], ], value: parseEther(`${prediction.wager ?? 0}`), From 6b993fdb44ee721ac4779bc1e37d358db89fd264 Mon Sep 17 00:00:00 2001 From: imollov Date: Mon, 12 Feb 2024 11:26:13 +0200 Subject: [PATCH 5/5] fix(ci): make fsevents optional --- contracts/package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/package.json b/contracts/package.json index 258ff5c..31b8c0c 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -56,6 +56,9 @@ "readline": "^1.3.0", "vm2": "^3.9.18" }, + "optionalDependencies": { + "fsevents": "*" + }, "prettier": { "trailingComma": "es5", "tabWidth": 2,