UNPKG

@witnet/ethers

Version:

Wit/Oracle SDK Framework package for Solidity projects

1,114 lines 179 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.WitOracle = void 0; const ethers_1 = require("ethers"); const sdk_1 = require("@witnet/sdk"); const utils_1 = require("./utils"); class ContractWrapper { constructor(signer, network, abi, target) { this.address = target; this.contract = new ethers_1.Contract(target, abi, signer); this.network = network; this.provider = signer.provider; this.signer = signer; } /** * Name of the underlying logic implementation contract. * @returns Contract name. */ async getEvmImplClass() { return this.contract .getFunction("class()") .staticCall(); } /** * Get specs identifier of the underlying logic implementation contract. * @returns 4-byte hex string. */ async getEvmImplSpecs() { return this.contract .getFunction("specs()") .staticCall(); } /** * Version tag of the underlying logic implementation contract. * @returns Version tag. */ async getEvmImplVersion() { let version; try { version = await this.provider .call({ to: this.address, data: "0x54fd4d50", // funcSig for 'version()' }) .then(result => ethers_1.AbiCoder.defaultAbiCoder().decode(["string"], result)) .then(result => result.toString()); } catch (_err) { return "(immutable)"; } return version; } } class WitArtifactWrapper extends ContractWrapper { constructor(signer, network, artifact, at) { const abis = utils_1.ABIs; const target = at || (0, utils_1.getEvmNetworkAddresses)(network)?.core[artifact]; if (!abis[artifact] || !target) { throw new Error(`EVM network ${network} => artifact is not available: ${artifact}`); } else { super(signer, network, abis[artifact], target); } } } class WitApplianceWrapper extends ContractWrapper { constructor(witOracle, artifact, at) { const abis = utils_1.ABIs; const addresses = (0, utils_1.getEvmNetworkAddresses)(witOracle.network); const target = at || addresses?.core[artifact] || addresses?.apps[artifact]; if (!abis[artifact] || !target) { throw new Error(`EVM network ${witOracle.network} => artifact not available: ${artifact}`); } super(witOracle.signer, witOracle.network, abis[artifact], target); this.witOracle = witOracle; } } /** * Wrapper class for the Wit/Oracle contract as deployed in some specified EVM network. * It provides wrappers to other main artifacts of the Wit/Oracle Framework, as well * as factory methods for wrapping existing `WitOracleRadonRequestTemplate` and `WitOracleConsumer` * compliant contracts, provably bound to the Wit/Oracle core contract. * */ class WitOracle extends WitArtifactWrapper { constructor(signer, network) { super(signer, network, "WitOracle"); } /** * Create a WitOracle attached to the Wit/Oracle main address on the connected EVM network. * Fails if the EVM network served at the specified JSON ETH/RPC endpoint, is not currently bridged * to the Witnet blockchain. * @param url ETH/RPC endpoint URL. * @param signer Specific signer address, other than default, to use for signing EVM transactions. */ static async fromJsonRpcUrl(url, signerId) { const provider = new ethers_1.JsonRpcProvider(url); const signer = await provider.getSigner(signerId); const chainId = Number((await provider.getNetwork()).chainId); const network = (0, utils_1.getEvmNetworkByChainId)(chainId); if (!network) { throw new Error(`WitOracle: unsupported chain id: ${chainId}`); } return new WitOracle(signer, network); } async estimateBaseFee(evmGasPrice) { return this.contract .getFunction("estimateBaseFee(uint256)") .staticCall(evmGasPrice); } async estimateBaseFeeWithCallback(evmGasPrice, evmCallbackGas) { return this.contract .getFunction("estimateBaseFeeWithCallback(uint256,uint24)") .staticCall(evmGasPrice, evmCallbackGas); } async estimateExtraFee(evmGasPrice, evmWitPrice, queryParams) { return this.contract .getFunction("estimateExtraFee(uint256,uint256,(uint16,uint16,uint64)") .staticCall(evmGasPrice, evmWitPrice, (0, utils_1.abiEncodeWitOracleQueryParams)(queryParams)); } async filterWitOracleQueryEvents(options) { const witOracleQueryEvent = this.contract.filters["WitOracleQuery(address indexed,uint256,uint256,uint64,bytes32,(uint16,uint16,uint64))"](options?.where?.evmRequester); return this.contract .queryFilter(witOracleQueryEvent, options.fromBlock, options?.toBlock) .then(logs => logs.filter(log => !log.removed // && (!options?.where?.evmRequester || (log as EventLog).args?.requester === options.where.evmRequester) && (!options?.where?.queryRadHash || log.args?.radonHash.indexOf(options.where.queryRadHash) >= 0))) .then(logs => logs.map(log => ({ evmBlockNumber: BigInt(log.blockNumber), evmRequester: log.args?.evmRequester, evmTransactionHash: log.transactionHash, queryId: BigInt(log.args.queryId), queryRadHash: log.args.radonHash, queryParams: { witnesses: log.args.radonParams[1], unitaryReward: BigInt(log.args.radonParams[2]), resultMaxSize: log.args.radonParams[0], }, }))); } async filterWitOracleReportEvents(options) { const witOracleReportEvent = this.contract.filters.WitOracleReport(options?.where?.evmOrigin, options?.where?.evmConsumer); return this.contract .queryFilter(witOracleReportEvent, options.fromBlock, options?.toBlock) .then(logs => logs.filter(log => !log.removed && (!options?.where?.queryRadHash || log.args?.queryRadHash.indexOf(options.where.queryRadHash) >= 0))) .then(logs => logs.map(log => ({ evmBlockNumber: BigInt(log.blockNumber), evmOrigin: log.args.evmOrigin, evmConsumer: log.args.evmConsumer, evmReporter: log.args.evmReporter, evmTransactionHash: log.transactionHash, witDrTxHash: log.args.witDrTxHash, queryRadHash: log.args.queryRadHash, queryParams: { witnesses: log.args.queryParams[1], unitaryReward: BigInt(log.args.queryParams[2]), resultMaxSize: log.args.queryParams[0], }, resultCborBytes: log.args.resultCborBytes, resultTimestamp: Number(log.args.resultTimestamp), }))); } async getEvmChainId() { return this.provider.getNetwork().then(network => Number(network.chainId)); } async getEvmChannel() { return this.contract .getFunction("channel()") .staticCall(); } async getNextQueryId() { return this.contract .getFunction("getNextQueryId()") .staticCall(); } async getQueryResultStatusDescription(queryId) { let reason; try { try { reason = await this.contract.getQueryResultStatusDescription.staticCall(queryId); } catch { const legacy = new ethers_1.Contract(this.address, [ "function getQueryResultError(uint256) public view returns ((uint8,string))", ], this.signer); reason = await legacy.getQueryResultError.staticCall(queryId).then(result => result[1]); } } catch { reason = "(unparsable error)"; } return reason; } async getQueryStatuses(queryIds) { return this.contract .getQueryStatusBatch .staticCall(queryIds) .then((statuses) => statuses.map(value => (0, utils_1.abiDecodeQueryStatus)(value))); } async getWitOracleConsumerAt(target) { return WitOracleConsumer.at(this, target); } /** * Wrapper class for the Wit/Oracle Radon Registry core contract as deployed in some supported EVM network. * It allows formal verification of Radon Requests and Witnet-compliant data sources into such network, * as to be securely referred on both Wit/Oracle queries pulled from within smart contracts, * or Wit/Oracle query results pushed into smart contracts from offchain workflows. */ async getWitOracleRadonRegistry() { return new WitOracleRadonRegistry(this.signer, this.network); } /** * Wrapper class for the Wit/Oracle Request Factory core contract as deployed in some supported EVM network. * It allows construction of `WitOracleRadonRequestTemplate` minimal-proxy contracts out of one ore more * parameterized Radon Retievals (Witnet-compliant data sources). Template addresses are counter-factual to * the set of data sources they are built on. */ async getWitOracleRadonRequestFactory() { return WitOracleRadonRequestFactory.deployed(this, await this.getWitOracleRadonRegistry()); } /** * Wrapper class for Wit/Oracle Radon Template artifacts as deployed in some supported EVM network. * `IWitOracleRadonRequestTemplate` contracts enable smart contracts to formally verify Radon Requests * built out out of a set of parameterized Witnet-compliant data sources, on the fly. */ async getWitOracleRadonRequestTemplateAt(target) { return WitOracleRadonRequestTemplate.at(this, target); } /** * Wrapper class for Wit/Oracle Radon Modal artifacts as deployed in some supported EVM network. * `IWitOracleRadonRequestModal` contracts enable smart contracts to formally verify Radon Requests * built out out of a single Radon Retrieval and multiple data providers, all of them expected to * provided exactly the same data. */ async getWitOracleRadonRequestModalAt(target) { return WitOracleRadonRequestModal.arguments(this, target); } async getWitPriceFeedsAt(target) { return WitPriceFeeds.at(this, target); } async getWitPriceFeedsLegacyAt(target) { return WitPriceFeedsLegacy.at(this, target); } async getWitRandomnessAt(target) { return WitRandomness.at(this, target); } } exports.WitOracle = WitOracle; class WitOracleConsumer extends WitApplianceWrapper { constructor(witOracle, target) { super(witOracle, "WitOracleConsumer", target); } static async at(witOracle, target) { const consumer = new WitOracleConsumer(witOracle, target); const consumerWitOracleAddr = await consumer.contract.witOracle.staticCall(); if (consumerWitOracleAddr !== witOracle.address) { throw new Error(`${this.constructor.name} at ${target}: mismatching Wit/Oracle address (${consumerWitOracleAddr})`); } return consumer; } async pushDataReport(report, options) { return this.contract .pushDataReport .populateTransaction((0, utils_1.abiEncodeDataPushReport)(report), report?.evm_proof) .then(tx => { tx.gasPrice = options?.gasPrice || tx?.gasPrice; tx.gasLimit = options?.gasLimit || tx?.gasLimit; return this.signer.sendTransaction(tx); }) .then(response => { if (options?.onDataPushReportTransaction) options.onDataPushReportTransaction(response.hash); return response.wait(options?.confirmations || 1, options?.timeout); }); } } /** * Wrapper class for the Wit/Oracle Radon Registry core contract as deployed in some supported EVM network. * It allows formal verification of Radon Requests and Witnet-compliant data sources into such network, * as to be securely referred on both Wit/Oracle queries pulled from within smart contracts, * or Wit/Oracle query results pushed into smart contracts from offchain workflows. */ class WitOracleRadonRegistry extends WitArtifactWrapper { constructor(signer, network) { super(signer, network, "WitOracleRadonRegistry"); } /// =========================================================================================================== /// --- IWitOracleRadonRegistry ------------------------------------------------------------------------------- /** * Determines the unique hash that would identify the given Radon Retrieval, if it was * formally verified into the connected EVM network. * @param retrieval Instance of a Radon Retrieval object. */ async determineRadonRetrievalHash(retrieval) { return this.contract .getFunction("verifyRadonRetrieval(uint8,string,string,string[2][],bytes)") .staticCall(...(0, utils_1.abiEncodeRadonAsset)(retrieval)) .then(hash => { return hash.slice(2); }); } /** * Returns information related to some previously verified Radon Request, on the connected EVM network. * @param radHash The RAD hash that uniquely identifies the Radon Request. */ async lookupRadonRequest(radHash) { return this.contract .getFunction("lookupRadonRequestBytecode(bytes32)") .staticCall(`0x${radHash}`) .then(bytecode => sdk_1.Witnet.Radon.RadonRequest.fromBytecode(bytecode)); } /** * Returns the bytecode of some previously verified Radon Request, on the connected EVM network. * @param radHash The RAD hash that uniquely identifies the Radon Request. */ async lookupRadonRequestBytecode(radHash) { return this.contract .getFunction("lookupRadonRequestBytecode(bytes32)") .staticCall(`0x${radHash}`); } /** * Returns information about some previously verified Radon Retrieval on the connected EVM network. * This information includes retrieval the method, URL, body, headers and the Radon script in charge * to transform data before delivery, on the connected EVM network. * @param radHash The RAD hash that uniquely identifies the Radon Request. */ async lookupRadonRetrieval(hash) { return this.contract .getFunction("lookupRadonRetrieval(bytes32)") .staticCall(`0x${hash}`) .then((result) => { return new sdk_1.Witnet.Radon.RadonRetrieval({ method: result[1], url: result[3], body: result[4], headers: Object.fromEntries(result[5]), script: sdk_1.utils.parseRadonScript(result[6]), }); }); } /** * Formally verify the given Radon Request object into the connected EVM network. * It also verifies all the Radon Retrieval scripts (i.e. data source) the Request * relies on, if not yet done before. * * Verifying Radon assets modifies the EVM storage and therefore requires * spending gas in proportion to the number and complexity of the data sources, * and whether these had been previously verified before or not. * * If the given Radon Request happened to be already verified, no gas would be actually consumed. * * @param request Instance of a Radon Request object. * @param options Async EVM transaction handlers. * @returns The RAD hash of the Radon Request, as verified on the connected EVM network. */ async verifyRadonRequest(request, options) { const radHash = request.radHash; await this.lookupRadonRequest(radHash) .catch(async () => { const hashes = []; for (const index in request.sources) { const retrieval = request.sources[index]; hashes.push(`0x${await this.verifyRadonRetrieval(retrieval, options)}`); } const aggregate = (0, utils_1.abiEncodeRadonAsset)(request.sourcesReducer); const tally = (0, utils_1.abiEncodeRadonAsset)(request.witnessReducer); if (options?.onVerifyRadonRequest) { options.onVerifyRadonRequest(radHash); } await this.contract .getFunction("verifyRadonRequest(bytes32[],(uint8,(uint8,bytes)[]),(uint8,(uint8,bytes)[]))") .send(hashes, aggregate, tally) .then(async (tx) => { const receipt = await tx.wait(options?.confirmations || 1); if (options?.onVerifyRadonRequestReceipt) { options.onVerifyRadonRequestReceipt(receipt); } }); }); return radHash; } /** * Formally verify the given Radon Retrieval script (i.e. data source), into the connected EVM network. * * Verifying Radon assets modifies the EVM storage and therefore requires * spending gas in proportion to the size of the data source parameters (e.g. URL, body, headers, or Radon script). * * If the given Radon Retrieval object happened to be already verified, no EVM gas would be actually consumed. * * @param request Instance of a Radon Retrieval object. * @param options Async EVM transaction handlers. * @returns The unique hash of the Radon Retrieval object, as verified on the connected EVM network. */ async verifyRadonRetrieval(retrieval, options) { return this.determineRadonRetrievalHash(retrieval) .then(async (hash) => { await this.lookupRadonRetrieval(hash) .catch(async () => { if (options?.onVerifyRadonRetrieval) { options.onVerifyRadonRetrieval(hash); } await this.contract .getFunction("verifyRadonRetrieval(uint8,string,string,string[2][],bytes)") .send(...(0, utils_1.abiEncodeRadonAsset)(retrieval)) .then(async (tx) => { const receipt = await tx.wait(options?.confirmations || 1); if (options?.onVerifyRadonRetrievalReceipt) { options.onVerifyRadonRetrievalReceipt(receipt); } }); }); return hash; }); } } class WitOracleRadonRequestFactory extends WitApplianceWrapper { constructor(witOracle, registry, at) { super(witOracle, "WitOracleRadonRequestFactory", at); this.registry = registry; } static async deployed(witOracle, registry) { const deployer = new WitOracleRadonRequestFactory(witOracle, registry); const witOracleRegistryAddress = await witOracle.contract.registry.staticCall(); if (registry.address !== witOracleRegistryAddress) { throw new Error(`${this.constructor.name} at ${deployer.address}: uncompliant WitOracleRadonRegistry at ${registry.address})`); } return deployer; } async deployRadonRequestTemplate(template, options) { const hashes = []; for (const index in template.sources) { const retrieval = template.sources[index]; const hash = `0x${await this.registry.determineRadonRetrievalHash(retrieval)}`; await this.registry.verifyRadonRetrieval(retrieval, options); hashes.push(hash); } const aggregator = (0, utils_1.abiEncodeRadonAsset)(template.sourcesReducer); const tally = (0, utils_1.abiEncodeRadonAsset)(template.witnessReducer); const target = await this.contract .getFunction("buildRadonRequestTemplate(bytes32[],(uint8,(uint8,bytes)[]),(uint8,(uint8,bytes)[]))") .staticCall(hashes, aggregator, tally); if (options?.onDeployRadonRequestTemplate) options.onDeployRadonRequestTemplate(target); await this.contract .getFunction("buildRadonRequestTemplate(bytes32[],(uint8,(uint8,bytes)[]),(uint8,(uint8,bytes)[]))") .send(hashes, aggregator, tally) .then(async (tx) => { const receipt = await tx.wait(options?.confirmations || 1); if (options?.onDeployRadonRequestTemplateReceipt) { options.onDeployRadonRequestTemplateReceipt(receipt); } }); return await WitOracleRadonRequestTemplate.at(this.witOracle, target); } async deployRadonRequestModal(modal, options) { const retrieval = [ modal.sources[0].method, modal.sources[0].body || "", modal.sources[0]?.headers ? Object.entries(modal.sources[0].headers) : [], modal.sources[0].script?.toBytecode() || "0x", ]; const tally = (0, utils_1.abiEncodeRadonAsset)(modal.witnessReducer); const target = await this.contract .buildRadonRequestModal //getFunction("buildRadonRequestModal((uint8,string,string[2][],bytes),(uint8,(uint8,bytes)[]))") .staticCall(retrieval, tally); if (options?.onDeployRadonRequestModal) options.onDeployRadonRequestModal(target); await this.contract .buildRadonRequestModal // .getFunction("buildRadonRequestModal((uint8,string,string[2][],bytes),(uint8,(uint8,bytes)[]))") .send(retrieval, tally) .then(async (tx) => { const receipt = await tx.wait(options?.confirmations || 1); if (options?.onDeployRadonRequestModalReceipt) { options.onDeployRadonRequestModalReceipt(receipt); } }); return await WitOracleRadonRequestModal.at(this.witOracle, target); } async verifyRadonRequest(request, _options) { // TODO: // return request.radHash; } } class WitOracleRadonRequestModal extends WitApplianceWrapper { constructor(witOracle, at) { super(witOracle, "IWitOracleRadonRequestModal", at); } static async at(witOracle, target) { const template = new WitOracleRadonRequestModal(witOracle, target); const templateWitOracleAddr = await template.contract.witOracle.staticCall(); if (templateWitOracleAddr !== witOracle.address) { throw new Error(`${this.constructor.name} at ${target}: mismatching Wit/Oracle address (${templateWitOracleAddr})`); } return template; } async getDataResultType() { return this.contract .getFunction("getDataResultType()") .staticCall() .then((result) => { switch (Number(result)) { case 1: return "array"; case 2: return "boolean"; case 3: return "bytes"; case 4: return "integer"; case 5: return "float"; case 6: return "map"; case 7: return "string"; default: return "any"; } }); } async getDataSourcesArgsCount() { return this.contract .getFunction("getDataSourcesArgsCount()") .staticCall() .then((argsCount) => Number(argsCount)); } async getRadonModalRetrieval() { return this.contract .getFunction("getRadonModalRetrieval()") .staticCall() .then((result) => { return new sdk_1.Witnet.Radon.RadonRetrieval({ method: result[1], url: result[3], body: result[4], headers: Object.fromEntries(result[5]), script: sdk_1.utils.parseRadonScript(result[6]), }); }); } async verifyRadonRequest(dataProviders, commonRetrievalArgs, options) { const argsCount = await this.getDataSourcesArgsCount(); if (argsCount != 1 + (commonRetrievalArgs?.length || 0)) { throw TypeError(`${this.constructor.name}@${this.address}: unmatching args count != ${argsCount - 1}.`); } const method = this.contract.getFunction("verifyRadonRequest(string[],string[])"); const radHash = (await method.staticCall(commonRetrievalArgs || [], dataProviders)).slice(2); try { await (await this.witOracle.getWitOracleRadonRegistry()).lookupRadonRequestBytecode(radHash); } catch { if (options?.onVerifyRadonRequest) options.onVerifyRadonRequest(radHash); await method .send(commonRetrievalArgs || [], dataProviders) .then(tx => tx.wait(options?.confirmations || 1)) .then(receipt => { if (options?.onVerifyRadonRequestReceipt) { options.onVerifyRadonRequestReceipt(receipt); } return radHash; }); } return radHash; } } class WitOracleRadonRequestTemplate extends WitApplianceWrapper { constructor(witOracle, at) { super(witOracle, "IWitOracleRadonRequestTemplate", at); } static async at(witOracle, target) { const template = new WitOracleRadonRequestTemplate(witOracle, target); const templateWitOracleAddr = await template.contract.witOracle.staticCall(); if (templateWitOracleAddr !== witOracle.address) { throw new Error(`${this.constructor.name} at ${target}: mismatching Wit/Oracle address (${templateWitOracleAddr})`); } return template; } async getDataResultType() { return this.contract .getFunction("getDataResultType()") .staticCall() .then((result) => { switch (Number(result)) { case 1: return "array"; case 2: return "boolean"; case 3: return "bytes"; case 4: return "integer"; case 5: return "float"; case 6: return "map"; case 7: return "string"; default: return "any"; } }); } async getDataSources() { return this.contract .getFunction("getDataSources()") .staticCall() .then((results) => { return results.map(result => new sdk_1.Witnet.Radon.RadonRetrieval({ method: result[1], url: result[3], body: result[4], headers: Object.fromEntries(result[5]), script: sdk_1.utils.parseRadonScript(result[6]), })); }); } async getDataSourcesArgsCount() { return this.contract .getFunction("getDataSourcesArgsCount()") .staticCall() .then((dims) => dims.map(dim => Number(dim))); } async verifyRadonRequest(args, options) { const argsCount = await this.getDataSourcesArgsCount(); let encodedArgs = []; if (typeof args === 'string') { if (argsCount.length === 1 && argsCount[0] === 1) { encodedArgs = [[args]]; } } else if (Array.isArray(args)) { if (Array.isArray(args[0])) { if (argsCount.length === args.length && !args.find((subargs, index) => subargs.length !== argsCount[index])) { encodedArgs = args; } } else if (args.length === argsCount[0] && !args.find(arg => typeof arg !== 'string')) { encodedArgs = [args]; } } if (encodedArgs.length === 0) { throw TypeError(`${this.constructor.name}@${this.address}: unmatching args count != [${argsCount}, ].`); } const method = this.contract.getFunction("verifyRadonRequest(string[][])"); const radHash = (await method.staticCall(encodedArgs)).slice(2); try { await (await this.witOracle.getWitOracleRadonRegistry()).lookupRadonRequestBytecode(radHash); } catch { if (options?.onVerifyRadonRequest) options.onVerifyRadonRequest(radHash); await method .send(encodedArgs) .then(tx => tx.wait(options?.confirmations || 1)) .then(receipt => { if (options?.onVerifyRadonRequestReceipt) { options.onVerifyRadonRequestReceipt(receipt); } return radHash; }); } return radHash; } } class WitPriceFeeds extends WitApplianceWrapper { constructor(witOracle, at) { super(witOracle, "WitPriceFeeds", at); } static async at(witOracle, target) { const priceFeeds = new WitPriceFeeds(witOracle, target); const oracleAddr = await priceFeeds.contract.witOracle.staticCall(); if (oracleAddr !== witOracle.address) { throw new Error(`${this.constructor.name} at ${target}: mismatching Wit/Oracle address (${oracleAddr})`); } return priceFeeds; } async createChainlinkAggregator(id4, options) { const evmTransaction = await this.contract .createChainlinkAggregator .populateTransaction(id4); evmTransaction.gasPrice = options?.evmGasPrice || evmTransaction?.gasPrice; return this.signer .sendTransaction(evmTransaction) .then(response => { if (options?.onCreateChainlinkAggregatorTransaction) { options.onCreateChainlinkAggregatorTransaction(response.hash); } return response.wait(options?.evmConfirmations || 1, options?.evmTimeout); }) .then(receipt => { if (options?.onCreateChainlinkAggregatorTransactionReceipt) { options.onCreateChainlinkAggregatorTransactionReceipt(receipt); } return receipt; }); } async determineChainlinkAggregatorAddress(id4) { return this.contract .createChainlinkAggregator .staticCall(id4); } async getEvmFootprint() { return this.contract .footprint .staticCall(); } async getPrice(id4, ema) { return this.contract .getPrice .staticCall(id4, ema) .then((result) => ({ delta1000: BigInt(result.conf), exponent: Number(result.expo), timestamp: BigInt(result.publishTime), trackHash: result.track, value: Number(result.price) / 10 ** Number(result.expo), })); } async getPriceNotOlderThan(id4, ema, age) { return this.contract .getPriceNotOlderThan .staticCall(id4, ema, age) .then((result) => ({ delta1000: BigInt(result.conf), exponent: Number(result.expo), timestamp: BigInt(result.publishTime), trackHash: result.track, value: Number(result.price) / 10 ** Number(result.expo), })); } async getPriceUnsafe(id4, ema) { return this.contract .getPriceUnsafe .staticCall(id4, ema) .then((result) => ({ delta1000: BigInt(result.conf), exponent: Number(result.expo), timestamp: BigInt(result.publishTime), trackHash: result.track, value: Number(result.price) / 10 ** Number(result.expo), })); } async isCaptionSupported(caption) { return this.contract .supportsCaption .staticCall(caption); } async lookupPriceFeed(id4) { return this.contract .lookupPriceFeed .staticCall(id4) .then((result) => ({ id: result.id, exponent: Number(result.exponent), symbol: result.symbol, mapper: { algorithm: (0, utils_1.abiDecodePriceFeedMappingAlgorithm)(result.mapper.algo), description: result.mapper.desc, dependencies: result.mapper.deps, }, oracle: { address: result.oracle.addr, name: result.oracle.name, dataBytecode: result.oracle.dataBytecode, dataSources: result.oracle.dataSources, interfaceId: result.oracle.interfaceId, }, updateConditions: { computeEMA: result.computeEma, cooldownSecs: result.cooldownSecs, heartbeatSecs: result.heartbeatSecs, maxDeviation1000: result.maxDeviation100, }, lastUpdate: { delta1000: BigInt(result.conf), exponent: Number(result.expo), timestamp: BigInt(result.publishTime), trackHash: result.track, value: Number(result.price) / 10 ** Number(result.expo), }, })); } async lookupPriceFeedCaption(id4) { return this.contract .lookupSymbol .staticCall(id4); } async lookupPriceFeedExponent(id4) { return this.contract .lookupPriceFeedExponent .staticCall(id4) .then(result => Number(result)); } async lookupPriceFeedID(id4) { return this.contract .lookupPriceFeedID .staticCall(id4); } async lookupPriceFeeds() { return this.contract .lookupPriceFeeds .staticCall() .then(results => results.map((result) => ({ id: result.id, exponent: Number(result.exponent), symbol: result.symbol, mapper: { algorithm: "", // todo: PriceFeedMappers[result.mapper.algo], description: result.mapper.desc, dependencies: result.mapper.deps, }, oracle: { address: result.oracle.addr, name: result.oracle.name, dataBytecode: result.oracle.dataBytecode, dataSources: result.oracle.dataSources, interfaceId: result.oracle.interfaceId, }, updateConditions: { computeEMA: result.computeEma, cooldownSecs: result.cooldownSecs, heartbeatSecs: result.heartbeatSecs, maxDeviation1000: result.maxDeviation100, }, lastUpdate: { delta1000: BigInt(result.conf), exponent: Number(result.expo), timestamp: BigInt(result.publishTime), trackHash: result.track, value: BigInt(result.price), }, }))); } } class WitPriceFeedsLegacy extends WitApplianceWrapper { constructor(witOracle, at) { super(witOracle, "WitPriceFeedsLegacy", at); } static async at(witOracle, target) { const priceFeeds = new WitPriceFeedsLegacy(witOracle, target); const oracleAddr = await priceFeeds.contract.witnet.staticCall(); if (oracleAddr !== witOracle.address) { throw new Error(`WitPriceFeedsLegacy at ${target}: mismatching Wit/Oracle address (${oracleAddr})`); } return priceFeeds; } async getEvmFootprint() { return this.contract .footprint .staticCall(); } // public async getPrice(id4: Witnet.HexString): Promise<PriceFeedUpdate> { // return this.contract // .latestPrice // .staticCall(id4) // .then((result: any) => ({ // timestamp: BigInt(result?.timestamp), // trackHash: result?.drTxHash, // value: Number(result?.value) / 10 ** exponent, // })) // } async isCaptionSupported(caption) { return this.contract .supportsCaption .staticCall(caption); } // public async lookupPriceFeed(id4: Witnet.HexString): Promise<PriceFeed> { // return this.contract // .lookupPriceFeed // .staticCall(id4) // .then((result: any) => ({ // id: result.id, // exponent: Number(result.exponent), // symbol: result.symbol, // mapper: { // algorithm: abiDecodePriceFeedMappingAlgorithm(result.mapper.algo), // description: result.mapper.desc, // dependencies: result.mapper.deps, // }, // oracle: { // address: result.oracle.addr, // name: result.oracle.name, // dataSources: result.oracle.dataSources, // interfaceId: result.oracle.interfaceId, // }, // updateConditions: { // computeEMA: result.computeEma, // cooldownSecs: result.cooldownSecs, // heartbeatSecs: result.heartbeatSecs, // maxDeviation1000: result.maxDeviation100, // }, // lastUpdate: { // delta1000: BigInt(result.conf), // exponent: Number(result.expo), // timestamp: BigInt(result.publishTime), // trackHash: result.track, // value: BigInt(result.price), // }, // })) // } async lookupPriceFeedCaption(id4) { return this.contract .lookupCaption .staticCall(id4); } async lookupPriceFeedExponent(id4) { return this.contract .lookupDecimals .staticCall(id4) .then(result => Number(result)); } async lookupPriceFeeds() { const interfaceId = await this.witOracle.getEvmImplSpecs(); let priceFeeds = await this.contract .supportedFeeds .staticCall() .then(results => { const [id4s, captions, dataSources] = results; return id4s.map((id4, index) => ({ id: id4, exponent: Number(captions[index].split('-').slice(-1)[0]), symbol: captions[index], //.split('-').slice(1, -1).join('-'), oracle: { address: this.witOracle.address, name: "WitOracle", dataSources: dataSources[index], interfaceId, } })); }); let latestPrices = await this.contract .latestPrices .staticCall(priceFeeds.map(pf => pf.id)); return priceFeeds.map((pf, index) => ({ ...pf, lastUpdate: { timestamp: latestPrices[index].timestamp, trackHash: latestPrices[index].drTxHash, value: Number(latestPrices[index].value) / 10 ** pf.exponent } })); } } class WitRandomness extends WitApplianceWrapper { constructor(witOracle, at) { super(witOracle, "WitRandomness", at); this._legacy = new ethers_1.Contract(at, [ "function fetchRandomnessAfter(uint256) public view returns (bytes32)", "function fetchRandomnessAfterProof(uint256) public view returns ((bytes32,uint64,bytes32,uint256))", "function getRandomizePrevBlock(uint256) public view returns (uint256)", "function getRandomizeStatus(uint256) public view returns (uint8)", "function isRandomized(uint256) public view returns (bool)", ], this.signer); } static async at(witOracle, target) { const randomizer = new WitRandomness(witOracle, target); try { let oracleAddr; try { oracleAddr = await randomizer.contract.witOracle.staticCall(); } catch { const abi = ["function witnet() public view returns (address)",]; const contract = new ethers_1.Contract(target, abi, randomizer.signer); oracleAddr = await contract.witnet.staticCall(); } if (oracleAddr !== witOracle.address) { throw new Error(`WitRandomness at ${target}: mismatching Wit/Oracle address (${oracleAddr})`); } } catch (error) { throw new Error(`WitRandomness at ${target}: cannot fetch Wit/Oracle address\n${error?.stack?.split('\n')[0] || error}`); } return randomizer; } async estimateRandomizeFee(evmGasPrice) { return this.contract .getFunction("estimateRandomizeFee(uint256)") .staticCall(evmGasPrice); } async getRandomnessAfter(evmBlockNumber) { if (await this.isRandomized(evmBlockNumber)) { let randomness; try { try { randomness = await this.contract.fetchRandomnessAfter.staticCall(evmBlockNumber); } catch { randomness = await this._legacy.fetchRandomnessAfter.staticCall(evmBlockNumber); } } catch (error) { throw new Error(`${this.constructor.name}: cannot fetch randomness on block ${evmBlockNumber}.\n${error?.stack?.split('\n')[0] || error}`); } return randomness; } else { return undefined; } } async fetchRandomnessAfterProof(evmBlockNumber) { let result; try { try { result = await this.contract.fetchRandomnessAfterProof.staticCall(evmBlockNumber); } catch { result = await this._legacy .fetchRandomnessAfterProof.staticCall(evmBlockNumber) .then(result => [result[2], result[1]]); } } catch (error) { throw new Error(`${this.constructor.name}: cannot fetch randomness proof on block ${evmBlockNumber}.\n${error?.stack?.split('\n')[0] || error}`); } return result; } async getLastRandomizeBlock() { return this.contract .getFunction("getLastRandomizeBlock()") .staticCall(); } async getRandomizePrevBlock(evmBlockNumber) { let evmPrevBlock; try { try { evmPrevBlock = await this.contract.getRandomizePrevBlock.staticCall(evmBlockNumber).then(result => BigInt(result)); } catch { evmPrevBlock = await this._legacy.getRandomizePrevBlock.staticCall(evmBlockNumber).then(result => BigInt(result)); } } catch (error) { throw new Error(`${this.constructor.name}: cannot fetch previous randomize block before ${evmBlockNumber}.\n${error?.stack?.split('\n')[0] || error}`); } return evmPrevBlock; } async getRandomizeStatus(evmBlockNumber) { let result; try { try { result = await this.contract.getRandomizeStatus.staticCall(evmBlockNumber); } catch { result = await this._legacy.getRandomizeStatus.staticCall(evmBlockNumber); } } catch (error) { throw new Error(`${this.constructor.name}: cannot get randomize status on block ${evmBlockNumber}.\n${error?.stack?.split('\n')[0] || error}`); } switch (Number(result)) { case 1: return "Awaiting"; case 2: return "Ready"; case 3: return "Error"; case 4: return "Finalizing"; default: return "Void"; } } async getRandomizeStatusDescription(evmBlockNumber) { try { return this.contract .getFunction("getRandomizeStatusDescription(uint96)") .staticCall(evmBlockNumber); } catch { return this.getRandomizeStatus(evmBlockNumber); } } async getRandomizeHistory(limit = 16) { const result = []; let evmBlockNumber = await this.getLastRandomizeBlock(); while (evmBlockNumber && result.length < limit) { const evmRandomizeStatus = await this.getRandomizeStatus(evmBlockNumber); const witResult = await this.getRandomnessAfter(evmBlockNumber); let witResultDrTxHash, witResultTimestamp; if (witResult) { [witResultDrTxHash, witResultTimestamp] = await this.fetchRandomnessAfterProof(evmBlockNumber); } result.push({ evmBlockNumber, evmRandomizeStatus, witResult, witResultDrTxHash, witResultTimestamp }); evmBlockNumber = await this.getRandomizePrevBlock(evmBlockNumber); } return result; } async isRandomized(evmBlockNumber) { let result; try { try { result = await this.contract.isRandomized.staticCall(evmBlockNumber); } catch { result = await this._legacy.isRandomized.staticCall(evmBlockNumber); } } catch (error) { throw new Error(`${this.constructor.name}: cannot get randomize status on block ${evmBlockNumber}.\n${error?.stack?.split('\n')[0] || error}`); } return result; } async randomize(options) { const evmGasPrice = options?.evmGasPrice || (await this.provider.getFeeData()).gasPrice || 0n; const evmRandomizeFee = await this.estimateRandomizeFee(evmGasPrice); const evmTransaction = await this.contract .getFunction("randomize()") .populateTransaction(); evmTransaction.gasPrice = evmGasPrice || evmTransaction?.gasPrice; evmTransaction.value = evmRandomizeFee; return this.signer .sendTransaction(evmTransaction) .then(response => { if (options?.onRandomizeTransaction) options.onRandomizeTransaction(response.hash); return response.wait(options?.evmConfirmations || 1, options?.evmTimeout); }) .then(receipt => { if (options?.onRandomizeTransactionReceipt) options.onRandomizeTransactionReceipt(receipt); return receipt; }); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JhcHBlcnMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3dyYXBwZXJzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQWVlO0FBRWYscUNBQTJDO0FBRTNDLG1DQVNnQjtBQVloQixNQUFlLGVBQWU7SUFFMUIsWUFBYSxNQUFxQixFQUFFLE9BQWUsRUFBRSxHQUE2QixFQUFFLE1BQWM7UUFDOUYsSUFBSSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUE7UUFDckIsSUFBSSxDQUFDLFFBQVEsR0FBRyxJQUFJLGlCQUFRLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxNQUF3QixDQUFDLENBQUE7UUFDbkUsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUE7UUFDdEIsSUFBSSxDQUFDLFFBQVEsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFBO1FBQy9CLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFBO0lBQ3hCLENBQUM7SUEyQkQ7OztPQUdHO0lBQ0ksS0FBSyxDQUFDLGVBQWU7UUFDeEIsT0FBTyxJQUFJLENBQUMsUUFBUTthQUNmLFdBQVcsQ0FBQyxTQUFTLENBQUM7YUFDdEIsVUFBVSxFQUFFLENBQUE7SUFDckIsQ0FBQztJQUVEOzs7T0FHRztJQUNJLEtBQUssQ0FBQyxlQUFlO1FBQ3hCLE9BQU8sSUFBSSxDQUFDLFFBQVE7YUFDZixXQUFXLENBQUMsU0FBUyxDQUFDO2FBQ3RCLFVBQVUsRUFBRSxDQUFBO0lBQ3JCLENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMsaUJBQWlCO1FBQzFCLElBQUksT0FBTyxDQUFBO1FBQ1gsSUFBSSxDQUFDO1lBQ0QsT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLFFBQVE7aUJBQ3hCLElBQUksQ0FBQztnQkFDRixFQUFFLEVBQUUsSUFBSSxDQUFDLE9BQU87Z0JBQ2hCLElBQUksRUFBRSxZQUFZLEVBQUUsMEJBQTBCO2FBQ2pELENBQUM7aUJBQ0QsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsaUJBQVEsQ0FBQyxlQUFlLEVBQUUsQ0FBQyxNQUFNLENBQUMsQ0FBQyxRQUFRLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQztpQkFDckUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFDM0MsQ0FBQztRQUFDLE9BQU8sSUFBSSxFQUFFLENBQUM7WUFDWixPQUFPLGFBQWEsQ0FBQTtRQUN4QixDQUFDO1FBQ0QsT0FBTyxPQUFPLENBQUE7SUFDbEIsQ0FBQztDQUNKO0FBRUQsTUFBZSxrQkFBbUIsU0FBUSxlQUFlO0lBRXJELFlBQWEsTUFBcUIsRUFBRSxPQUFlLEVBQUUsUUFBZ0IsRUFBRSxFQUFXO1FBQzlFLE1BQU0sSUFBSSxHQUE2QyxZQUFJLENBQUE7UUFDM0QsTUFBTSxNQUFNLEdBQUcsRUFBRSxJQUFJLElBQUEsOEJBQXNCLEVBQUMsT0FBTyxDQUFDLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQ3BFLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUM3QixNQUFNLElBQUksS0FBSyxDQUFDLGVBQWUsT0FBTyxrQ0FBa0MsUUFBUSxFQUFFLENBQUMsQ0FBQTtRQUN2RixDQUFDO2FBQU0sQ0FBQztZQUNKLEtBQUssQ0FBQyxNQUFNLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQTtRQUNsRCxDQUFDO0lBQ0wsQ0FBQztDQUNKO0FBRUQsTUFBZSxtQkFBb0IsU0FBUSxlQUFlO0lBSXRELFlBQWEsU0FBb0IsRUFBRSxRQUFnQixFQUFFLEVBQVc7UUFDNUQsTUFBTSxJQUFJLEdBQTZDLFlBQUksQ0FBQTtRQUMzRCxNQUFNLFNBQVMsR0FBRyxJQUFBLDhCQUFzQixFQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQTtRQUMzRCxNQUFNLE1BQU0sR0FBRyxFQUFFLElBQUksU0FBUyxFQUFFLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxTQUFTLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBQzNFLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUM3QixNQUFNLElBQUksS0FBSyxDQUFDLGVBQWUsU0FBUyxDQUFDLE9BQU8sK0JBQStCLFFBQVEsRUFBRSxDQUFDLENBQUE7UUFDOUYsQ0FBQztRQUNELEtBQUssQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLFNBQVMsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFBO1FBQ2xFLElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFBO0lBQzlCLENBQUM7Q0FDSjtBQUVEOzs7Ozs7R0FNRztBQUNILE1BQWEsU0FBVSxTQUFRLGtCQUFrQjtJQUU3QyxZQUFhLE1BQXFCLEVBQUUsT0FBZTtRQUMvQyxLQUFLLENBQUMsTUFBTSxFQUFFLE9BQU8sRUFBRSxXQUFXLENBQUMsQ0FBQTtJQUN2QyxDQUFDO0lBRUQ7Ozs7OztPQU1HO0lBQ0ksTUFBTSxDQUFDLEtBQUssQ0FBQyxjQUFjLENBQUMsR0FBVyxFQUFFLFFBQTBCO1FBQ3RFLE1BQU0sUUFBUSxHQUFHLEl