Governance Transactions
Participate in Cardano's on-chain governance through DRep delegation, registration, and voting.
Overview
Cardano's on-chain governance system (CIP-1694) enables the community to vote on proposals and protocol updates. You can delegate your voting power to Decentralized Representatives (DReps), register as a DRep yourself, and vote on governance actions.
When to use this:
- Delegating your voting power to a DRep
- Registering as a DRep to represent other voters
- Voting on governance actions and protocol changes
- Building governance dashboards and tools
- Creating DRep management applications
Quick Start
import { MeshTxBuilder, BlockfrostProvider } from "@meshsdk/core";
// Initialize
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
// Get wallet data
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const rewardAddresses = await wallet.getRewardAddresses();
const rewardAddress = rewardAddresses[0]!;
// Delegate to a DRep
txBuilder
.voteDelegationCertificate(
{ dRepId: "drep1yv4uesaj92wk8ljlsh4p7jzndnzrflchaz5fzug3zxg4naqkpeas3" },
rewardAddress
)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);API Reference
voteDelegationCertificate()
Delegate voting power to a DRep.
.voteDelegationCertificate(drep: DRepId, rewardAddress: string)| Parameter | Type | Description |
|---|---|---|
drep | DRepId | DRep identifier object |
rewardAddress | string | Your stake/reward address |
The DRepId object can specify:
{ dRepId: "drep1..." }- Delegate to a specific DRep{ alwaysAbstain: true }- Always abstain from voting{ alwaysNoConfidence: true }- Always vote no confidence
drepRegistrationCertificate()
Register as a DRep.
.drepRegistrationCertificate(dRepId: string, anchor?: Anchor)| Parameter | Type | Description |
|---|---|---|
dRepId | string | Your DRep ID (from wallet) |
anchor | Anchor | Optional metadata anchor |
The Anchor object:
{
anchorUrl: string; // URL to metadata JSON
anchorDataHash: string; // Hash of metadata content
}drepUpdateCertificate()
Update DRep metadata.
.drepUpdateCertificate(dRepId: string, anchor?: Anchor)| Parameter | Type | Description |
|---|---|---|
dRepId | string | Your DRep ID |
anchor | Anchor | New metadata anchor |
drepDeregistrationCertificate()
Retire as a DRep and reclaim deposit.
.drepDeregistrationCertificate(dRepId: string)| Parameter | Type | Description |
|---|---|---|
dRepId | string | Your DRep ID to retire |
vote()
Cast a vote on a governance action.
.vote(voter: Voter, govActionId: GovActionId, votingProcedure: VotingProcedure)| Parameter | Type | Description |
|---|---|---|
voter | Voter | Voter identity |
govActionId | GovActionId | Governance action reference |
votingProcedure | VotingProcedure | Vote details |
The Voter object:
// DRep voter
{ type: "DRep", drepId: "drep1..." }
// Stake pool operator
{ type: "StakePool", poolId: "pool1..." }
// Constitutional committee member
{ type: "ConstitutionalCommittee", hotCred: "..." }The VotingProcedure object:
{
voteKind: "Yes" | "No" | "Abstain";
anchor?: {
anchorUrl: string;
anchorDataHash: string;
};
}votePlutusScriptV3()
Indicate voting with a Plutus script.
.votePlutusScriptV3()voteScript()
Provide the voting script.
.voteScript(scriptCbor: string)voteRedeemerValue()
Provide the redeemer for script-based voting.
.voteRedeemerValue(redeemer: Data | object | string, type?: "Mesh" | "CBOR" | "JSON", exUnits?: Budget)Common Patterns
Delegate to a DRep
Delegate your voting power to a registered DRep:
import { MeshTxBuilder, BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const rewardAddresses = await wallet.getRewardAddresses();
const rewardAddress = rewardAddresses[0]!;
const dRepId = "drep1yv4uesaj92wk8ljlsh4p7jzndnzrflchaz5fzug3zxg4naqkpeas3";
txBuilder
.voteDelegationCertificate({ dRepId }, rewardAddress)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log(`Delegated to DRep: ${txHash}`);Delegate to Always Abstain
Configure your stake to always abstain from voting:
import { MeshTxBuilder, BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const rewardAddresses = await wallet.getRewardAddresses();
const rewardAddress = rewardAddresses[0]!;
txBuilder
.voteDelegationCertificate({ alwaysAbstain: true }, rewardAddress)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);Register as a DRep
Register yourself as a DRep with metadata:
import { MeshTxBuilder, BlockfrostProvider, hashDrepAnchor } from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
// Get DRep ID from wallet
const dRep = await wallet.getDRep();
const dRepId = dRep.dRepIDCip105;
// Prepare metadata anchor
const anchorUrl = "https://example.com/drep-metadata.jsonld";
const anchorMetadata = {
"@context": {
"@language": "en",
"CIP100": "https://github.com/cardano-foundation/CIPs/blob/master/CIP-0100/README.md",
"CIP119": "https://github.com/cardano-foundation/CIPs/blob/master/CIP-0119/README.md",
"hashAlgorithm": "CIP100:hashAlgorithm",
"body": { "@id": "CIP119:body" },
"givenName": "CIP119:givenName"
},
"hashAlgorithm": "blake2b-256",
"body": {
"givenName": "My DRep Name"
}
};
const anchorHash = hashDrepAnchor(anchorMetadata);
// Get wallet data
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
txBuilder
.drepRegistrationCertificate(dRepId, {
anchorUrl,
anchorDataHash: anchorHash,
})
.changeAddress(changeAddress)
.selectUtxosFrom(utxos);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log(`DRep registered: ${txHash}`);Update DRep Metadata
Update your DRep profile information:
import { MeshTxBuilder, BlockfrostProvider, hashDrepAnchor } from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
const dRep = await wallet.getDRep();
const dRepId = dRep.dRepIDCip105;
// New metadata
const anchorUrl = "https://example.com/drep-metadata-v2.jsonld";
const anchorMetadata = { /* updated metadata */ };
const anchorHash = hashDrepAnchor(anchorMetadata);
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
txBuilder
.drepUpdateCertificate(dRepId, {
anchorUrl,
anchorDataHash: anchorHash,
})
.changeAddress(changeAddress)
.selectUtxosFrom(utxos);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log(`DRep updated: ${txHash}`);Retire as DRep
Retire from being a DRep and reclaim your deposit:
import { MeshTxBuilder, BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
const dRep = await wallet.getDRep();
const dRepId = dRep.dRepIDCip105;
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
txBuilder
.drepDeregistrationCertificate(dRepId)
.selectUtxosFrom(utxos)
.changeAddress(changeAddress);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log(`DRep retired: ${txHash}`);Vote on Governance Action
Cast a vote as a DRep:
import { MeshTxBuilder, BlockfrostProvider } from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
const dRep = await wallet.getDRep();
const dRepId = dRep.dRepIDCip105;
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
// Governance action to vote on
const govActionTxHash = "aff2909f8175ee02a8c1bf96ff516685d25bf0c6b95aac91f4dfd53a5c0867cc";
const govActionIndex = 0;
txBuilder
.vote(
{
type: "DRep",
drepId: dRepId,
},
{
txHash: govActionTxHash,
txIndex: govActionIndex,
},
{
voteKind: "Yes",
}
)
.selectUtxosFrom(utxos)
.changeAddress(changeAddress);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);
console.log(`Vote cast: ${txHash}`);Vote with Rationale
Include reasoning for your vote:
import { MeshTxBuilder, BlockfrostProvider, hashDrepAnchor } from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
const dRep = await wallet.getDRep();
const dRepId = dRep.dRepIDCip105;
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
// Prepare vote rationale
const rationaleUrl = "https://example.com/vote-rationale.jsonld";
const rationaleContent = {
rationale: "I support this proposal because...",
references: ["https://example.com/supporting-doc"],
};
const rationaleHash = hashDrepAnchor(rationaleContent);
txBuilder
.vote(
{
type: "DRep",
drepId: dRepId,
},
{
txHash: "abc123...",
txIndex: 0,
},
{
voteKind: "Yes",
anchor: {
anchorUrl: rationaleUrl,
anchorDataHash: rationaleHash,
},
}
)
.selectUtxosFrom(utxos)
.changeAddress(changeAddress);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
const txHash = await wallet.submitTx(signedTx);Vote with Plutus Script
Vote using a Plutus script DRep:
import {
MeshTxBuilder,
BlockfrostProvider,
resolveScriptHash,
resolveScriptHashDRepId,
applyCborEncoding
} from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
const txBuilder = new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
const utxos = await wallet.getUtxos();
const collateral = await wallet.getCollateral();
const changeAddress = await wallet.getChangeAddress();
// Your voting script
const votingScriptCbor = applyCborEncoding("your-voting-script-cbor");
const scriptHash = resolveScriptHash(votingScriptCbor, "V3");
const scriptDRepId = resolveScriptHashDRepId(scriptHash);
txBuilder
.txIn(
utxos[0]!.input.txHash,
utxos[0]!.input.outputIndex,
utxos[0]!.output.amount,
utxos[0]!.output.address
)
.txInCollateral(
collateral[0]!.input.txHash,
collateral[0]!.input.outputIndex,
collateral[0]!.output.amount,
collateral[0]!.output.address
)
.votePlutusScriptV3()
.vote(
{
type: "DRep",
drepId: scriptDRepId,
},
{
txHash: "abc123...",
txIndex: 0,
},
{
voteKind: "Yes",
}
)
.voteScript(votingScriptCbor)
.voteRedeemerValue("")
.changeAddress(changeAddress);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx, true);
const txHash = await wallet.submitTx(signedTx);Complete Example
This example demonstrates a complete DRep lifecycle:
import {
MeshTxBuilder,
BlockfrostProvider,
hashDrepAnchor
} from "@meshsdk/core";
const provider = new BlockfrostProvider("<YOUR_API_KEY>");
function getTxBuilder() {
return new MeshTxBuilder({
fetcher: provider,
verbose: true,
});
}
// Register as a DRep
async function registerDRep(
wallet: any,
name: string,
metadataUrl: string
) {
const txBuilder = getTxBuilder();
const dRep = await wallet.getDRep();
const dRepId = dRep.dRepIDCip105;
const metadata = {
"@context": { /* CIP-119 context */ },
"body": { "givenName": name }
};
const anchorHash = hashDrepAnchor(metadata);
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
txBuilder
.drepRegistrationCertificate(dRepId, {
anchorUrl: metadataUrl,
anchorDataHash: anchorHash,
})
.changeAddress(changeAddress)
.selectUtxosFrom(utxos);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
return await wallet.submitTx(signedTx);
}
// Vote on a governance action
async function castVote(
wallet: any,
govActionTxHash: string,
govActionIndex: number,
voteKind: "Yes" | "No" | "Abstain"
) {
const txBuilder = getTxBuilder();
const dRep = await wallet.getDRep();
const dRepId = dRep.dRepIDCip105;
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
txBuilder
.vote(
{ type: "DRep", drepId: dRepId },
{ txHash: govActionTxHash, txIndex: govActionIndex },
{ voteKind }
)
.selectUtxosFrom(utxos)
.changeAddress(changeAddress);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
return await wallet.submitTx(signedTx);
}
// Delegate to a DRep
async function delegateToDRep(wallet: any, dRepId: string) {
const txBuilder = getTxBuilder();
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
const rewardAddresses = await wallet.getRewardAddresses();
const rewardAddress = rewardAddresses[0]!;
txBuilder
.voteDelegationCertificate({ dRepId }, rewardAddress)
.changeAddress(changeAddress)
.selectUtxosFrom(utxos);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
return await wallet.submitTx(signedTx);
}
// Retire as DRep
async function retireDRep(wallet: any) {
const txBuilder = getTxBuilder();
const dRep = await wallet.getDRep();
const dRepId = dRep.dRepIDCip105;
const utxos = await wallet.getUtxos();
const changeAddress = await wallet.getChangeAddress();
txBuilder
.drepDeregistrationCertificate(dRepId)
.selectUtxosFrom(utxos)
.changeAddress(changeAddress);
const unsignedTx = await txBuilder.complete();
const signedTx = await wallet.signTx(unsignedTx);
return await wallet.submitTx(signedTx);
}
// Usage
async function main() {
// 1. Register as DRep
const regTx = await registerDRep(
wallet,
"Community DRep",
"https://example.com/metadata.jsonld"
);
console.log(`Registered as DRep: ${regTx}`);
// 2. Vote on proposals
const voteTx = await castVote(
wallet,
"proposal-tx-hash...",
0,
"Yes"
);
console.log(`Vote cast: ${voteTx}`);
// 3. Others delegate to you
const delegateTx = await delegateToDRep(
otherWallet,
"drep1..." // Your DRep ID
);
console.log(`Delegation received: ${delegateTx}`);
// 4. Eventually retire
const retireTx = await retireDRep(wallet);
console.log(`Retired as DRep: ${retireTx}`);
}Troubleshooting
"DRep not registered" error
- You must register as a DRep before voting
- Use
drepRegistrationCertificate()first
"Invalid anchor hash" error
- The hash must match the content at the URL exactly
- Use
hashDrepAnchor()to compute the correct hash - Ensure the metadata file is publicly accessible
"Insufficient deposit" error
- DRep registration requires a deposit (check current protocol parameters)
- Ensure wallet has enough ADA for deposit + fees
"DRep inactive" error
- DReps must vote regularly to stay active
- Vote on governance actions or submit an update certificate
"Governance action not found" error
- Verify the transaction hash and index are correct
- The governance action may have already been resolved
"Invalid voter type" error
- Ensure the voter type matches your role (DRep, SPO, or CC)
- Use the correct identifier format for each type
Related
- Transaction Basics — Core transaction building
- Staking — Stake delegation
- CIP-1694 — Governance specification
- CIP-119 — DRep metadata standard