Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/pyth-crosschain"]
path = lib/pyth-crosschain
url = https://github.com/pyth-network/pyth-crosschain
[submodule "lib/rain.math.float"]
path = lib/rain.math.float
url = https://github.com/rainlanguage/rain.math.float
[submodule "lib/rain.interpreter"]
path = lib/rain.interpreter
url = https://github.com/rainlanguage/rain.interpreter
[submodule "lib/pyth-crosschain"]
path = lib/pyth-crosschain
url = https://github.com/pyth-network/pyth-crosschain
3 changes: 2 additions & 1 deletion REUSE.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ path = [
"flake.nix",
"foundry.toml",
"slither.config.json",
"REUSE.toml"
"REUSE.toml",
"foundry.lock",
]
SPDX-FileCopyrightText = "Copyright (c) 2020 Rain Open Source Software Ltd"
SPDX-License-Identifier = "LicenseRef-DCL-1.0"
42 changes: 21 additions & 21 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions foundry.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"lib/forge-std": {
"rev": "17a9b2398a7d629931dc66a168a098d051d53dc6"
},
"lib/pyth-crosschain": {
"rev": "6248709ef4f5c20e4fb2828bc68789eb0164df99"
},
"lib/rain.interpreter": {
"rev": "6068857887731d910f3f1850c5f27248fce3a5d5"
}
}
4 changes: 2 additions & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ solc = "0.8.25"

remappings = [
"pyth-sdk/=lib/pyth-crosschain/target_chains/ethereum/sdk/solidity/",
"rain.math.float/=lib/rain.math.float/src/",
"rain.math.float/=lib/rain.interpreter/lib/rain.interpreter.interface/lib/rain.math.float/src/",
"rain.interpreter/=lib/rain.interpreter/src/",
"rain.metadata/=lib/rain.interpreter/lib/rain.metadata/src/",
"rain.sol.codegen/=lib/rain.interpreter/lib/rain.interpreter.interface/lib/rain.sol.codegen/src/",
Expand All @@ -19,7 +19,7 @@ fs_permissions = [
evm_version = "paris"

optimizer = true
optimizer_runs = 1000000
optimizer_runs = 1000

bytecode_hash = "none"
cbor_metadata = false
2 changes: 1 addition & 1 deletion lib/pyth-crosschain
Submodule pyth-crosschain updated 924 files
2 changes: 1 addition & 1 deletion lib/rain.interpreter
Submodule rain.interpreter updated 408 files
1 change: 0 additions & 1 deletion lib/rain.math.float
Submodule rain.math.float deleted from b506dd
16 changes: 10 additions & 6 deletions src/abstract/PythExtern.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity ^0.8.25;

import {BaseRainterpreterExternNPE2, Operand} from "rain.interpreter/abstract/BaseRainterpreterExternNPE2.sol";
import {
BaseRainterpreterExternNPE2,
OperandV2,
StackItem
} from "rain.interpreter/abstract/BaseRainterpreterExternNPE2.sol";
import {LibOpPythPrice} from "../lib/op/LibOpPythPrice.sol";
import {LibConvert} from "rain.lib.typecast/LibConvert.sol";
import {OPCODE_FUNCTION_POINTERS, INTEGRITY_FUNCTION_POINTERS} from "../generated/PythWords.pointers.sol";
Expand All @@ -21,13 +25,13 @@ abstract contract PythExtern is BaseRainterpreterExternNPE2 {
}

function buildOpcodeFunctionPointers() external pure returns (bytes memory) {
function(Operand, uint256[] memory)
function(OperandV2, StackItem[] memory)
internal
view
returns (uint256[] memory)[] memory fs = new function(Operand, uint256[] memory)
returns (StackItem[] memory)[] memory fs = new function(OperandV2, StackItem[] memory)
internal
view
returns (uint256[] memory)[](OPCODE_FUNCTION_POINTERS_LENGTH);
returns (StackItem[] memory)[](OPCODE_FUNCTION_POINTERS_LENGTH);
fs[OPCODE_PYTH_PRICE] = LibOpPythPrice.run;

uint256[] memory pointers;
Expand All @@ -38,10 +42,10 @@ abstract contract PythExtern is BaseRainterpreterExternNPE2 {
}

function buildIntegrityFunctionPointers() external pure returns (bytes memory) {
function(Operand, uint256, uint256)
function(OperandV2, uint256, uint256)
internal
pure
returns (uint256, uint256)[] memory fs = new function(Operand, uint256, uint256)
returns (uint256, uint256)[] memory fs = new function(OperandV2, uint256, uint256)
internal
pure
returns (uint256, uint256)[](OPCODE_FUNCTION_POINTERS_LENGTH);
Expand Down
20 changes: 10 additions & 10 deletions src/abstract/PythSubParser.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
pragma solidity ^0.8.25;

import {OPCODE_PYTH_PRICE} from "./PythExtern.sol";
import {Operand, BaseRainterpreterSubParserNPE2} from "rain.interpreter/abstract/BaseRainterpreterSubParserNPE2.sol";
import {OperandV2, BaseRainterpreterSubParserNPE2} from "rain.interpreter/abstract/BaseRainterpreterSubParserNPE2.sol";
import {LibParseOperand} from "rain.interpreter/lib/parse/LibParseOperand.sol";
import {SUB_PARSER_WORD_PARSERS_LENGTH, SUB_PARSER_WORD_PYTH_PRICE} from "../lib/parse/LibPythSubParser.sol";
import {LibConvert} from "rain.lib.typecast/LibConvert.sol";
import {LibSubParse} from "rain.interpreter/lib/parse/LibSubParse.sol";
import {IInterpreterExternV3} from "rain.interpreter.interface/interface/IInterpreterExternV3.sol";
import {IInterpreterExternV4} from "rain.interpreter.interface/interface/unstable/IInterpreterExternV4.sol";
import {
OPERAND_HANDLER_FUNCTION_POINTERS as SUB_PARSER_OPERAND_HANDLERS,
PARSE_META as SUB_PARSER_PARSE_META,
Expand Down Expand Up @@ -39,10 +39,10 @@ abstract contract PythSubParser is BaseRainterpreterSubParserNPE2 {
}

function buildOperandHandlerFunctionPointers() external pure returns (bytes memory) {
function(uint256[] memory) internal pure returns (Operand)[] memory fs = new function(uint256[] memory)
function(bytes32[] memory) internal pure returns (OperandV2)[] memory fs = new function(bytes32[] memory)
internal
pure
returns (Operand)[](SUB_PARSER_WORD_PARSERS_LENGTH);
returns (OperandV2)[](SUB_PARSER_WORD_PARSERS_LENGTH);
fs[SUB_PARSER_WORD_PYTH_PRICE] = LibParseOperand.handleOperandDisallowed;

uint256[] memory pointers;
Expand All @@ -57,13 +57,13 @@ abstract contract PythSubParser is BaseRainterpreterSubParserNPE2 {
}

function buildSubParserWordParsers() external pure returns (bytes memory) {
function(uint256, uint256, Operand)
function(uint256, uint256, OperandV2)
internal
view
returns (bool, bytes memory, uint256[] memory)[] memory fs = new function(uint256, uint256, Operand)
returns (bool, bytes memory, bytes32[] memory)[] memory fs = new function(uint256, uint256, OperandV2)
internal
view
returns (bool, bytes memory, uint256[] memory)[](SUB_PARSER_WORD_PARSERS_LENGTH);
returns (bool, bytes memory, bytes32[] memory)[](SUB_PARSER_WORD_PARSERS_LENGTH);
fs[SUB_PARSER_WORD_PYTH_PRICE] = pythPriceSubParser;

uint256[] memory pointers;
Expand All @@ -74,14 +74,14 @@ abstract contract PythSubParser is BaseRainterpreterSubParserNPE2 {
}

// slither-disable-next-line dead-code
function pythPriceSubParser(uint256 constantsHeight, uint256 ioByte, Operand operand)
function pythPriceSubParser(uint256 constantsHeight, uint256 ioByte, OperandV2 operand)
internal
view
returns (bool, bytes memory, uint256[] memory)
returns (bool, bytes memory, bytes32[] memory)
{
// slither-disable-next-line unused-return
return LibSubParse.subParserExtern(
IInterpreterExternV3(extern()), constantsHeight, ioByte, operand, OPCODE_PYTH_PRICE
IInterpreterExternV4(extern()), constantsHeight, ioByte, operand, OPCODE_PYTH_PRICE
);
}
}
18 changes: 9 additions & 9 deletions src/generated/PythWords.pointers.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity ^0.8.25;

// THIS FILE IS AUTOGENERATED BY ./script/BuildPointers.sol

// This file is committed to the repository because there is a circular
// dependency between the contract and its pointers file. The contract
// needs the pointers file to exist so that it can compile, and the pointers
// file needs the contract to exist so that it can be compiled.

// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity =0.8.25;

/// @dev Hash of the known bytecode.
bytes32 constant BYTECODE_HASH = bytes32(0xc2563d7f113f4586c661be35de1861e2a415f818178ecc8d568dd03a46eb9d92);
bytes32 constant BYTECODE_HASH = bytes32(0x61b6a9bc93d4d671ec1edf90e01875a8f2ca1b9b9963cfd7f2afc0b80b677edc);

/// @dev The hash of the meta that describes the contract.
bytes32 constant DESCRIBED_BY_META_HASH = bytes32(0xe7bb5842b2cf1d25681a9885109fbf8943495bcebb9ec049bc3790e5db57fa80);
Expand Down Expand Up @@ -42,18 +42,18 @@ uint8 constant PARSE_META_BUILD_DEPTH = 1;
/// bytecode that dials back into this contract at eval time, and mapping
/// to things that happen entirely on the interpreter such as well known
/// constants and references to the context grid.
bytes constant SUB_PARSER_WORD_PARSERS = hex"0782";
bytes constant SUB_PARSER_WORD_PARSERS = hex"0700";

/// @dev Every two bytes is a function pointer for an operand handler.
/// These positional indexes all map to the same indexes looked up in the parse
/// meta.
bytes constant OPERAND_HANDLER_FUNCTION_POINTERS = hex"087c";
bytes constant OPERAND_HANDLER_FUNCTION_POINTERS = hex"0aca";

/// @dev The function pointers for the integrity check fns.
bytes constant INTEGRITY_FUNCTION_POINTERS = hex"086f";
bytes constant INTEGRITY_FUNCTION_POINTERS = hex"0abf";

/// @dev The function pointers known to the interpreter for dynamic dispatch.
/// By setting these as a constant they can be inlined into the interpreter
/// and loaded at eval time for very low gas (~100) due to the compiler
/// optimising it to a single `codecopy` to build the in memory bytes array.
bytes constant OPCODE_FUNCTION_POINTERS = hex"0835";
bytes constant OPCODE_FUNCTION_POINTERS = hex"07b3";
24 changes: 13 additions & 11 deletions src/lib/op/LibOpPythPrice.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,39 @@
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity ^0.8.25;

import {Operand} from "rain.interpreter.interface/interface/deprecated/IInterpreterV2.sol";
import {OperandV2, StackItem} from "rain.interpreter.interface/interface/unstable/IInterpreterV4.sol";
import {LibIntOrAString, IntOrAString} from "rain.intorastring/lib/LibIntOrAString.sol";
import {LibPyth} from "../pyth/LibPyth.sol";
import {Float} from "rain.math.float/lib/LibDecimalFloat.sol";

library LibOpPythPrice {
using LibIntOrAString for IntOrAString;

/// Extern integrity for the Pyth price operation.
/// Always requires 2 inputs and produces 1 output.
function integrity(Operand, uint256, uint256) internal pure returns (uint256, uint256) {
return (2, 1);
/// Always requires 2 inputs and produces 2 outputs.
function integrity(OperandV2, uint256, uint256) internal pure returns (uint256, uint256) {
return (2, 2);
}

/// Runs the Pyth price operation.
/// @param inputs the inputs to the extern.
function run(Operand, uint256[] memory inputs) internal view returns (uint256[] memory) {
function run(OperandV2, StackItem[] memory inputs) internal view returns (StackItem[] memory) {
IntOrAString symbol;
uint256 staleAfter;
Float staleAfter;
assembly ("memory-safe") {
symbol := mload(add(inputs, 0x20))
staleAfter := mload(add(inputs, 0x40))
}

uint256 price18 = LibPyth.getPriceNoOlderThan(symbol, staleAfter);
(Float price, Float conf) = LibPyth.getPriceNoOlderThan(symbol, staleAfter);

uint256[] memory outputs;
StackItem[] memory outputs;
assembly ("memory-safe") {
outputs := mload(0x40)
mstore(0x40, add(outputs, 0x40))
mstore(outputs, 1)
mstore(add(outputs, 0x20), price18)
mstore(0x40, add(outputs, 0x60))
mstore(outputs, 2)
mstore(add(outputs, 0x20), price)
mstore(add(outputs, 0x40), conf)
}
Comment on lines +31 to 38
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Output array construction is correct; optional high-level alternative

The memory layout (len + 2 elements) and free memory pointer advance are correct.

Optional for readability:

-        StackItem[] memory outputs;
-        assembly ("memory-safe") {
-            outputs := mload(0x40)
-            mstore(0x40, add(outputs, 0x60))
-            mstore(outputs, 2)
-            mstore(add(outputs, 0x20), price)
-            mstore(add(outputs, 0x40), conf)
-        }
+        StackItem[] memory outputs = new StackItem[](2);
+        assembly ("memory-safe") {
+            mstore(add(outputs, 0x20), price)
+            mstore(add(outputs, 0x40), conf)
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
StackItem[] memory outputs;
assembly ("memory-safe") {
outputs := mload(0x40)
mstore(0x40, add(outputs, 0x40))
mstore(outputs, 1)
mstore(add(outputs, 0x20), price18)
mstore(0x40, add(outputs, 0x60))
mstore(outputs, 2)
mstore(add(outputs, 0x20), price)
mstore(add(outputs, 0x40), conf)
}
StackItem[] memory outputs = new StackItem[](2);
assembly ("memory-safe") {
mstore(add(outputs, 0x20), price)
mstore(add(outputs, 0x40), conf)
}
🤖 Prompt for AI Agents
In src/lib/op/LibOpPythPrice.sol around lines 31 to 38, the current inline
assembly that allocates and populates the outputs array is functionally correct;
if you prefer a clearer high-level alternative, replace the assembly with a
Solidity memory array allocation by creating a new StackItem[] of length 2,
assign the first element to price and the second to conf, and use that array as
outputs (removing the assembly block). Ensure the new allocation uses memory
(not storage) and that subsequent code expects the same two-element layout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that costs more gas

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thedavidmeister, understood! The inline assembly approach is more gas-efficient. I'll note that the current implementation is optimal for performance.

return outputs;
}
Expand Down
Loading