Seedelf-Wallet

Chunk 21 plan: be your own DRep, and vote

The public account registers its own DRep, votes Yes, No or Abstain on live governance actions, keeps an optional profile, and retires to get its deposit back. Then the dApp connector learns CIP-95, so GovTool and the other governance sites work too. post-release-roadmap.md’s item 2, the last of public-side completeness, and the last chunk before the UX pass.

Status: built, 21a and 21b (2026-10-04). Branch web-wallet/governance, from main at 3a3deed (chunk 20 merged). What landed, and where it went differently from this plan, is under What was built. The owner ran it on preprod (2026-10-04): a DRep registered with its own vote, and a vote on an info action. Not done, and the owner’s: retiring live (and its move of the account’s own vote to Always abstain), and GovTool by hand.

Why

Chunk 13 delegated the vote and stopped there: Always abstain, Always no confidence, or a DRep. The owner (2026-10-02, and again 2026-10-04): voting is “a required feature for a complete wallet experience”. Registering is the gate on voting, not a sibling of it: a plain delegator casts no vote, DReps do. So the chunk is be your own DRep and vote, one thing (the correction, confirmed by the owner’s pick).

Decided with the owner (2026-10-04)

Question Decision
Native screens, CIP-95, or both? Native first, then CIP-95. The wallet’s own screens are the default, since no site sees the DRep key; CIP-95 in the connector is the route a user chooses, and it’s how Lace does it (Lace has no screens of its own; GovTool builds and Lace signs). May land as two PRs, 21a and 21b.
The DRep’s profile (CIP-119 metadata) None by default, optional. No anchor is all that voting your own stake needs. An optional name and bio make the wallet write the CIP-119 file and its hash, for the user to host; they paste its address. The wallet never fetches it, so no new host.
Delegate the account’s own vote on registering? Yes, in the same transaction, by default, with a switch to keep the current delegation. Without it the account’s own stake doesn’t count toward its own votes.
Where governance lives on the public side (the owner, 2026-10-04) One row and one page, renamed “Staking and governance”. Home’s row says the pool and its rewards, and “Voting power: …” (“Your own DRep” when it’s the account’s).
Governance at connect, for a site that asks for it A switch on Your public account, off by default, by the owner’s standing rule that the most private option is the default. Declined, a site isn’t asked again until it connects anew, and keeps its connection.
Moving a vote that’s on another DRep to your own (the owner’s review, 2026-10-04: “I have to search for me basically”) Voting power gets a “Your own DRep” choice, always shown, between the two Always options and A DRep. A DRep already: it delegates there, review and Send. Not one yet: it says what registering costs and that it moves the vote in the same transaction, and opens Become a DRep. And the DRep card gets “Delegate your voting power to it” when the vote goes elsewhere. Both build the same vote delegation, from the DRep the Staking page already read: no request.

Found before building (2026-10-04)

Lace (_reference/lace, 2.4.2) has no DRep or voting screens. It derives the DRep key at account'/3/0 (KeyRole.DRep in cardano-key-agent.ts, ROLE_DREP = 3 in its Keystone paths) and offers it over CIP-95: cip95.getPubDRepKey, getRegisteredPubStakeKeys, getUnregisteredPubStakeKeys, and signData taking a DRep ID. It signs what the site builds, with the DRep key when the transaction needs it (getUniqueSigners.ts collects DRep credentials from certificates and voters).

The key is CIP-105’s, m/1852'/1815'/account'/3/0, so a phrase restored in Lace, Eternl or Yoroi is the same DRep. CIP-105’s test vector 1 (test walk nut penalty hip pave soap entry language right filter choice, account 0) pins it: verification key f74d7ac3…7a752b, key hash a5b45515…b832aa, CIP-129 ID drep1y2jmg4g450lced7q9n34rq6d5vjwkm0ugx6h0894u6ur92s9txn3a. Adding the role changes nothing that’s derived today: roles 0–2 are the same keys, and the Seedelf key (derivation.rs) is untouched.

Pallas 0.35 has every type needed (pallas-primitives conway): Certificate::RegDRepCert(cred, deposit, anchor), UnRegDRepCert(cred, deposit), UpdateDRepCert(cred, anchor); the body’s voting_procedures: Option<VotingProcedures>, a map of Voter::DRepKey(hash) to a map of GovActionId { transaction_id, action_index } to VotingProcedure { vote: Yes | No | Abstain, anchor }. The builder can stage none of it, as with chunk 13’s certificates, so the same patch sets them after the build.

The transaction view already decodes all of it (chunk 17, decode.rs): DRep registration, update and retirement, and votes. Every review and the connector’s sign window show them with no new work.

The connector offers no extensions today: supportedExtensions is [] and getExtensions answers [] (content/page.ts, background/dapp.ts). GovTool can’t use this wallet until 21b.

Koios, checked live:

The ledger’s rules, which the builders keep

Scope

21a · Core (seedelf-core/src/staking.rs)

21a · Key derivation (seedelf-crypto/src/cardano.rs)

21a · WebAssembly

21a · Worker

21a · UI

21b · CIP-95 in the connector

Koios requests

When Requests
Opening Staking 2 (was 1): pool_info, and drep_info for the account’s own DRep
Governance actions 1 proposal_list, then none for an hour; +1 drep_info (its standing alone); +1 vote_list for a DRep’s own votes
A DRep or vote build 5: account_addresses, credential_utxos, account_info, epoch_params, drep_info
CIP-95’s stake key lists 1 account_info

Nothing in the background, and nothing on Home.

Tests and checks

Docs

privacy.md (Known links: the DRep and its votes tie to the account), flows.md (a Governance section), architecture.md (the voting-procedures patch, the DRep key), keys-and-accounts.md (role 3), the README, and the store listing’s wording if the owner wants it to say so. The manifest doesn’t change for 21a. The CSP doesn’t either, since nothing new is fetched.

Not in it

What was built

21a, the wallet’s own screens (2026-10-04). Everything in Scope under 21a. Checked: cargo test --workspace --locked 502 passed (8 new), clippy and fmt clean; the WebAssembly’s Node tests (a new one); Vitest 1,403 passed (21 new, in governance.test.ts and governance-screen.test.ts); Playwright 68 passed (two new: becoming a DRep with the account’s own vote, and a DRep voting). The probe (tests/fixtures/probe-governance.mjs) had preprod’s node decode every kind: a registration with the account’s own vote, with a profile and alone, and registering the stake key too; an update; a retirement moving the account’s vote to always abstain; a vote on all seven live actions. Fees 0.17–0.18 ₳.

Where it went differently from the plan:

21b, CIP-95 in the connector (2026-10-04). Everything in Scope under 21b, and Settings’ Connected sites marks a site that has governance. Checked: cargo test --workspace --locked 510 passed (four new in cip30_test.rs: the DRep key signing the account’s own registration and vote only with the grant, a retirement’s refund counted, and data signed as the DRep by each form of its ID); Vitest 1,408 passed (five new in dapp-governance.test.ts); Playwright 69 passed (one new: a site asks for CIP-95, the window says what it gives, and the site gets the DRep key).

Where it went differently from the plan:

The fix round (2026-10-04), after the owner’s own test (a DRep registered with its own vote, and a vote on an info action, on preprod) and an independent review of the whole diff: