Conversation
Integrate pyth_price validator
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| // The output contains state NFT and nothing else, except ADA. We can't | ||
| // check policy ID (minting script hash) here without introducing circular | ||
| // dependency, so malicious entity could send here a token from a different | ||
| // policy, but as long as we only trust state tied to a well known policy | ||
| // ID, this spending script will always ensure that updates to it are valid. | ||
| expect [(id, asset, 1)] = | ||
| assets.without_lovelace(input.value) |> assets.flatten() | ||
| expect (asset == config.state_nft)? |
There was a problem hiding this comment.
📝 Info: State NFT policy ID cannot be validated in spend handler (documented design limitation)
The state.spend function at state.ak:132-134 extracts the single non-ADA asset from the input and uses its policy ID to locate the output, but cannot verify the policy ID matches the expected minting script hash without creating a circular dependency. The code documents this limitation clearly at lines 127-131. Security relies on the fact that only the legitimate minting script can produce tokens with the correct policy ID (since the origin UTxO is consumed during minting). This is a known Cardano pattern for avoiding circular script references.
Was this helpful? React with 👍 or 👎 to provide feedback.
lazer/contracts/cardano/lib/pyth.ak
Outdated
| redeemers: Pairs<ScriptPurpose, Redeemer>, | ||
| ) -> List<PriceUpdate> { | ||
| expect Some(redeemer) = pairs.get_first(redeemers, Withdraw(Script(script))) | ||
| expect updates: List<ByteArray> = redeemer | ||
| list.map( | ||
| updates, | ||
| fn(message) { | ||
| let message = message.parse_without_verification(message) | ||
| parse_update(message.payload) | ||
| }, | ||
| ) | ||
| } | ||
|
|
||
| fn parse_update(payload: ByteArray) -> PriceUpdate { |
There was a problem hiding this comment.
📝 Info: get_updates intentionally skips signature verification
The get_updates function at lazer/contracts/cardano/lib/pyth.ak:26-39 calls message.parse_without_verification rather than message.parse_and_verify. This is intentional: it's an SDK helper for consumer contracts to read price updates from a transaction's redeemers after the pyth_price withdraw validator (lazer/contracts/cardano/validators/pyth_price.ak:15-33) has already verified each update's signature and signer trust. Duplicating verification on the consumer side would waste execution budget. The variable shadowing on line 35 (let message = message.parse_without_verification(message)) is valid Aiken — the module message is used for the function call, and the parameter message is used as the argument.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Moved shared logic to https://github.com/pyth-network/pyth-lazer-cardano repo - we can merge its PR first (pyth-network/pyth-lazer-cardano#1). |
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
| utxo: OutputReference, | ||
| self: Transaction, | ||
| ) { | ||
| let old, _ <- state.spend(config, old, utxo, self) |
There was a problem hiding this comment.
📝 Info: Wormhole guardian set upgrade doesn't require owner NFT
In lazer/contracts/cardano/validators/wormhole_state.ak:36, the is_owner boolean from state.spend is discarded with _. This means anyone can submit a guardian set upgrade VAA. This is intentionally correct — guardian set upgrades are authenticated by the VAA itself (signed by the current guardian set quorum), and the contract at wormhole/governance.ak:31 enforces old.set_index + 1 == new.set_index. Not flagged as a bug because the security comes from VAA verification, not owner NFT.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const state = spender.receive(ctx.parameters, stateNFT, { | ||
| deprecated_withdraw_scripts: new Map(), | ||
| governance: { ...initial, seen_sequence: 0n }, | ||
| trusted_signers: new Map(), | ||
| withdraw_script: new Uint8Array(), | ||
| }); |
There was a problem hiding this comment.
📝 Info: Pyth state initialized with empty withdraw_script
In lazer/contracts/cardano/sdk/js/src/transactions.ts:108, the initial Pyth state is created with withdraw_script: new Uint8Array() (empty bytes). This means no withdraw script is active at deployment time, so price verification via the zero-withdraw trick won't work until a governance action sets one. The is_active_withdraw_script function at lazer/contracts/cardano/validators/pyth_state.ak:26-35 would fail to match any credential against an empty hash. This appears intentional (deploy first, configure via governance), but operators should be aware that the contract is non-functional for price verification immediately after deployment.
Was this helpful? React with 👍 or 👎 to provide feedback.
governance CLI, docs
Summary
This is a big PR, merging Cardano work that was previously in a separate branch to
main. There's more to do when it comes to SDK and building transactions off-chain, but contract itself should be in a place where the design is set and implemented.How has this been tested?