The Governor Bravo client enables interaction with governance systems built on Compound’s Governor Bravo protocol. It supports both gasless voting through signatures and direct transaction-based voting.
Installation
npm install @snapshot-labs/sx
Overview
Governor Bravo is a widely-used on-chain governance protocol originally developed by Compound. Many DAOs use Governor Bravo or its variants for on-chain governance.
The Governor Bravo client currently supports voting only . Proposal creation and execution must be done through direct contract interaction or other tools.
Client Types
The Governor Bravo client comes in two implementations:
GovernorBravoEthereumSig : Signature-based voting (gasless)
GovernorBravoEthereumTx : Transaction-based voting (direct)
GovernorBravoEthereumSig (Signature-based)
The signature-based client allows users to vote using EIP-712 signatures, which can be submitted by relayers for gasless voting.
Initialization
import { clients } from '@snapshot-labs/sx' ;
const client = new clients . GovernorBravoEthereumSig ({
chainId: 1 // Ethereum mainnet
});
Configuration
Chain ID for the network (1 for Ethereum mainnet, 11155111 for Sepolia)
Methods
vote()
Create a signed vote message that can be submitted to the Governor Bravo contract.
Vote For
Vote Against
Vote Abstain
Vote with Reason
import { Wallet } from '@ethersproject/wallet' ;
const signer = new Wallet ( privateKey );
const envelope = await client . vote ({
signer ,
authenticatorType: 'GovernorBravoAuthenticatorSignature' ,
data: {
spaceId: '0x...' , // Governor contract address
proposalId: 42 , // Proposal ID
choice: 1 // 1 = For
}
});
// The envelope contains signature data that can be submitted
// to the Governor contract's castVoteBySig function
console . log ( 'Signature:' , envelope . signatureData . signature );
signer
Signer & TypedDataSigner
required
Ethers.js signer with EIP-712 signing capabilities
authenticatorType
'GovernorBravoAuthenticatorSignature'
required
Must be set to ‘GovernorBravoAuthenticatorSignature’
Vote data object Governor Bravo contract address
Vote choice:
0 = Against
1 = For
2 = Abstain
Optional reason for the vote (max ~1000 characters recommended)
Returns an envelope containing the signature data and vote data EIP-712 signature that can be submitted to castVoteBySig
EIP-712 domain with governor name and chain ID
Signed message containing proposalId and support
Submitting Signed Votes
After creating a signed vote, it must be submitted to the Governor Bravo contract:
Submit via castVoteBySig
Submit with Reason
import { Contract } from '@ethersproject/contracts' ;
const governorAbi = [
'function castVoteBySig(uint256 proposalId, uint8 support, uint8 v, bytes32 r, bytes32 s)'
];
const governor = new Contract (
envelope . data . spaceId ,
governorAbi ,
provider
);
// Extract signature components
const sig = envelope . signatureData . signature ;
const r = sig . slice ( 0 , 66 );
const s = '0x' + sig . slice ( 66 , 130 );
const v = parseInt ( sig . slice ( 130 , 132 ), 16 );
// Submit the vote
const tx = await governor . castVoteBySig (
envelope . data . proposalId ,
envelope . data . choice ,
v ,
r ,
s
);
await tx . wait ();
console . log ( 'Vote submitted:' , tx . hash );
GovernorBravoEthereumTx (Transaction-based)
The transaction-based client allows direct voting through contract transactions.
Initialization
import { clients } from '@snapshot-labs/sx' ;
import { JsonRpcProvider } from '@ethersproject/providers' ;
const provider = new JsonRpcProvider ( 'https://eth.llamarpc.com' );
const client = new clients . GovernorBravoEthereumTx ({
provider
});
Methods
vote()
Submit a vote transaction directly to the Governor Bravo contract.
Vote Transaction
Vote with Reason
import { Wallet } from '@ethersproject/wallet' ;
const signer = new Wallet ( privateKey , provider );
const tx = await client . vote ({
signer ,
data: {
spaceId: '0x...' , // Governor contract address
proposalId: 42 ,
choice: 1 // 1 = For
}
});
await tx . wait ();
console . log ( 'Vote cast:' , tx . hash );
Ethers.js signer connected to a provider
Vote data object (same structure as signature-based client)
Returns an ethers.js transaction response that can be awaited
Type Definitions
Vote
type Vote = {
spaceId : string ; // Governor Bravo contract address
proposalId : number ; // Proposal ID
choice : 0 | 1 | 2 ; // 0=Against, 1=For, 2=Abstain
reason ?: string ; // Optional vote reason
};
EIP712Ballot
type EIP712Ballot = {
proposalId : number ;
support : number ; // Same as choice
};
type EIP712BallotWithReason = {
proposalId : number ;
support : number ;
reason : string ;
};
SignatureData
type SignatureData = {
authenticatorType : 'GovernorBravoAuthenticatorSignature' ;
address : string ;
signature : string ;
domain : TypedDataDomain ;
types : Record < string , TypedDataField []>;
message : Record < string , any >;
};
Governor Contract Compatibility
The client automatically detects the governor contract name for EIP-712 signing:
// The client queries the governor contract's name() function
const name = await governorContract . name ();
// e.g., "Compound Governor Bravo"
// Used in EIP-712 domain:
const domain = {
name: name ,
chainId: 1 ,
verifyingContract: '0x...'
};
For contracts without a name() function, custom names can be configured in the client source code.
Complete Example
Here’s a full example of voting on a Governor Bravo proposal:
import { clients } from '@snapshot-labs/sx' ;
import { Wallet } from '@ethersproject/wallet' ;
import { JsonRpcProvider } from '@ethersproject/providers' ;
import { Contract } from '@ethersproject/contracts' ;
// 1. Initialize client
const client = new clients . GovernorBravoEthereumSig ({
chainId: 1
});
// 2. Create signer
const provider = new JsonRpcProvider ( 'https://eth.llamarpc.com' );
const signer = new Wallet ( process . env . PRIVATE_KEY , provider );
// 3. Generate signed vote
const envelope = await client . vote ({
signer ,
authenticatorType: 'GovernorBravoAuthenticatorSignature' ,
data: {
spaceId: '0x...' , // Governor address
proposalId: 42 ,
choice: 1 ,
reason: 'Fully support this initiative'
}
});
// 4. Submit vote to Governor contract
const governorAbi = [
'function castVoteWithReasonBySig(uint256 proposalId, uint8 support, string reason, uint8 v, bytes32 r, bytes32 s)'
];
const governor = new Contract (
envelope . data . spaceId ,
governorAbi ,
provider
);
const sig = envelope . signatureData . signature ;
const r = sig . slice ( 0 , 66 );
const s = '0x' + sig . slice ( 66 , 130 );
const v = parseInt ( sig . slice ( 130 , 132 ), 16 );
const tx = await governor . castVoteWithReasonBySig (
envelope . data . proposalId ,
envelope . data . choice ,
envelope . data . reason || '' ,
v ,
r ,
s
);
await tx . wait ();
console . log ( 'Vote successfully cast:' , tx . hash );
Source Code
View the complete implementation: