The NebGov SDK provides TypeScript clients for interacting with the NebGov on-chain governance framework on Stellar. This document covers all exported classes, methods, types, and provides complete usage examples.
npm install @nebgov/sdk
# or
pnpm add @nebgov/sdkInteract with a deployed NebGov governor contract for proposal management and voting.
constructor(config: GovernorConfig)Parameters:
config: GovernorConfig- Configuration object containing contract addresses and network settings
Example:
import { GovernorClient } from "@nebgov/sdk";
const client = new GovernorClient({
governorAddress: "CABC...",
timelockAddress: "CDEF...",
votesAddress: "CGHI...",
network: "testnet",
rpcUrl: "https://custom-rpc.example.com" // optional
});Create a new governance proposal.
async propose(signer: Keypair, description: string): Promise<bigint>Parameters:
signer: Keypair- Stellar keypair authorized to create proposalsdescription: string- Human-readable description of the proposal
Returns: Promise<bigint> - The ID of the created proposal
Example:
const proposalId = await client.propose(keypair, "Upgrade protocol fee to 0.3%");
console.log(`Proposal created with ID: ${proposalId}`);Cast a vote on an active proposal.
async castVote(signer: Keypair, proposalId: bigint, support: VoteSupport): Promise<void>Parameters:
signer: Keypair- Stellar keypair of the voterproposalId: bigint- ID of the proposal to vote onsupport: VoteSupport- Vote direction (For, Against, or Abstain)
Returns: Promise<void>
Example:
import { VoteSupport } from "@nebgov/sdk";
await client.castVote(keypair, 1n, VoteSupport.For);Get the current state of a proposal.
async getProposalState(proposalId: bigint): Promise<ProposalState>Parameters:
proposalId: bigint- ID of the proposal to query
Returns: Promise<ProposalState> - Current state of the proposal
Example:
const state = await client.getProposalState(1n);
console.log(`Proposal state: ${state}`);Get vote breakdown for a proposal.
async getProposalVotes(proposalId: bigint): Promise<ProposalVotes>Parameters:
proposalId: bigint- ID of the proposal to query
Returns: Promise<ProposalVotes> - Vote counts for/against/abstain
Example:
const votes = await client.getProposalVotes(1n);
console.log(`For: ${votes.votesFor}, Against: ${votes.votesAgainst}, Abstain: ${votes.votesAbstain}`);Get total number of proposals.
async proposalCount(): Promise<bigint>Returns: Promise<bigint> - Total number of proposals created
Example:
const count = await client.proposalCount();
console.log(`Total proposals: ${count}`);Interact with the token-votes contract for delegation and voting power queries.
constructor(config: GovernorConfig)Parameters:
config: GovernorConfig- Configuration object (same as GovernorClient)
Example:
import { VotesClient } from "@nebgov/sdk";
const votesClient = new VotesClient({
governorAddress: "CABC...",
timelockAddress: "CDEF...",
votesAddress: "CGHI...",
network: "testnet"
});Delegate voting power to another address.
async delegate(signer: Keypair, delegatee: string): Promise<void>Parameters:
signer: Keypair- Stellar keypair of the delegatordelegatee: string- Stellar address to delegate voting power to
Returns: Promise<void>
Example:
await votesClient.delegate(keypair, "GDEF...");Get current voting power of an address.
async getVotes(account: string): Promise<bigint>Parameters:
account: string- Stellar address to query
Returns: Promise<bigint> - Current voting power
Example:
const votingPower = await votesClient.getVotes("GDEF...");
console.log(`Voting power: ${votingPower}`);Get voting power at a past ledger sequence.
async getPastVotes(account: string, ledger: number): Promise<bigint>Parameters:
account: string- Stellar address to queryledger: number- Past ledger sequence number
Returns: Promise<bigint> - Voting power at the specified ledger
Example:
const pastPower = await votesClient.getPastVotes("GDEF...", 500000);
console.log(`Voting power at ledger 500000: ${pastPower}`);Get current delegatee of an account.
async getDelegatee(account: string): Promise<string | null>Parameters:
account: string- Stellar address to query
Returns: Promise<string | null> - Address of the current delegatee, or null if self-delegated
Example:
const delegatee = await votesClient.getDelegatee("GDEF...");
if (delegatee) {
console.log(`Delegated to: ${delegatee}`);
} else {
console.log("Self-delegated");
}Interact with a deployed NebGov timelock contract for delayed execution of governance actions.
constructor(config: GovernorConfig)Parameters:
config: GovernorConfig- Configuration object (same as GovernorClient)
Example:
import { TimelockClient } from "@nebgov/sdk";
const timelockClient = new TimelockClient({
governorAddress: "CABC...",
timelockAddress: "CDEF...",
votesAddress: "CGHI...",
network: "testnet"
});Schedule a timelock operation.
async schedule(signer: Keypair, target: string, data: Buffer, delay: bigint): Promise<string>Parameters:
signer: Keypair- Keypair authorising the call (must be the governor signer)target: string- Strkey address of the contract to invoke on executiondata: Buffer- Encoded calldata for the target invocationdelay: bigint- Delay in seconds; must be >= the contract'sminDelay
Returns: Promise<string> - Hex-encoded operation ID (SHA-256 of data)
Example:
const calldata = Buffer.from("encoded_function_call", "utf8");
const opId = await timelockClient.schedule(keypair, "TARGET...", calldata, 86400n);
console.log(`Operation scheduled with ID: ${opId}`);Execute a ready timelock operation.
async execute(signer: Keypair, opId: string): Promise<void>Parameters:
signer: Keypair- Keypair authorising the call (must be the governor signer)opId: string- Hex-encoded operation ID returned byschedule()
Returns: Promise<void>
Example:
await timelockClient.execute(keypair, "abc123...");Cancel a pending timelock operation.
async cancel(signer: Keypair, opId: string): Promise<void>Parameters:
signer: Keypair- Keypair authorising the call (admin or governor signer)opId: string- Hex-encoded operation ID returned byschedule()
Returns: Promise<void>
Example:
await timelockClient.cancel(keypair, "abc123...");Check whether an operation is ready for execution.
async isReady(opId: string): Promise<boolean>Parameters:
opId: string- Hex-encoded operation ID
Returns: Promise<boolean> - True if operation is ready to execute
Example:
const ready = await timelockClient.isReady("abc123...");
if (ready) {
console.log("Operation is ready for execution");
}Check whether an operation is pending.
async isPending(opId: string): Promise<boolean>Parameters:
opId: string- Hex-encoded operation ID
Returns: Promise<boolean> - True if operation is pending (delay not yet elapsed)
Example:
const pending = await timelockClient.isPending("abc123...");
if (pending) {
console.log("Operation is still pending");
}Get the minimum enforced delay for new operations.
async minDelay(): Promise<bigint>Returns: Promise<bigint> - Minimum delay in seconds
Example:
const minDelay = await timelockClient.minDelay();
console.log(`Minimum delay: ${minDelay} seconds`);The SDK provides utilities for subscribing to governance events.
Subscribe to new propose events emitted by a NebGov governor contract.
function subscribeToProposals(
governorAddress: string,
callback: (event: SorobanEvent) => void,
opts: SubscriptionOptions
): () => voidParameters:
governorAddress: string- Strkey contract address of the governorcallback: (event: SorobanEvent) => void- Invoked with each decoded proposal eventopts: SubscriptionOptions- Network, optional RPC URL, and polling interval
Returns: () => void - Unsubscribe function to stop polling
Example:
const unsub = subscribeToProposals(
"CABC...",
(event) => console.log("New proposal!", event),
{ network: "testnet" }
);
// Later...
unsub(); // Stop listeningSubscribe to vote events on a specific proposal.
function subscribeToVotes(
governorAddress: string,
proposalId: bigint,
callback: (event: SorobanEvent) => void,
opts: SubscriptionOptions
): () => voidParameters:
governorAddress: string- Strkey contract address of the governorproposalId: bigint- The proposal to watch for votescallback: (event: SorobanEvent) => void- Invoked with each decoded vote eventopts: SubscriptionOptions- Network, optional RPC URL, and polling interval
Returns: () => void - Unsubscribe function to stop polling
Example:
const unsub = subscribeToVotes(
"CABC...",
1n,
(event) => console.log("Vote cast!", event),
{ network: "testnet" }
);
// Later...
unsub(); // Stop listeningFetch historical propose events from a governor contract.
async function getProposalEvents(
governorAddress: string,
fromLedger: number,
opts: SubscriptionOptions
): Promise<SorobanEvent[]>Parameters:
governorAddress: string- Strkey contract address of the governorfromLedger: number- Ledger sequence to start scanning fromopts: SubscriptionOptions- Network and optional RPC URL
Returns: Promise<SorobanEvent[]> - Array of decoded proposal events
Example:
const events = await getProposalEvents("CABC...", 500_000, {
network: "testnet"
});
console.log(`Found ${events.length} historical proposals`);type Network = "mainnet" | "testnet" | "futurenet";enum ProposalState {
Pending = "Pending",
Active = "Active",
Defeated = "Defeated",
Succeeded = "Succeeded",
Queued = "Queued",
Executed = "Executed",
Cancelled = "Cancelled",
}enum VoteSupport {
Against = 0,
For = 1,
Abstain = 2,
}interface GovernorConfig {
/** Contract address of the governor */
governorAddress: string;
/** Contract address of the timelock */
timelockAddress: string;
/** Contract address of the token-votes contract */
votesAddress: string;
/** Stellar network to connect to */
network: Network;
/** RPC URL override (optional — defaults to public horizon) */
rpcUrl?: string;
}interface Proposal {
id: bigint;
proposer: string;
description: string;
startLedger: number;
endLedger: number;
votesFor: bigint;
votesAgainst: bigint;
votesAbstain: bigint;
executed: boolean;
cancelled: boolean;
}interface ProposalVotes {
votesFor: bigint;
votesAgainst: bigint;
votesAbstain: bigint;
}interface TimelockOperation {
id: string; // hex-encoded operation hash
target: string;
readyAt: bigint;
executed: boolean;
cancelled: boolean;
}interface TreasuryTx {
id: bigint;
proposer: string;
target: string;
approvals: number;
executed: boolean;
cancelled: boolean;
}interface SorobanEvent {
/** Ledger sequence the event was emitted in */
ledger: number;
/** Contract that emitted the event */
contractId: string;
/** Decoded topic segments (symbol strings) */
topic: string[];
/** Decoded event body value */
value: unknown;
}interface SubscriptionOptions {
/** Stellar network to connect to */
network: Network;
/** RPC URL override (optional — defaults to public endpoint) */
rpcUrl?: string;
/**
* Polling interval in milliseconds.
* @default 10_000
*/
intervalMs?: number;
}Thrown when an unknown proposal state variant is encountered.
class UnknownProposalStateError extends Error {
constructor(variant: string)
}When thrown: When decoding a proposal state from the contract returns an unrecognized variant.
Example:
try {
const state = await client.getProposalState(proposalId);
} catch (error) {
if (error instanceof UnknownProposalStateError) {
console.error(`Unknown proposal state: ${error.message}`);
}
}All client methods that submit transactions may throw errors for:
- Network connectivity issues
- Transaction failures (insufficient balance, invalid signatures, etc.)
- Simulation errors (contract reverts, invalid parameters)
- Transaction confirmation timeouts
Example:
try {
await client.propose(keypair, "My proposal");
} catch (error) {
console.error("Proposal failed:", error.message);
// Handle different error types appropriately
}This example demonstrates a complete governance workflow: deploy → propose → vote → execute.
import {
GovernorClient,
VotesClient,
TimelockClient,
VoteSupport,
ProposalState,
GovernorConfig,
Keypair
} from "@nebgov/sdk";
import { Networks, BASE_FEE } from "@stellar/stellar-sdk";
// Configuration
const config: GovernorConfig = {
governorAddress: "CABC123...", // Replace with actual governor contract
timelockAddress: "CDEF456...", // Replace with actual timelock contract
votesAddress: "CGHI789...", // Replace with actual votes contract
network: "testnet"
};
// Initialize clients
const governorClient = new GovernorClient(config);
const votesClient = new VotesClient(config);
const timelockClient = new TimelockClient(config);
// Key pairs (replace with actual key pairs)
const proposer = Keypair.fromSecret("S...");
const voter1 = Keypair.fromSecret("S...");
const voter2 = Keypair.fromSecret("S...");
async function completeGovernanceFlow() {
try {
console.log("=== NebGov Complete Governance Flow ===\n");
// 1. Check current voting power
console.log("1. Checking voting power...");
const proposerPower = await votesClient.getVotes(proposer.publicKey());
const voter1Power = await votesClient.getVotes(voter1.publicKey());
const voter2Power = await votesClient.getVotes(voter2.publicKey());
console.log(`Proposer voting power: ${proposerPower}`);
console.log(`Voter 1 voting power: ${voter1Power}`);
console.log(`Voter 2 voting power: ${voter2Power}\n`);
// 2. Create a proposal
console.log("2. Creating proposal...");
const proposalDescription = "Upgrade protocol parameter to improve efficiency";
const proposalId = await governorClient.propose(proposer, proposalDescription);
console.log(`Proposal created with ID: ${proposalId}\n`);
// 3. Check proposal state
console.log("3. Checking proposal state...");
let state = await governorClient.getProposalState(proposalId);
console.log(`Initial state: ${state}`);
// Wait for proposal to become active (if needed)
if (state === ProposalState.Pending) {
console.log("Proposal is pending, waiting for activation...");
// In a real scenario, you'd poll or wait for the voting period to start
}
// 4. Cast votes
console.log("\n4. Casting votes...");
await governorClient.castVote(voter1, proposalId, VoteSupport.For);
console.log("Voter 1 voted FOR");
await governorClient.castVote(voter2, proposalId, VoteSupport.For);
console.log("Voter 2 voted FOR");
// 5. Check vote results
console.log("\n5. Checking vote results...");
const votes = await governorClient.getProposalVotes(proposalId);
console.log(`Votes FOR: ${votes.votesFor}`);
console.log(`Votes AGAINST: ${votes.votesAgainst}`);
console.log(`Votes ABSTAIN: ${votes.votesAbstain}`);
// 6. Check final proposal state
console.log("\n6. Checking final proposal state...");
state = await governorClient.getProposalState(proposalId);
console.log(`Final state: ${state}`);
// 7. If proposal succeeded, schedule timelock operation
if (state === ProposalState.Succeeded) {
console.log("\n7. Scheduling timelock operation...");
// Example calldata (replace with actual encoded function call)
const targetContract = "TARGET123...";
const calldata = Buffer.from("example_calldata", "utf8");
const delay = 86400n; // 24 hours in seconds
const operationId = await timelockClient.schedule(
proposer,
targetContract,
calldata,
delay
);
console.log(`Timelock operation scheduled with ID: ${operationId}`);
// 8. Wait for delay and execute
console.log("\n8. Waiting for timelock delay...");
// In a real scenario, you'd wait for the delay period
// For this example, we'll check if it's ready
const isReady = await timelockClient.isReady(operationId);
if (isReady) {
console.log("Operation is ready, executing...");
await timelockClient.execute(proposer, operationId);
console.log("Timelock operation executed successfully!");
// 9. Check final proposal state (should be Executed)
const finalState = await governorClient.getProposalState(proposalId);
console.log(`\n9. Final proposal state: ${finalState}`);
} else {
console.log("Operation is not ready yet (delay period not elapsed)");
}
}
console.log("\n=== Governance Flow Complete ===");
} catch (error) {
console.error("Error in governance flow:", error);
throw error;
}
}
// Event subscription example
function setupEventMonitoring() {
console.log("\n=== Setting up Event Monitoring ===");
// Subscribe to new proposals
const proposalUnsub = subscribeToProposals(
config.governorAddress,
(event) => {
console.log(`📋 New proposal: ${JSON.stringify(event.value)}`);
},
{ network: config.network }
);
// Subscribe to votes on our proposal
const voteUnsub = subscribeToVotes(
config.governorAddress,
1n, // proposal ID
(event) => {
console.log(`🗳️ New vote: ${JSON.stringify(event.value)}`);
},
{ network: config.network }
);
// Return cleanup function
return () => {
proposalUnsub();
voteUnsub();
};
}
// Run the complete example
async function main() {
const cleanup = setupEventMonitoring();
try {
await completeGovernanceFlow();
} finally {
cleanup(); // Clean up event subscriptions
}
}
// Execute if this file is run directly
if (require.main === module) {
main().catch(console.error);
}
export { completeGovernanceFlow, setupEventMonitoring };=== NebGov Complete Governance Flow ===
1. Checking voting power...
Proposer voting power: 1000000
Voter 1 voting power: 500000
Voter 2 voting power: 750000
2. Creating proposal...
Proposal created with ID: 1
3. Checking proposal state...
Initial state: Active
4. Casting votes...
Voter 1 voted FOR
Voter 2 voted FOR
5. Checking vote results...
Votes FOR: 1250000
Votes AGAINST: 0
Votes ABSTAIN: 0
6. Checking final proposal state...
Final state: Succeeded
7. Scheduling timelock operation...
Timelock operation scheduled with ID: abc123def456...
8. Waiting for timelock delay...
Operation is ready, executing...
Timelock operation executed successfully!
9. Final proposal state: Executed
=== Governance Flow Complete ===
This example demonstrates the complete lifecycle of a governance proposal from creation through execution, including voting power delegation, proposal submission, voting, and timelock-protected execution.