Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
fabb0e6
feat: multiple instructions per crank
Dodecahedr0x Jun 22, 2026
924fab4
feat: ephemeral cranks
Dodecahedr0x Jun 24, 2026
47639ad
test: e2e ephemeral
Dodecahedr0x Jun 25, 2026
6c8a777
feat: remove magicsvm tests
Dodecahedr0x Jun 26, 2026
13fa05d
feat: extend SDK
Dodecahedr0x Jun 26, 2026
db02270
feat: api expose both programs
Dodecahedr0x Jun 26, 2026
f3327a5
fix: make modules public
Dodecahedr0x Jun 26, 2026
01b6c86
feat: move args out of client
Dodecahedr0x Jun 26, 2026
09b3c8e
feat: relax version
Dodecahedr0x Jun 29, 2026
e80dba8
feat: split programs
Dodecahedr0x Jun 30, 2026
1f24ace
feat: refund ephemeral cranks
Dodecahedr0x Jun 30, 2026
7355686
test: exact funding
Dodecahedr0x Jul 1, 2026
1250362
style: lint
Dodecahedr0x Jul 1, 2026
139bb39
feat: remove unused lockfile
Dodecahedr0x Jul 1, 2026
a70d17a
feat: untrack lockfile
Dodecahedr0x Jul 1, 2026
17e6a01
feat: unsafe mode
Dodecahedr0x Jul 2, 2026
dceaa48
fix: writability conflict tracking
Dodecahedr0x Jul 2, 2026
001fb1f
fix: create account when unfunded
Dodecahedr0x Jul 2, 2026
1da5ce5
Merge branch 'dode/multiple-instructions' into dode/ephemeral-cranks
Dodecahedr0x Jul 3, 2026
15a6f76
fix: delegate cranker
Dodecahedr0x Jul 6, 2026
8b4a5db
Merge branch 'main' into dode/ephemeral-cranks
Dodecahedr0x Jul 6, 2026
76cf159
fix: conflicts
Dodecahedr0x Jul 6, 2026
cb62015
fix: account meta
Dodecahedr0x Jul 6, 2026
25ab7a8
fix: divergent economics
Dodecahedr0x Jul 6, 2026
a3a2269
feat: gate ephemeral close
Dodecahedr0x Jul 6, 2026
307f1fd
fix: remove obsolete error
Dodecahedr0x Jul 6, 2026
fd06710
feat: remove incompatible feature
Dodecahedr0x Jul 6, 2026
213513b
fix: anchor example in CI
Dodecahedr0x Jul 6, 2026
fbbd432
docs: fix header
Dodecahedr0x Jul 6, 2026
bd29172
feat: use body_len
Dodecahedr0x Jul 6, 2026
cba4277
fix: client
Dodecahedr0x Jul 6, 2026
d5d68a4
feat: add makefile
Dodecahedr0x Jul 6, 2026
c3b10f2
fix: increase file limits
Dodecahedr0x Jul 6, 2026
7e38e4a
fix: update validators
Dodecahedr0x Jul 7, 2026
07d584a
feat: clean processes
Dodecahedr0x Jul 7, 2026
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
118 changes: 118 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
name: CI

on:
push:
branches: [main]
pull_request:

# Cancel superseded runs on the same ref to save runner minutes.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -D warnings
# Pinned Solana/Agave toolchain that ships `cargo build-sbf`.
SOLANA_VERSION: stable

jobs:
# ---------------------------------------------------------------------------
# Formatting — covers all root workspace members, including `tests/e2e`.
# ---------------------------------------------------------------------------
fmt:
name: rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust (rustfmt)
uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.91.1
components: rustfmt

- name: Check formatting
run: make fmt-check

# ---------------------------------------------------------------------------
# Default feature set: lint + tests with the `ephemeral` feature OFF.
# ---------------------------------------------------------------------------
default:
name: lint + test (default)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust (clippy)
uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.91.1
components: clippy,rustfmt

- name: Install Solana toolchain (build-sbf)
run: |
sh -c "$(curl -sSfL https://release.anza.xyz/${SOLANA_VERSION}/install)"
echo "$HOME/.local/share/solana/install/active_release/bin" >> "$GITHUB_PATH"

- name: Install cargo-nextest
uses: taiki-e/install-action@nextest

- uses: Swatinem/rust-cache@v2

- name: Default CI checks
run: make ci

# ---------------------------------------------------------------------------
# Live end-to-end: boots the real three-process stack (mb-test-validator +
# ephemeral-validator + hydra-cranker) and asserts ephemeral cranks fire on
# schedule. The validators ship as the `@magicblock-labs/ephemeral-validator`
# npm package (mb-test-validator wraps `solana-test-validator`, which the Anza
# toolchain provides). The `tests/e2e` crate is its own `[workspace]`.
# ---------------------------------------------------------------------------
e2e:
name: e2e (ephemeral cranks, live validators)
runs-on: ubuntu-latest
env:
# Pin to the version the test was developed against.
EPHEMERAL_VALIDATOR_VERSION: 0.13.3
steps:
- uses: actions/checkout@v4

- name: Install Rust (clippy)
uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.91.1
components: clippy

- name: Install Solana toolchain (build-sbf + solana-test-validator)
run: |
sh -c "$(curl -sSfL https://release.anza.xyz/${SOLANA_VERSION}/install)"
echo "$HOME/.local/share/solana/install/active_release/bin" >> "$GITHUB_PATH"

- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 20

- name: Install MagicBlock validators (mb-test-validator + ephemeral-validator)
run: |
npm install -g "@magicblock-labs/ephemeral-validator@${EPHEMERAL_VALIDATOR_VERSION}"
mb-test-validator --version
ephemeral-validator --version

- uses: Swatinem/rust-cache@v2
Comment on lines +27 to +104

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚖️ Poor tradeoff

Consider pinning third-party actions to commit SHAs.

actions/checkout@v4, dtolnay/rust-toolchain@master, taiki-e/install-action@nextest, Swatinem/rust-cache@v2, and actions/setup-node@v4 are all tag/branch refs, which are mutable. Pinning to a commit SHA hardens against supply-chain tampering, at the cost of manual bumps (particularly painful for @master).

🧰 Tools
🪛 zizmor (1.26.1)

[warning] 27-27: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[warning] 45-45: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[warning] 80-80: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)


[warning] 41-72: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block

(excessive-permissions)


[error] 27-27: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 30-30: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 45-45: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 48-48: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 59-59: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 61-61: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 80-80: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 83-83: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 94-94: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[error] 104-104: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)


[info] 30-30: action functionality is already included by the runner (superfluous-actions): use rustup and/or cargo in a script step

(superfluous-actions)


[info] 48-48: action functionality is already included by the runner (superfluous-actions): use rustup and/or cargo in a script step

(superfluous-actions)


[info] 83-83: action functionality is already included by the runner (superfluous-actions): use rustup and/or cargo in a script step

(superfluous-actions)


[warning] 100-100: ad-hoc installation of packages (adhoc-packages): installs a package outside of a lockfile

(adhoc-packages)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 27 - 104, The CI workflow still uses
mutable action refs, so update the workflows in ci.yml to pin each third-party
action to an immutable commit SHA instead of tags or branches. Replace the uses
entries for actions/checkout, dtolnay/rust-toolchain, taiki-e/install-action,
Swatinem/rust-cache, and actions/setup-node with their corresponding
commit-pinned versions, and keep the existing step structure and names
unchanged.

Source: Linters/SAST tools

with:
workspaces: |
.
tests/e2e

- name: Clippy — e2e crate
run: make lint-e2e

- name: Test — live ephemeral cranks
run: |
sudo prlimit --pid $$ --nofile=1048576:1048576
sudo sysctl fs.inotify.max_user_instances=1280
sudo sysctl fs.inotify.max_user_watches=655360
make test-e2e
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/target
target/
# Anchor example is its own workspace with its own target dir.
/examples/anchor/target
Cargo.lock
Expand Down
24 changes: 18 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ resolver = "2"
default-members = ["programs/hydra"]
members = [
"programs/hydra",
"programs/hydra-ephemeral",
"crates/hydra-api",
"crates/hydra-cranker",
"tests",
"tests/e2e",
"tests/programs/noop",
"examples/native",
"examples/pinocchio",
Expand All @@ -22,21 +24,32 @@ license = "MIT"
repository = "https://github.com/magicblock-labs/hydra"

[workspace.dependencies]
pinocchio = { version = "0.11", features = ["cpi"] }
pinocchio-system = "0.6"
pinocchio = { version = "0.10", features = ["cpi"] }
pinocchio-system = "0.5"
pinocchio-log = "0.5"
solana-address = { version = "2.6", features = ["decode", "curve25519"] }
# CPI helpers for MagicBlock ephemeral accounts (used by the ephemeral-crank
# instructions). Tracks pinocchio 0.10, which is why the program is pinned to
# 0.10 rather than 0.11.
ephemeral-rollups-pinocchio = "0.15"
solana-address = { version = "2", features = ["decode", "curve25519"] }
solana-define-syscall = "5.0"
hydra-api = { path = "crates/hydra-api" }

# tests only:
mollusk-svm = "=0.12.1-agave-4.0"
mollusk-svm-programs-memo = "=0.12.1-agave-4.0"
solana-svm-log-collector = { version = "4.0.0-beta", features = ["agave-unstable-api"] }
solana-svm-log-collector = { version = "4.0.0-beta", features = [
"agave-unstable-api",
] }
solana-logger = "3"
solana-account = "3.0"
solana-instruction = "3.0"
solana-pubkey = "4.0"
solana-account = "3.0"
solana-keypair = "3.1"
solana-message = "3.0.1"
solana-signer = "3.0"
solana-transaction = "3.0"
solana-hash = "3.1.0"
solana-system-interface = { version = "2.0", features = ["bincode"] }

[profile.release]
Expand All @@ -47,5 +60,4 @@ overflow-checks = true
[workspace.lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = [
'cfg(target_os, values("solana"))',
'cfg(feature, values("create-account-allow-prefund", "custom-alloc", "custom-panic", "no-entrypoint", "logging"))',
] }
108 changes: 108 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Hydra — build & test commands.
#
# Quickstart:
# make build # compile the on-chain programs (needed before tests)
# make test # run the hydra-tests suite
# make ci # everything the default CI job runs, locally
#
# Run `make` or `make help` for the full list.
#
# Toolchain prerequisites:
# - Rust + `cargo build-sbf` (Solana/Anza toolchain)
# - cargo-nextest -> `make install-tools`
# - anchor CLI -> only for `make build-examples`
# - @magicblock-labs/ephemeral-validator (npm) -> only for `make test-e2e`

SHELL := /bin/bash
.DEFAULT_GOAL := help

# Manifests for the crates that live outside the default workspace build.
BASE_MANIFEST := programs/hydra/Cargo.toml
EPHEMERAL_MANIFEST := programs/hydra-ephemeral/Cargo.toml
NOOP_MANIFEST := tests/programs/noop/Cargo.toml
NATIVE_MANIFEST := examples/native/Cargo.toml
PINOCCHIO_MANIFEST := examples/pinocchio/Cargo.toml
ANCHOR_MANIFEST := examples/anchor/Cargo.toml
E2E_MANIFEST := tests/e2e/Cargo.toml

CLIPPY := --all-targets -- -D warnings

.PHONY: help
help: ## Show this help
@grep -hE '^[a-zA-Z0-9_-]+:.*?## ' $(MAKEFILE_LIST) \
| awk 'BEGIN{FS=":.*?## "}{printf " \033[36m%-16s\033[0m %s\n", $$1, $$2}'

# ---------------------------------------------------------------------------
# Build — on-chain SBF programs (artifacts land in target/deploy/*.so).
# ---------------------------------------------------------------------------
.PHONY: build build-base build-ephemeral build-noop build-examples

build: build-base build-ephemeral ## build-sbf the base + ephemeral hydra programs

build-base: build-noop ## build-sbf the base hydra program
cargo build-sbf --manifest-path $(BASE_MANIFEST)

build-ephemeral: build-noop ## build-sbf the ephemeral-rollup hydra program
cargo build-sbf --manifest-path $(EPHEMERAL_MANIFEST)

build-noop: ## build-sbf the noop test program (target of scheduled ixs)
cargo build-sbf --manifest-path $(NOOP_MANIFEST)

build-examples: ## build-sbf the native + pinocchio example programs
cargo build-sbf --manifest-path $(NATIVE_MANIFEST)
cargo build-sbf --manifest-path $(PINOCCHIO_MANIFEST)
cd examples/anchor && anchor build

# ---------------------------------------------------------------------------
# Format & lint (mirrors the fmt + default CI jobs).
# ---------------------------------------------------------------------------
.PHONY: fmt fmt-check lint lint-e2e lint-ephemeral

fmt: ## Format the workspace
cargo fmt --all

fmt-check: ## Check formatting without writing (CI)
cargo fmt --all --check

lint: ## Clippy the workspace and check the excluded anchor example
cargo clippy --workspace $(CLIPPY)
cargo check --manifest-path $(ANCHOR_MANIFEST) --all-targets

lint-e2e: ## Clippy only the e2e crate
cargo clippy --manifest-path $(E2E_MANIFEST) $(CLIPPY)

# ---------------------------------------------------------------------------
# Test. `hydra-tests` and the example mollusk tests load the compiled .so
# files at runtime, so the build targets are prerequisites.
# ---------------------------------------------------------------------------
.PHONY: test test-examples test-e2e test-all bench cu-table

test: build-base ## Run the hydra-tests suite (unit + integration, via nextest)
cargo nextest run -p hydra-tests

test-examples: build-base build-examples ## Run the native + pinocchio example mollusk tests
cargo nextest run -p hydra-example-native -p hydra-example-pinocchio

test-e2e: build-ephemeral build-noop ## Live e2e: spawns validators + cranker (needs the ephemeral-validator npm pkg)
cargo test --manifest-path $(E2E_MANIFEST) -- --ignored --nocapture --test-threads=1

test-all: test test-examples test-e2e ## Run hydra-tests, examples, and live e2e

bench: build-base ## Run the compute-unit benchmarks
cargo bench -p hydra-tests

cu-table: build-base ## Print the per-instruction CU table (the ignored cu_table test)
cargo test -p hydra-tests cu_table -- --ignored --nocapture

# ---------------------------------------------------------------------------
# Aggregate / housekeeping.
# ---------------------------------------------------------------------------
.PHONY: ci install-tools clean

ci: fmt-check lint build test ## Run the default CI job locally (fmt-check + lint + build + test)

install-tools: ## Install cargo-nextest (Solana/anchor/node toolchains are installed separately)
cargo install cargo-nextest --locked

clean: ## Remove Cargo build artifacts
cargo clean
Loading
Loading