Created
December 15, 2025 17:51
-
-
Save fassko/4bb50851ebe28dfda1982c84609e4803 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { run, web3 } from "hardhat"; | |
| import { | |
| prepareAttestationRequestBase, | |
| submitAttestationRequest, | |
| retrieveDataAndProofBaseWithRetry, | |
| } from "../utils/fdc"; | |
| import { sleep } from "../utils/core"; | |
| // TypeChain typings for Truffle don't always include newly renamed contracts immediately. | |
| // Cast to `any` to avoid artifacts.require overload issues. | |
| const LatviaSnowDepth = (artifacts as any).require("LatviaSnowDepth"); | |
| const { WEB2JSON_VERIFIER_URL_TESTNET, VERIFIER_API_KEY_TESTNET, COSTON2_DA_LAYER_URL } = process.env; | |
| // yarn hardhat run scripts/fdcExample/LatviaSnowDepth.ts --network coston2 | |
| // Request data | |
| const apiUrl = "https://data.gov.lv/dati/lv/api/action/datastore_search"; | |
| const queryParams = JSON.stringify({ | |
| resource_id: "17460efb-ae99-4d1d-8144-1068f184b05f", | |
| q: "[RIGASLU,HSNOW,2025-12-14T06:00:00]", | |
| }); | |
| // Extract `result.records[0].VALUE` and normalize to a lower-case `value` field. | |
| // Also extract `DATETIME`, treat it as UTC (append "Z"), and convert to unix timestamp (seconds). | |
| const postProcessJq = | |
| `{timestamp: (((.result.records[0].DATETIME // .result.records[0].datetime) + "Z") | fromdateiso8601 | floor), ` + | |
| `value: (.result.records[0].VALUE // .result.records[0].value)}`; | |
| const httpMethod = "GET"; | |
| // Defaults to "Content-Type": "application/json" | |
| // Some public APIs respond better with explicit Accept/User-Agent. | |
| const headers = JSON.stringify({ | |
| accept: "application/json", | |
| "user-agent": "Mozilla/5.0", | |
| }); | |
| const body = "{}"; | |
| // Must match the DTO decoded in `LatviaSnowDepth.sol`. | |
| const abiSignature = `{"components": [{"internalType": "uint64", "name": "timestamp", "type": "uint64"},{"internalType": "uint256", "name": "value", "type": "uint256"}],"name": "task","type": "tuple"}`; | |
| // Configuration constants | |
| const attestationTypeBase = "Web2Json"; | |
| const sourceIdBase = "PublicWeb2"; | |
| const verifierUrlBase = WEB2JSON_VERIFIER_URL_TESTNET; | |
| async function prepareAttestationRequest(apiUrl: string, postProcessJq: string, abiSignature: string) { | |
| const requestBody = { | |
| url: apiUrl, | |
| httpMethod: httpMethod, | |
| headers: headers, | |
| queryParams: queryParams, | |
| body: body, | |
| postProcessJq: postProcessJq, | |
| abiSignature: abiSignature, | |
| }; | |
| const url = `${verifierUrlBase}Web2Json/prepareRequest`; | |
| const apiKey = VERIFIER_API_KEY_TESTNET; | |
| return await prepareAttestationRequestBase(url, apiKey ?? "", attestationTypeBase, sourceIdBase, requestBody); | |
| } | |
| async function retrieveDataAndProof(abiEncodedRequest: string, roundId: number) { | |
| const url = `${COSTON2_DA_LAYER_URL}api/v1/fdc/proof-by-request-round-raw`; | |
| console.log("Url:", url, "n"); | |
| return await retrieveDataAndProofBaseWithRetry(url, abiEncodedRequest, roundId); | |
| } | |
| async function deployAndVerifyContract() { | |
| const args: any[] = []; | |
| const latviaSnowDepth = await LatviaSnowDepth.new(...args); | |
| console.log("LatviaSnowDepth deployed to", latviaSnowDepth.address, "\n"); | |
| // Explorer indexing can lag a bit after deployment; retry verification a few times. | |
| for (let attempt = 1; attempt <= 5; attempt++) { | |
| try { | |
| console.log(`Verifying LatviaSnowDepth (attempt ${attempt}/5)...`); | |
| await run("verify:verify", { | |
| address: latviaSnowDepth.address, | |
| constructorArguments: args, | |
| contract: "contracts/fdcExample/LatviaSnowDepth.sol:LatviaSnowDepth", | |
| }); | |
| break; | |
| } catch (e: any) { | |
| console.log(e?.message ?? e); | |
| await sleep(20000); | |
| } | |
| } | |
| return latviaSnowDepth; | |
| } | |
| async function interactWithContract(contract: any, proof: any) { | |
| console.log("Proof hex:", proof.response_hex, "\n"); | |
| // A piece of black magic that allows us to read the response type from an artifact | |
| const IWeb2JsonVerification = await artifacts.require("IWeb2JsonVerification"); | |
| const responseType = IWeb2JsonVerification._json.abi[0].inputs[0].components[1]; | |
| console.log("Response type:", responseType, "\n"); | |
| const decodedResponse = web3.eth.abi.decodeParameter(responseType, proof.response_hex); | |
| console.log("Decoded proof:", decodedResponse, "\n"); | |
| const transaction = await contract.updateSnowDepth({ | |
| merkleProof: proof.proof, | |
| data: decodedResponse, | |
| }); | |
| console.log("Transaction:", transaction.tx, "\n"); | |
| console.log("Stored lastSnowDepth:", (await contract.lastSnowDepth()).toString(), "\n"); | |
| const lastTimestamp = await contract.lastTimestamp(); | |
| console.log("Stored lastTimestamp:", lastTimestamp.toString(), "\n"); | |
| console.log( | |
| "Stored snowDepthByTimestamp[lastTimestamp]:", | |
| (await contract.snowDepthByTimestamp(lastTimestamp)).toString(), | |
| "\n" | |
| ); | |
| } | |
| async function main() { | |
| const data = await prepareAttestationRequest(apiUrl, postProcessJq, abiSignature); | |
| console.log("Data:", data, "\n"); | |
| if (!data?.abiEncodedRequest) { | |
| throw new Error( | |
| `Web2Json prepareRequest did not return abiEncodedRequest. Response: ${JSON.stringify(data)}` | |
| ); | |
| } | |
| const abiEncodedRequest = data.abiEncodedRequest; | |
| const roundId = await submitAttestationRequest(abiEncodedRequest); | |
| const proof = await retrieveDataAndProof(abiEncodedRequest, roundId); | |
| const latviaSnowDepth = await deployAndVerifyContract(); | |
| await interactWithContract(latviaSnowDepth, proof); | |
| } | |
| void main().then(() => { | |
| process.exit(0); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment