feat(proxy-oracle): Pyth Lazer SEP-40 source and batcher for the Soroban stack (ENG-691) - #623
Draft
peer2f00l wants to merge 2 commits into
Draft
feat(proxy-oracle): Pyth Lazer SEP-40 source and batcher for the Soroban stack (ENG-691)#623peer2f00l wants to merge 2 commits into
peer2f00l wants to merge 2 commits into
Conversation
…ban stack (ENG-691) Pyth's Lazer contract on Stellar is a stateless verifier (verify_update(Bytes) -> Bytes, no stored prices, no replay or freshness protection), so it cannot be a SEP-40 source for the proxy oracle directly. - pyth-lazer-source-contract: SEP-40 source over the verifier. Permissionless update_price_feeds verifies, enforces channel and a freshness window on the payload and each feed's own update time, and stores mapped feeds whose update time strictly advances. One feed_id <-> Asset map per instance. - batcher-contract: stateless fan-out of the runtime's permissionless refresh / extend_ttl and sibling extend_ttl()s, renewing each target's instance and code TTL, so a keeper needs one Soroban op per sweep. - common: RefreshStatus, bucket_timestamp, extend_persistent_ttl, owner_upgrade, MAX_SEP40_DECIMALS and ProxyOracleMaintenanceTrait shared by all contracts (ABI-neutral for the runtime and adapter). - pyth-lazer-stellar-sdk 0.3.0 vendored verbatim into the pyth-lazer-public fork on soroban-sdk 25 (crates.io needs soroban-sdk 26.1 / Rust >= 1.91); both git deps pin fork tag templar/stellar-sdk-0.3.0-soroban25. - justfile, release manifest and dry-run cover five artifacts; scripts share one artifact table. scripts/e2e_live.sh drives a live rehearsal. - Docs: RedStone's SEP-40 adapter is CBMGLKUQ...WYRF (its per-feed contracts are not SEP-40); RUNBOOK constructor ordering and max_age_secs example fixed. Verified on Stellar testnet end to end against the real Pyth, Reflector and RedStone contracts: signed payload -> three-source SetProxy -> refresh Accepted -> adapter lastprice; batcher fan-out. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CJusKazQ9PNya7ntYaQL9U
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueComment |
… failed uploads
The stellar CLI's `mainnet` network alias is a placeholder ("Bring Your Own"
RPC), so `--network mainnet` fails; pass --rpc-url and the passphrase instead.
A failed `contract upload` used to be recorded as an empty hash and the
following deploy then targeted hash 0000…; record ids only on success.
Both surfaced on the mainnet rehearsal, which then passed end to end.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CJusKazQ9PNya7ntYaQL9U
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes ENG-691.
Pyth's Lazer (Pyth Pro) contract on Stellar is a stateless verifier —
verify_update(Bytes) -> Byteswith no stored prices, no replay protection, no freshness check — so it cannot be a SEP-40 source for the Soroban proxy oracle directly. This adds the contract that sits in between, plus what a keeper and a deployment need around it.New contracts
pyth-lazer-source-contract— SEP-40 source over Pyth's verifier.update_price_feeds(payload)is permissionless (authenticity is cryptographic): verify through the configured verifier, require the configured channel, gate the payload and every feed's own update time on a freshness window, and store each mapped feed whose update time strictly advances (anti-replay). Onefeed_id ↔ Assetmap per instance, so one deployment backs every asset's proxy. Owner-gatedadd_feed/remove_feed/set_freshness/set_decimals/upgrade; permissionlessextend_ttl. Optimized wasm ≈ 25.7 KiB.batcher-contract— stateless fan-out of the runtime's permissionlessrefresh/extend_ttl(and siblingextend_ttl()s), renewing every target's instance and code TTL along the way. Stellar allows one Soroban op per transaction, so this gives a keeper two transactions per cycle regardless of asset count, with no separatestellar contract extendstep. ≈ 6.6 KiB.Shared changes
RefreshStatus,bucket_timestamp,extend_persistent_ttl,owner_upgrade,MAX_SEP40_DECIMALS, and aProxyOracleMaintenanceTrait(refresh/extend_ttl) move intosoroban/common; the runtime and adapter use them (ABI-neutral — same exports, same names).pyth-lazer-stellar-sdk0.3.0 is vendored verbatim into thepyth-lazer-publicfork on soroban-sdk 25 (crates.io needs soroban-sdk 26.1 → Rust ≥ 1.91; the 1.93 migration is blocked by a soroban-sdk/near-sdkarbitrarylockfile conflict, see ENG-691). Both git deps now pin fork tagtemplar/stellar-sdk-0.3.0-soroban25(7ee94541…, recorded in Cargo.lock)._build-wasm,_optimize, a size-check loop); release manifest and dry-run scripts share one artifact table (scripts/release_artifacts.py, manifestschema_version3) and the manifest takes--wasm-dir.scripts/e2e_live.sh: resumable live rehearsal (deploy → configure → push → refresh) with a mainnet write guard.CBMGLKUQ…WYRF(its per-feed contracts are not SEP-40); RUNBOOK constructor ordering andmax_age_secsexample fixed (the old example dropped Reflector on every refresh); AUDIT scope and AGENTS entry.Verified
End to end on testnet and mainnet with the real contracts: a Pyth-signed
leEcdsapayload verified on-chain → three-sourceSetProxy(Reflector + RedStone + Lazer) →refresh=Accepted(median-low of the three) → SEP-40 adapterlastprice; batcher fan-out incl. code-TTL renewal. Mainnet XLM/USD at refresh: Lazer 0.18624346, RedStone 0.18629441 (accepted), Reflector 0.18640258 — 0.085% spread. Mainnet rehearsal contracts (zero-timelock, deployer-admin; not the production instance): runtimeCB2VNEE4…ZTQT, governanceCBPZLC6K…7J7L, Lazer sourceCBR2JTRQ…GBHS, batcherCA2B2BN7…RRUS, adapterCDN7JPOO…YIVX.Measured mainnet cost: WASM upload rent ≈ 1.3 XLM/KB (runtime 126 XLM, governance 51 XLM); budget ≈ 250 XLM for a fresh five-artifact deployment.
Unit: source 21, common 8, runtime 67, governance 27, adapter 25; integration suite incl. new
lazer_source(source + batcher against the real runtime). Clippy clean with-D warnings;just size-checkgreen.Not in this PR
Mainnet deployment (needs a funded identity), the NEAR-free Lazer source crate extraction for the keeper, the keeper itself.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CJusKazQ9PNya7ntYaQL9U
This change is