# Sulkta-Coop/pallas — `feat-aux-data` branch notes This is a vendored fork of [txpipe/pallas](https://github.com/txpipe/pallas) based on the `v0.32.1` tag. It exists to fill in three upstream `// TODO` markers that block real-world Conway-era transaction building. The fork is consumed via `[patch.crates-io]` in [`Sulkta-Coop/aldabra`](http://192.168.0.5:3001/Sulkta-Coop/aldabra)'s workspace `Cargo.toml`. Once these changes are accepted upstream, the patch entry gets dropped and aldabra goes back to vanilla crates.io. ## What's added beyond `v0.32.1` ### 1. `pallas-txbuilder` — `auxiliary_data` field on `StagingTransaction` Upstream had `// pub auxiliary_data: TODO` on the struct and `auxiliary_data: None.into(), // TODO` in the conway builder. Without this, **CIP-20 / CIP-25 / CIP-68 metadata can't ride a tx built with pallas-txbuilder.** Added: - `pub auxiliary_data: Option>` field (opaque CBOR bytes — `pallas_primitives::AuxiliaryData` doesn't impl `Eq`, which the rest of `StagingTransaction` requires). - Builder methods `.auxiliary_data(cbor_bytes)` and `.clear_auxiliary_data()`. - `conway::build_conway_raw` now decodes the bytes via `AuxiliaryData::decode_fragment` and plumbs them in. The existing `auxiliary_data_hash` computation block (already in upstream) runs after assignment and populates the body's hash automatically. Tests added: - `auxiliary_data_round_trips_through_build` — encodes a CIP-25 shape, attaches, builds, decodes, asserts byte-equivalent re-encode + matching `auxiliary_data_hash`. - `no_auxiliary_data_means_no_hash` — negative path. ### 2. `pallas-txbuilder` — `certificates` field on `StagingTransaction` Upstream had `// pub certificates: TODO` and `certificates: None, // TODO` in the conway builder. Without this, **stake registration / delegation, pool registration, DRep ops, and Voltaire-era cert types can't be built.** Added: - `pub certificates: Option>>` field (opaque CBOR per cert, same reason as `auxiliary_data`). - Builder methods `.add_certificate(cbor_bytes)` and `.clear_certificates()`. - `conway::build_conway_raw` decodes each entry via `Certificate::decode_fragment` and plumbs them into `TransactionBody.certificates` via `NonEmptySet::from_vec`. Tests added: - `certificates_plumb_through_to_tx_body` — encodes a `StakeRegistration(AddrKeyhash([7;28]))`, builds, decodes, asserts the cert round-trips byte-for-byte. - `no_certificates_means_none` — negative path. ### 3. `pallas-txbuilder` — `voting_procedures` field on `StagingTransaction` Upstream had `voting_procedures: None, // TODO` in the conway builder. Without this, **DRep / SPO / committee voting on Conway governance actions can't ride a tx built with pallas-txbuilder.** Added 2026-05-06: - `pub voting_procedures: Option>` field (opaque CBOR for the same `Eq`-derive reason as `auxiliary_data`). - Builder methods `.voting_procedures(cbor_bytes)` and `.clear_voting_procedures()`. - `conway::build_conway_raw` decodes via `VotingProcedures::decode_fragment` and assigns to `TransactionBody.voting_procedures`. The `VotingProcedures` type is itself a single map (`NonEmptyKeyValuePairs>`), so callers pre-assemble the full map and encode once. No staged accumulator needed. Tests added: - `voting_procedures_plumb_through_to_tx_body` — encodes a single DRepKey-vote-Yes on a synthetic GovActionId, builds, decodes, asserts the vote round-trips byte-for-byte. - `no_voting_procedures_means_none` — negative path. ### 4. `pallas-addresses` — `pub fn new` on `StakeAddress` Upstream defined `pub struct StakeAddress(Network, StakePayload)` with **unexported tuple fields** and no `new()` constructor — so external callers can't construct one directly. `ShelleyAddress::new` exists already; this just matches the pattern. Without this, computing a wallet's reward (stake) address required round-tripping through a `ShelleyAddress` and `TryFrom` — clunky and indirect. ## How aldabra consumes this `Sulkta-Coop/aldabra/Cargo.toml` has: ```toml [patch.crates-io] pallas-codec = { git = "...Sulkta-Coop/pallas.git", branch = "feat-aux-data" } pallas-crypto = { git = "...Sulkta-Coop/pallas.git", branch = "feat-aux-data" } pallas-primitives = { git = "...Sulkta-Coop/pallas.git", branch = "feat-aux-data" } pallas-traverse = { git = "...Sulkta-Coop/pallas.git", branch = "feat-aux-data" } pallas-addresses = { git = "...Sulkta-Coop/pallas.git", branch = "feat-aux-data" } pallas-wallet = { git = "...Sulkta-Coop/pallas.git", branch = "feat-aux-data" } pallas-txbuilder = { git = "...Sulkta-Coop/pallas.git", branch = "feat-aux-data" } ``` All seven pallas crates we depend on are patched against the same commit so cargo's version graph resolves consistently. ## Upstream PR status **Not yet submitted.** Plan: 1. ✅ Land the changes locally on the fork. 2. ✅ Verify no upstream tests broken (`cargo test --workspace` passes — confirmed 2026-05-04). 3. ✅ Verify aldabra builds + tests against the fork (88 unit tests green). 4. ☐ Open PR against `txpipe/pallas` on github.com. 5. ☐ Once upstream merges, drop our `[patch.crates-io]`. The PR will probably need to be split into three separate PRs (one per change) for upstream review ergonomics: - `pallas-txbuilder: thread auxiliary_data through StagingTransaction → Conway build` - `pallas-txbuilder: thread certificates through StagingTransaction → Conway build` - `pallas-txbuilder: thread voting_procedures through StagingTransaction → Conway build` - `pallas-addresses: pub fn new on StakeAddress` ## Change discipline going forward If we add more fork-only patches (e.g. for upcoming withdrawals / voting_procedures / proposal_procedures TODOs), each goes in a separate commit on this branch so the eventual upstream PRs are clean to extract. All commits on this branch must: - Have a self-contained subject explaining what TODO it fills in. - Pass the full pallas-txbuilder test suite (`cargo test -p pallas-txbuilder`). - Add at least one positive + one negative test for the new code path. - Not depend on Sulkta-specific naming or assumptions — these patches need to be upstream-mergeable as-is.