@@ -4,12 +4,14 @@ import {
44 applyParamsToScript ,
55 serializePlutusScript ,
66 resolvePlutusScriptHash ,
7+ serializeRewardAddress ,
78 deserializeAddress ,
89 mConStr0 ,
910 mConStr1 ,
1011 type UTxO ,
1112 type BrowserWallet ,
1213} from "@meshsdk/core" ;
14+ import { getPythScriptHash } from "@pythnetwork/pyth-lazer-cardano-js" ;
1315
1416import {
1517 UNPARAMETERISED_SCRIPT_CBOR ,
@@ -70,6 +72,17 @@ export async function buildBurnTx(
7072 if ( pythUtxos . length === 0 ) throw new Error ( "Pyth State NFT UTxO not found" ) ;
7173 const stateUtxo = pythUtxos [ 0 ] ;
7274
75+ // Read the withdraw script hash dynamically from the Pyth State inline datum.
76+ const withdrawScriptHash = getPythScriptHash ( stateUtxo as any ) ;
77+ const withdrawAddress = serializeRewardAddress ( withdrawScriptHash , true , 0 ) ;
78+
79+ // Find the UTxO at the Pyth State address that has the withdraw script published as a reference script.
80+ const allPythUtxos : UTxO [ ] = await provider . fetchAddressUTxOs ( PYTH . STATE_ADDRESS ) ;
81+ const withdrawRefUtxo = allPythUtxos . find (
82+ ( u ) => u . output . scriptHash === withdrawScriptHash
83+ ) ;
84+ if ( ! withdrawRefUtxo ) throw new Error ( "Pyth withdraw reference script UTxO not found" ) ;
85+
7386 // User UTxOs — for synth token input and collateral.
7487 const walletUtxos : UTxO [ ] = await wallet . getUtxos ( ) ;
7588 const collateral : UTxO [ ] = await wallet . getCollateral ( ) ;
@@ -93,10 +106,18 @@ export async function buildBurnTx(
93106
94107 // ── 3. Build datums and redeemers ─────────────────────────────────────────
95108
96- // Keep the existing pool datum (owner stays the same).
97- // In Mesh "Data" format: ByteArray is a plain hex string, Constr is mConStr0.
98- const ownerPkh = deserializeAddress ( walletAddress ) . pubKeyHash ;
99- const poolDatum = mConStr0 ( [ ownerPkh ] ) ;
109+ // Read owner PKH from the pool datum — this is what the validator checks.
110+ // PoolDatum is Constr(0, [owner_pkh_hex]).
111+ const datumOwnerPkh : string = ( poolUtxo . output . plutusData as any ) ?. fields ?. [ 0 ] ?? "" ;
112+ if ( ! datumOwnerPkh ) throw new Error ( "Could not read owner from pool datum" ) ;
113+
114+ // Fail fast if the connected wallet is not the position owner.
115+ const walletPkh = deserializeAddress ( walletAddress ) . pubKeyHash ;
116+ if ( walletPkh !== datumOwnerPkh )
117+ throw new Error ( "Connected wallet is not the position owner" ) ;
118+
119+ // Preserve the existing datum owner when writing back (owner never changes).
120+ const poolDatum = mConStr0 ( [ datumOwnerPkh ] ) ;
100121
101122 // Action.Burn — Constr(1, [])
102123 const burnRedeemer = mConStr1 ( [ ] ) ;
@@ -137,9 +158,16 @@ export async function buildBurnTx(
137158 . readOnlyTxInReference ( stateUtxo . input . txHash , stateUtxo . input . outputIndex )
138159
139160 // Zero-ADA withdrawal from Pyth verify script — carries the signed price message.
140- . withdrawal ( PYTH . WITHDRAW_ADDRESS , "0" )
161+ // Address and script hash derived dynamically from the Pyth State datum.
162+ // Script is referenced by UTxO (no CBOR needed).
163+ . withdrawal ( withdrawAddress , "0" )
141164 . withdrawalPlutusScriptV3 ( )
142- . withdrawalScript ( PYTH . WITHDRAW_SCRIPT_CBOR )
165+ . withdrawalTxInReference (
166+ withdrawRefUtxo . input . txHash ,
167+ withdrawRefUtxo . input . outputIndex ,
168+ String ( withdrawRefUtxo . output . scriptRef ?. length ? withdrawRefUtxo . output . scriptRef . length / 2 : 0 ) ,
169+ withdrawScriptHash
170+ )
143171 . withdrawalRedeemerValue ( pythRedeemer , "Mesh" )
144172
145173 // Collateral + change.
@@ -151,7 +179,7 @@ export async function buildBurnTx(
151179 )
152180 . changeAddress ( walletAddress )
153181 . selectUtxosFrom ( walletUtxos )
154- . requiredSignerHash ( ownerPkh ) // Burn requires owner signature (on-chain check)
182+ . requiredSignerHash ( datumOwnerPkh ) // Burn requires owner signature (on-chain check)
155183 . complete ( ) ;
156184
157185 const unsignedTx = txBuilder . txHex ;
0 commit comments