From e10d330e2b9d3732291ea9057f13fd0d49c64daf Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Sat, 16 May 2026 12:34:32 +0530 Subject: [PATCH 1/5] Update Base DIA oracle to equity feeds on PushOracleReceiverV2. Point LibDia at the new oracle contract, use 18-decimal prices and ticker keys (AMZN, NVDA, etc.), refresh fork tests and docs, and add a Basescan verification helper script. --- .pre-commit-config.yaml | 2 +- README.md | 13 +++-- meta/DiaSubParserAuthoringMeta.rain.meta | Bin 544 -> 544 bytes script/verify-dia-oracle-base.sh | 66 ++++++++++++++++++++++ src/lib/dia/LibDia.sol | 15 +++-- src/lib/parse/LibDiaSubParser.sol | 2 +- test/lib/LibFork.sol | 12 ++-- test/src/concrete/DiaWords.diaPrice.t.sol | 5 +- test/src/lib/dia/LibDia.t.sol | 44 +++++++++++---- test/src/lib/op/LibOpDiaPrice.t.sol | 5 +- 10 files changed, 123 insertions(+), 41 deletions(-) create mode 100755 script/verify-dia-oracle-base.sh diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 09eb304..b869378 120000 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1 +1 @@ -/nix/store/m6bkmh5cm3qlgi9pgmxg3kb4kvpj6lkv-pre-commit-config.json \ No newline at end of file +/nix/store/8yg5imq6129bmlig7y659qv0q72irz2b-pre-commit-config.json \ No newline at end of file diff --git a/README.md b/README.md index c35d9c7..95528ce 100644 --- a/README.md +++ b/README.md @@ -8,18 +8,21 @@ Provides a `dia-price` word that fetches prices from the DIA oracle on-chain. ```rain using-words-from -price updated-at: dia-price("BTC/USD" 3600); +price updated-at: dia-price("AMZN" 3600); ``` ### Inputs -1. **key** — DIA price feed key as a string, e.g. `"BTC/USD"`, `"ETH/USD"`. Passed through directly to the DIA oracle contract. -2. **staleAfter** — Maximum age of the price in seconds. Reverts if the price is older than this. +1. **key** — DIA price feed key as a string, e.g. `"AMZN"`, `"NVDA"`. Passed + through directly to the DIA oracle contract. +2. **staleAfter** — Maximum age of the price in seconds. Reverts if the price is + older than this. ### Outputs -1. **price** — The asset price as a Float (8 decimal places). -2. **updatedAt** — The timestamp of the last price update as a Float (unix seconds). +1. **price** — The asset price as a Float (18 decimal places). +2. **updatedAt** — The timestamp of the last price update as a Float (unix + seconds). ## Supported chains diff --git a/meta/DiaSubParserAuthoringMeta.rain.meta b/meta/DiaSubParserAuthoringMeta.rain.meta index 9962457e2a119d4cc421069dfd5b77664cab0bef..29714cbdea55067799d38c742858416910aadae2 100644 GIT binary patch delta 29 lcmZ3$vVdj6en!uU2Z~r6eWUy)TQcf08ct4Q+{MJe005VD2*CgV delta 27 jcmZ3$vVdj6en#(!2a4F8LY(zOgIy+@G3riEVcZ1(hk6N1 diff --git a/script/verify-dia-oracle-base.sh b/script/verify-dia-oracle-base.sh new file mode 100755 index 0000000..1f5c8da --- /dev/null +++ b/script/verify-dia-oracle-base.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Verify DIA PushOracleReceiverV2 on Base (0xCE521b52513242c5094bc56f57887BB2A05B8129). +# +# Requires: +# ETHERSCAN_API_KEY — Basescan / Etherscan API v2 key +# git, forge, cast (nix develop provides these) +# +# Usage: +# export ETHERSCAN_API_KEY=your_key +# ./script/verify-dia-oracle-base.sh + +set -euo pipefail + +ORACLE=0xCE521b52513242c5094bc56f57887BB2A05B8129 +SPECTRA_REPO="${SPECTRA_REPO:-/tmp/diadata-spectra-interoperability}" +SPECTRA_REF="${SPECTRA_REF:-main}" + +if [[ -z "${ETHERSCAN_API_KEY:-}" ]]; then + echo "error: set ETHERSCAN_API_KEY (Basescan API key)" >&2 + exit 1 +fi + +if [[ ! -d "$SPECTRA_REPO/.git" ]]; then + # Do not use --recurse-submodules: config-private is a private DIA repo. + git clone --depth 1 --branch "$SPECTRA_REF" \ + https://github.com/diadata-org/Spectra-interoperability.git "$SPECTRA_REPO" +fi + +cd "$SPECTRA_REPO" + +# Shallow clones omit submodule contents; forge remappings require lib/*. +git submodule update --init --depth 1 \ + contracts/lib/forge-std \ + contracts/lib/openzeppelin-contracts + +if [[ ! -f contracts/lib/forge-std/src/Script.sol ]]; then + echo "error: contracts/lib/forge-std not initialized" >&2 + exit 1 +fi +if [[ ! -f contracts/lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol ]]; then + echo "error: contracts/lib/openzeppelin-contracts not initialized" >&2 + exit 1 +fi + +cd contracts +npm install --silent + +echo "Building PushOracleReceiverV2..." +forge build --contracts contracts/PushOracleReceiverV2.sol + +CONSTRUCTOR_ARGS=$( + cast abi-encode \ + "constructor(string,string,uint256,address)" \ + "DIA Oracle" "1.0" 1050 0x5612599cf48032d7428399d5fcb99edcc75c06a7 +) + +echo "Submitting verification to Basescan..." +forge verify-contract \ + "$ORACLE" \ + contracts/PushOracleReceiverV2.sol:PushOracleReceiverV2 \ + --chain base \ + --etherscan-api-key "$ETHERSCAN_API_KEY" \ + --constructor-args "$CONSTRUCTOR_ARGS" \ + --watch + +echo "Verified: https://basescan.org/address/$ORACLE#code" diff --git a/src/lib/dia/LibDia.sol b/src/lib/dia/LibDia.sol index 1775ef8..d19e150 100644 --- a/src/lib/dia/LibDia.sol +++ b/src/lib/dia/LibDia.sol @@ -12,18 +12,17 @@ error ZeroDiaPrice(string key); /// @title LibDia /// @notice Core library for interacting with DIA oracle V2 on-chain. -/// DIA keys are simple strings like "BTC/USD", "ETH/USD", etc. +/// DIA keys are ticker strings like "AMZN", "NVDA", etc. /// The string is passed through directly from the Rain expression. -/// DIA prices have 8 decimals. +/// DIA prices have 18 decimals. library LibDia { uint256 constant CHAIN_ID_BASE = 8453; /// @dev DIA oracle V2 contract on Base. - /// https://docs.diadata.org/products/token-price-feeds/access-the-oracle - IDIAOracleV2 constant ORACLE_BASE = IDIAOracleV2(0xB8BF9ba432282F25F56e143641145349ab7c5Bf6); + IDIAOracleV2 constant ORACLE_BASE = IDIAOracleV2(0xCE521b52513242c5094bc56f57887BB2A05B8129); - /// @dev DIA prices have 8 decimal places. - int256 constant DIA_DECIMALS = -8; + /// @dev DIA prices have 18 decimal places. + int256 constant DIA_DECIMALS = -18; /// @dev Mask for the 5 bit length from V3 IntOrAString. uint256 constant LENGTH_MASK_V3 = 0x1f; @@ -58,10 +57,10 @@ library LibDia { /// @notice Fetches a price from the DIA oracle and reverts if the price is /// stale or zero. The key is passed through as a string directly from the - /// Rain expression, e.g. "BTC/USD". + /// Rain expression, e.g. "AMZN". /// @param feedKey The V3 IntOrAString key for the DIA feed. /// @param staleAfter The maximum age of the price in seconds as a Float. - /// @return price The price as a Float with 8 decimal places. + /// @return price The price as a Float with 18 decimal places. /// @return updatedAt The timestamp of the price update as a Float (seconds). function getPriceNoOlderThan(IntOrAString feedKey, Float staleAfter) internal diff --git a/src/lib/parse/LibDiaSubParser.sol b/src/lib/parse/LibDiaSubParser.sol index 2e060e6..a1d92d8 100644 --- a/src/lib/parse/LibDiaSubParser.sol +++ b/src/lib/parse/LibDiaSubParser.sol @@ -14,7 +14,7 @@ library LibDiaSubParser { meta[SUB_PARSER_WORD_DIA_PRICE] = AuthoringMetaV2( "dia-price", - "Returns the current price of the given asset according to DIA. Accepts 2 inputs, the price key as a string (e.g. \"BTC/USD\") and the timeout in seconds. The price has 8 decimal places. The timeout will be used to determine if the price is stale and revert if it is. Returns 2 outputs: the price and the timestamp of the last update." + "Returns the current price of the given asset according to DIA. Accepts 2 inputs, the price key as a string (e.g. \"AMZN\") and the timeout in seconds. The price has 18 decimal places. The timeout will be used to determine if the price is stale and revert if it is. Returns 2 outputs: the price and the timestamp of the last update." ); return abi.encode(meta); } diff --git a/test/lib/LibFork.sol b/test/lib/LibFork.sol index 45b8f44..e7d3762 100644 --- a/test/lib/LibFork.sol +++ b/test/lib/LibFork.sol @@ -4,11 +4,7 @@ pragma solidity ^0.8.25; string constant FORK_RPC_URL_BASE = "https://base.gateway.tenderly.co"; -/// @dev A recent Base block. DIA demo oracle has BTC/USD data with timestamp -/// 1744172776 (April 9, 2025). Tests use vm.warp to set block.timestamp -/// close to the DIA update time so staleness checks pass. -uint256 constant FORK_BLOCK_BASE = 44515230; - -/// @dev The timestamp of the DIA BTC/USD update at the demo oracle. -/// Tests warp to this + a small offset so staleness checks pass. -uint256 constant DIA_BTC_USD_TIMESTAMP = 1744172776; +/// @dev A recent Base block. DIA oracle has AMZN data with timestamp +/// 1778908932. Tests use vm.warp to set block.timestamp close to the DIA +/// update time so staleness checks pass. +uint256 constant FORK_BLOCK_BASE = 46061133; diff --git a/test/src/concrete/DiaWords.diaPrice.t.sol b/test/src/concrete/DiaWords.diaPrice.t.sol index f496559..da6455c 100644 --- a/test/src/concrete/DiaWords.diaPrice.t.sol +++ b/test/src/concrete/DiaWords.diaPrice.t.sol @@ -4,7 +4,7 @@ pragma solidity =0.8.25; import {Test} from "forge-std/Test.sol"; import {DiaWords} from "src/concrete/DiaWords.sol"; -import {FORK_RPC_URL_BASE, FORK_BLOCK_BASE, DIA_BTC_USD_TIMESTAMP} from "test/lib/LibFork.sol"; +import {FORK_RPC_URL_BASE, FORK_BLOCK_BASE} from "test/lib/LibFork.sol"; import {LibDia} from "src/lib/dia/LibDia.sol"; import {LibDecimalFloat, Float} from "rain.math.float/lib/LibDecimalFloat.sol"; import {IntOrAString} from "rain.intorastring/lib/LibIntOrAString.sol"; @@ -28,12 +28,11 @@ contract DiaWordsDiaPriceTest is Test { function testDiaWordsExternDispatch() external { vm.createSelectFork(FORK_RPC_URL_BASE, FORK_BLOCK_BASE); vm.chainId(LibDia.CHAIN_ID_BASE); - vm.warp(DIA_BTC_USD_TIMESTAMP + 60); DiaWords diaWords = new DiaWords(); StackItem[] memory inputs = new StackItem[](2); - inputs[0] = StackItem.wrap(bytes32(IntOrAString.unwrap(fromStringV3("BTC/USD")))); + inputs[0] = StackItem.wrap(bytes32(IntOrAString.unwrap(fromStringV3("AMZN")))); inputs[1] = StackItem.wrap(Float.unwrap(LibDecimalFloat.packLossless(3600, 0))); StackItem[] memory outputs = LibOpDiaPrice.run(OperandV2.wrap(0), inputs); diff --git a/test/src/lib/dia/LibDia.t.sol b/test/src/lib/dia/LibDia.t.sol index eaa891a..c0f3ed2 100644 --- a/test/src/lib/dia/LibDia.t.sol +++ b/test/src/lib/dia/LibDia.t.sol @@ -6,7 +6,7 @@ import {Test} from "forge-std/Test.sol"; import {LibDia, IDIAOracleV2, UnsupportedChainId} from "src/lib/dia/LibDia.sol"; import {IntOrAString} from "rain.intorastring/lib/LibIntOrAString.sol"; import {Float, LibDecimalFloat} from "rain.math.float/lib/LibDecimalFloat.sol"; -import {FORK_RPC_URL_BASE, FORK_BLOCK_BASE, DIA_BTC_USD_TIMESTAMP} from "test/lib/LibFork.sol"; +import {FORK_RPC_URL_BASE, FORK_BLOCK_BASE} from "test/lib/LibFork.sol"; /// @dev Create a V3-encoded IntOrAString matching the latest Rain parser output. /// Layout: string data right-aligned above the low byte, low byte = 0xE0 | length. @@ -40,36 +40,56 @@ contract LibDiaGetOracleContractTest is Test { contract LibDiaStringV3Test is Test { function testRoundTrip() external pure { - IntOrAString encoded = fromStringV3("BTC/USD"); + IntOrAString encoded = fromStringV3("AMZN"); string memory decoded = LibDia.intOrAStringToString(encoded); - assertEq(decoded, "BTC/USD"); + assertEq(decoded, "AMZN"); } - function testRoundTripETH() external pure { - IntOrAString encoded = fromStringV3("ETH/USD"); + function testRoundTripNVDA() external pure { + IntOrAString encoded = fromStringV3("NVDA"); string memory decoded = LibDia.intOrAStringToString(encoded); - assertEq(decoded, "ETH/USD"); + assertEq(decoded, "NVDA"); } } contract LibDiaGetPriceTest is Test { - function testGetPriceBtcUsd() external { + function setUp() external { vm.createSelectFork(FORK_RPC_URL_BASE, FORK_BLOCK_BASE); vm.chainId(8453); - vm.warp(DIA_BTC_USD_TIMESTAMP + 60); + } - IntOrAString key = fromStringV3("BTC/USD"); + function _assertFeedPrice(string memory symbol, uint256 rawPrice) internal view { + IntOrAString key = fromStringV3(symbol); Float staleAfter = LibDecimalFloat.packLossless(3600, 0); (Float price, Float updatedAt) = LibDia.getPriceNoOlderThan(key, staleAfter); assertTrue(Float.unwrap(price) != 0, "price should be non-zero"); assertTrue(Float.unwrap(updatedAt) != 0, "timestamp should be non-zero"); - assertEq( Float.unwrap(price), - Float.unwrap(LibDecimalFloat.packLossless(int256(uint256(7568457939217)), -8)), - "unexpected BTC price" + Float.unwrap(LibDecimalFloat.packLossless(int256(rawPrice), -18)), + string.concat("unexpected ", symbol, " price") ); } + + function testGetPriceAmzn() external { + _assertFeedPrice("AMZN", 264100000000000022736); + } + + function testGetPriceNvda() external { + _assertFeedPrice("NVDA", 225389999999999986352); + } + + function testGetPriceCoin() external { + _assertFeedPrice("COIN", 195539999999999992048); + } + + function testGetPriceMstr() external { + _assertFeedPrice("MSTR", 177455000000000012512); + } + + function testGetPriceTsla() external { + _assertFeedPrice("TSLA", 422350000000000022752); + } } diff --git a/test/src/lib/op/LibOpDiaPrice.t.sol b/test/src/lib/op/LibOpDiaPrice.t.sol index f512dff..e06942b 100644 --- a/test/src/lib/op/LibOpDiaPrice.t.sol +++ b/test/src/lib/op/LibOpDiaPrice.t.sol @@ -5,7 +5,7 @@ pragma solidity =0.8.25; import {Test} from "forge-std/Test.sol"; import {LibOpDiaPrice, OperandV2, StackItem} from "src/lib/op/LibOpDiaPrice.sol"; import {IntOrAString} from "rain.intorastring/lib/LibIntOrAString.sol"; -import {FORK_RPC_URL_BASE, FORK_BLOCK_BASE, DIA_BTC_USD_TIMESTAMP} from "test/lib/LibFork.sol"; +import {FORK_RPC_URL_BASE, FORK_BLOCK_BASE} from "test/lib/LibFork.sol"; import {Float, LibDecimalFloat} from "rain.math.float/lib/LibDecimalFloat.sol"; function fromStringV3(string memory s) pure returns (IntOrAString intOrAString) { @@ -27,10 +27,9 @@ contract LibOpDiaPriceTest is Test { function testRunForkCurrentPriceHappy() external { vm.createSelectFork(FORK_RPC_URL_BASE, FORK_BLOCK_BASE); vm.chainId(8453); - vm.warp(DIA_BTC_USD_TIMESTAMP + 60); StackItem[] memory inputs = new StackItem[](2); - inputs[0] = StackItem.wrap(bytes32(IntOrAString.unwrap(fromStringV3("BTC/USD")))); + inputs[0] = StackItem.wrap(bytes32(IntOrAString.unwrap(fromStringV3("AMZN")))); inputs[1] = StackItem.wrap(Float.unwrap(LibDecimalFloat.packLossless(3600, 0))); StackItem[] memory outputs = LibOpDiaPrice.run(OperandV2.wrap(0), inputs); From ae5ef184195290de6b68dd2e4c19143872741a17 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Sat, 16 May 2026 12:35:57 +0530 Subject: [PATCH 2/5] Remove Basescan verification helper script. Oracle verification is out of scope for this repository. --- script/verify-dia-oracle-base.sh | 66 -------------------------------- 1 file changed, 66 deletions(-) delete mode 100755 script/verify-dia-oracle-base.sh diff --git a/script/verify-dia-oracle-base.sh b/script/verify-dia-oracle-base.sh deleted file mode 100755 index 1f5c8da..0000000 --- a/script/verify-dia-oracle-base.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -# Verify DIA PushOracleReceiverV2 on Base (0xCE521b52513242c5094bc56f57887BB2A05B8129). -# -# Requires: -# ETHERSCAN_API_KEY — Basescan / Etherscan API v2 key -# git, forge, cast (nix develop provides these) -# -# Usage: -# export ETHERSCAN_API_KEY=your_key -# ./script/verify-dia-oracle-base.sh - -set -euo pipefail - -ORACLE=0xCE521b52513242c5094bc56f57887BB2A05B8129 -SPECTRA_REPO="${SPECTRA_REPO:-/tmp/diadata-spectra-interoperability}" -SPECTRA_REF="${SPECTRA_REF:-main}" - -if [[ -z "${ETHERSCAN_API_KEY:-}" ]]; then - echo "error: set ETHERSCAN_API_KEY (Basescan API key)" >&2 - exit 1 -fi - -if [[ ! -d "$SPECTRA_REPO/.git" ]]; then - # Do not use --recurse-submodules: config-private is a private DIA repo. - git clone --depth 1 --branch "$SPECTRA_REF" \ - https://github.com/diadata-org/Spectra-interoperability.git "$SPECTRA_REPO" -fi - -cd "$SPECTRA_REPO" - -# Shallow clones omit submodule contents; forge remappings require lib/*. -git submodule update --init --depth 1 \ - contracts/lib/forge-std \ - contracts/lib/openzeppelin-contracts - -if [[ ! -f contracts/lib/forge-std/src/Script.sol ]]; then - echo "error: contracts/lib/forge-std not initialized" >&2 - exit 1 -fi -if [[ ! -f contracts/lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol ]]; then - echo "error: contracts/lib/openzeppelin-contracts not initialized" >&2 - exit 1 -fi - -cd contracts -npm install --silent - -echo "Building PushOracleReceiverV2..." -forge build --contracts contracts/PushOracleReceiverV2.sol - -CONSTRUCTOR_ARGS=$( - cast abi-encode \ - "constructor(string,string,uint256,address)" \ - "DIA Oracle" "1.0" 1050 0x5612599cf48032d7428399d5fcb99edcc75c06a7 -) - -echo "Submitting verification to Basescan..." -forge verify-contract \ - "$ORACLE" \ - contracts/PushOracleReceiverV2.sol:PushOracleReceiverV2 \ - --chain base \ - --etherscan-api-key "$ETHERSCAN_API_KEY" \ - --constructor-args "$CONSTRUCTOR_ARGS" \ - --watch - -echo "Verified: https://basescan.org/address/$ORACLE#code" From dcfa8fbe59d451bcea4da1a518194ecb001d64f0 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Sat, 16 May 2026 13:26:52 +0530 Subject: [PATCH 3/5] Replace Nix symlink pre-commit config with portable YAML. Commit a static .pre-commit-config.yaml so non-Nix contributors can run pre-commit install; git-hooks.nix leaves the file in place when present. --- .pre-commit-config.yaml | 34 +++++++++++++++++++++++++++++++++- README.md | 14 ++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) mode change 120000 => 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 120000 index b869378..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1 +0,0 @@ -/nix/store/8yg5imq6129bmlig7y659qv0q72irz2b-pre-commit-config.json \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..704a82f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,33 @@ +# Portable pre-commit config (no Nix store paths). +# Install: pip install pre-commit && pre-commit install +# Or with tools on PATH: pre-commit run --all-files + +default_language_version: + python: python3 + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + + - repo: https://github.com/shellcheck-py/shellcheck-py + rev: v0.10.0.1 + hooks: + - id: shellcheck + + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v3.1.0 + hooks: + - id: prettier + types_or: [markdown, yaml, json] + + - repo: local + hooks: + - id: forge-fmt + name: forge fmt + entry: forge fmt + language: system + files: \.sol$ + pass_filenames: false diff --git a/README.md b/README.md index 95528ce..6713cfc 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,17 @@ forge test # Regenerate pointers forge script script/BuildPointers.sol ``` + +### Pre-commit + +Git hooks use the committed `.pre-commit-config.yaml` (not Nix store symlinks). +Install once, then hooks run on `git commit`: + +```sh +pip install pre-commit +pre-commit install +pre-commit run --all-files +``` + +Requires `forge` on `PATH` for Solidity formatting. Nix users can use +`nix develop` for the same tools without replacing the config file. From 1091bc99a52ffd97793736420a27648f8a973d12 Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Sat, 16 May 2026 13:48:41 +0530 Subject: [PATCH 4/5] update comments, fmt --- .pre-commit-config.yaml | 3 ++- README.md | 5 +++-- src/abstract/DiaExtern.sol | 20 ++++++-------------- src/abstract/DiaSubParser.sol | 23 +++++++++-------------- test/lib/LibFork.sol | 4 ++-- test/src/lib/dia/LibDia.t.sol | 13 +++++++++++++ 6 files changed, 35 insertions(+), 33 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 704a82f..640c7d2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,8 @@ repos: hooks: - id: forge-fmt name: forge fmt - entry: forge fmt + # Skip when forge is not on PATH (e.g. IDE Git); run via nix develop for enforcement. + entry: bash -c 'command -v forge >/dev/null 2>&1 || exit 0; exec forge fmt' language: system files: \.sol$ pass_filenames: false diff --git a/README.md b/README.md index 6713cfc..774ec66 100644 --- a/README.md +++ b/README.md @@ -52,5 +52,6 @@ pre-commit install pre-commit run --all-files ``` -Requires `forge` on `PATH` for Solidity formatting. Nix users can use -`nix develop` for the same tools without replacing the config file. +Solidity formatting runs when `forge` is on `PATH` (e.g. inside `nix develop`); +otherwise the hook is skipped so IDE commits still work. Run +`nix develop -c forge fmt` before committing `.sol` changes if needed. diff --git a/src/abstract/DiaExtern.sol b/src/abstract/DiaExtern.sol index b308f58..63694ca 100644 --- a/src/abstract/DiaExtern.sol +++ b/src/abstract/DiaExtern.sol @@ -25,13 +25,9 @@ abstract contract DiaExtern is BaseRainterpreterExternNPE2 { } function buildOpcodeFunctionPointers() external pure returns (bytes memory) { - function(OperandV2, StackItem[] memory) - internal - view - returns (StackItem[] memory)[] memory fs = new function(OperandV2, StackItem[] memory) - internal - view - returns (StackItem[] memory)[](OPCODE_FUNCTION_POINTERS_LENGTH); + function(OperandV2, StackItem[] memory) internal view returns (StackItem[] memory)[] memory fs = new function(OperandV2, StackItem[] memory) + internal + view returns (StackItem[] memory)[](OPCODE_FUNCTION_POINTERS_LENGTH); fs[OPCODE_DIA_PRICE] = LibOpDiaPrice.run; uint256[] memory pointers; @@ -42,13 +38,9 @@ abstract contract DiaExtern is BaseRainterpreterExternNPE2 { } function buildIntegrityFunctionPointers() external pure returns (bytes memory) { - function(OperandV2, uint256, uint256) - internal - pure - returns (uint256, uint256)[] memory fs = new function(OperandV2, uint256, uint256) - internal - pure - returns (uint256, uint256)[](OPCODE_FUNCTION_POINTERS_LENGTH); + function(OperandV2, uint256, uint256) internal pure returns (uint256, uint256)[] memory fs = new function(OperandV2, uint256, uint256) + internal + pure returns (uint256, uint256)[](OPCODE_FUNCTION_POINTERS_LENGTH); fs[OPCODE_DIA_PRICE] = LibOpDiaPrice.integrity; uint256[] memory pointers; diff --git a/src/abstract/DiaSubParser.sol b/src/abstract/DiaSubParser.sol index 7dcb73c..4c11293 100644 --- a/src/abstract/DiaSubParser.sol +++ b/src/abstract/DiaSubParser.sol @@ -39,10 +39,8 @@ abstract contract DiaSubParser is BaseRainterpreterSubParserNPE2 { } function buildOperandHandlerFunctionPointers() external pure returns (bytes memory) { - function(bytes32[] memory) internal pure returns (OperandV2)[] memory fs = new function(bytes32[] memory) - internal - pure - returns (OperandV2)[](SUB_PARSER_WORD_PARSERS_LENGTH); + function(bytes32[] memory) internal pure returns (OperandV2)[] memory fs = + new function(bytes32[] memory) internal pure returns (OperandV2)[](SUB_PARSER_WORD_PARSERS_LENGTH); fs[SUB_PARSER_WORD_DIA_PRICE] = LibParseOperand.handleOperandDisallowed; uint256[] memory pointers; @@ -57,13 +55,9 @@ abstract contract DiaSubParser is BaseRainterpreterSubParserNPE2 { } function buildSubParserWordParsers() external pure returns (bytes memory) { - function(uint256, uint256, OperandV2) - internal - view - returns (bool, bytes memory, bytes32[] memory)[] memory fs = new function(uint256, uint256, OperandV2) - internal - view - returns (bool, bytes memory, bytes32[] memory)[](SUB_PARSER_WORD_PARSERS_LENGTH); + function(uint256, uint256, OperandV2) internal view returns (bool, bytes memory, bytes32[] memory)[] memory fs = new function(uint256, uint256, OperandV2) + internal + view returns (bool, bytes memory, bytes32[] memory)[](SUB_PARSER_WORD_PARSERS_LENGTH); fs[SUB_PARSER_WORD_DIA_PRICE] = diaPriceSubParser; uint256[] memory pointers; @@ -80,8 +74,9 @@ abstract contract DiaSubParser is BaseRainterpreterSubParserNPE2 { returns (bool, bytes memory, bytes32[] memory) { // slither-disable-next-line unused-return - return LibSubParse.subParserExtern( - IInterpreterExternV4(extern()), constantsHeight, ioByte, operand, OPCODE_DIA_PRICE - ); + return + LibSubParse.subParserExtern( + IInterpreterExternV4(extern()), constantsHeight, ioByte, operand, OPCODE_DIA_PRICE + ); } } diff --git a/test/lib/LibFork.sol b/test/lib/LibFork.sol index e7d3762..8d8293c 100644 --- a/test/lib/LibFork.sol +++ b/test/lib/LibFork.sol @@ -5,6 +5,6 @@ pragma solidity ^0.8.25; string constant FORK_RPC_URL_BASE = "https://base.gateway.tenderly.co"; /// @dev A recent Base block. DIA oracle has AMZN data with timestamp -/// 1778908932. Tests use vm.warp to set block.timestamp close to the DIA -/// update time so staleness checks pass. +/// 1778908932. The fork block timestamp is close enough to that update for +/// staleness checks without vm.warp. uint256 constant FORK_BLOCK_BASE = 46061133; diff --git a/test/src/lib/dia/LibDia.t.sol b/test/src/lib/dia/LibDia.t.sol index c0f3ed2..8f98ac6 100644 --- a/test/src/lib/dia/LibDia.t.sol +++ b/test/src/lib/dia/LibDia.t.sol @@ -52,12 +52,20 @@ contract LibDiaStringV3Test is Test { } } +/// @dev Hardcoded prices are pinned to a Base fork snapshot (see comments on each test). +/// Oracle: 0xCE521b52513242c5094bc56f57887BB2A05B8129 (LibDia.ORACLE_BASE). +/// Fork block: 46061133 (block.timestamp 1778911613, 2026-05-16 06:06:53 UTC). +/// Reproduce: cast call 0xCE521b52513242c5094bc56f57887BB2A05B8129 +/// "getValue(string)(uint128,uint128)" SYMBOL --rpc-url https://mainnet.base.org --block 46061133 +/// When changing FORK_BLOCK_BASE in test/lib/LibFork.sol, re-run that command per symbol and +/// update the raw uint128 price literals below (first return value; 18 decimals). contract LibDiaGetPriceTest is Test { function setUp() external { vm.createSelectFork(FORK_RPC_URL_BASE, FORK_BLOCK_BASE); vm.chainId(8453); } + /// @dev Asserts getValue(key) raw price at the fork block matches `rawPrice` (18-decimal uint128). function _assertFeedPrice(string memory symbol, uint256 rawPrice) internal view { IntOrAString key = fromStringV3(symbol); Float staleAfter = LibDecimalFloat.packLossless(3600, 0); @@ -73,22 +81,27 @@ contract LibDiaGetPriceTest is Test { ); } + /// raw price 264100000000000022736 (~$264.10); update timestamp 1778908932 at fork block. function testGetPriceAmzn() external { _assertFeedPrice("AMZN", 264100000000000022736); } + /// raw price 225389999999999986352 (~$225.39); update timestamp 1778908933 at fork block. function testGetPriceNvda() external { _assertFeedPrice("NVDA", 225389999999999986352); } + /// raw price 195539999999999992048 (~$195.54); update timestamp 1778908934 at fork block. function testGetPriceCoin() external { _assertFeedPrice("COIN", 195539999999999992048); } + /// raw price 177455000000000012512 (~$177.46); update timestamp 1778908935 at fork block. function testGetPriceMstr() external { _assertFeedPrice("MSTR", 177455000000000012512); } + /// raw price 422350000000000022752 (~$422.35); update timestamp 1778908936 at fork block. function testGetPriceTsla() external { _assertFeedPrice("TSLA", 422350000000000022752); } From cd67af3d8c5adab585f00fd3f09a56ef8d70828b Mon Sep 17 00:00:00 2001 From: Siddharth2207 Date: Sat, 16 May 2026 13:50:25 +0530 Subject: [PATCH 5/5] pointers --- meta/DiaWords.rain.meta | Bin 288 -> 286 bytes src/generated/DiaWords.pointers.sol | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/meta/DiaWords.rain.meta b/meta/DiaWords.rain.meta index 5ea7cc86ace194bbeccc19bb658906f5ba1c5a72..903cbd09081509406a8d7fd627815dd90199ff37 100644 GIT binary patch delta 263 zcmV+i0r>u)0-geY{|br5bnbX`qySj$c$}S(u};J=5JZnZ8C?YuMB*w$MI@Rlpg>e~ z#-7Wqa2(5CAL8$E0zqjY5?i>i_sxuF^Y>uo{cMeQVyuvB*dbB`Ep{u1m5^jIT&P3BXiEC zYp`t}ULLm>Fu_f`Dm{myl}Dx>g3Aq_e>9F&;Q9uRR(j(Ry|MgWn8A1P9`*1gkK8WMWz?$C$GIDCvZ6$1l+7;u&0-yqa{|br5bnbX`qySj&c$}S(u}%Xq5JbbDj8cIFhm;CY5lDmRAbfza zC&>z*&$8D+{5`&dLsuY9$`&rXJ8x%a@qb~ae>SIgVy>a$sL1}5+lh4Qv@+T_rsGXkl<17I`7_}! N5;k{}0UQ63{30%`e^&qi diff --git a/src/generated/DiaWords.pointers.sol b/src/generated/DiaWords.pointers.sol index 1c3038b..1dab462 100644 --- a/src/generated/DiaWords.pointers.sol +++ b/src/generated/DiaWords.pointers.sol @@ -10,10 +10,10 @@ pragma solidity ^0.8.25; // file needs the contract to exist so that it can be compiled. /// @dev Hash of the known bytecode. -bytes32 constant BYTECODE_HASH = bytes32(0x157692d69cb425a3e6159c50374491d20546d625178b37ca2737df8dd3d838dc); +bytes32 constant BYTECODE_HASH = bytes32(0x53e6e58f065ec84a6b45d4ebb6e7ff03d1e27325b4a4eca65e7e2c50647116de); /// @dev The hash of the meta that describes the contract. -bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0xc865c530891587d866bcf511c911ef3847c5b94d31a4ca9c8f0cbca940e86f2f); +bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0xe5a735c20e9030c3d2727be05ed8f2f6121b9b6dcd252bb5d318d37498102881); /// @dev The parse meta that is used to lookup word definitions. /// The structure of the parse meta is: