Snapshot is a decentralized voting platform that enables gasless, off-chain governance for DAOs and token communities. Proposals are created and voted on using signed messages, eliminating transaction fees for voters. However, the results are not automatically enforced on-chain. To execute the will of the voters—such as transferring treasury funds or upgrading a protocol—you need a mechanism to bridge the off-chain vote to an on-chain transaction. This is where on-chain execution integrations come in, allowing trusted entities or smart contracts to execute proposals based on verified Snapshot results.
Setting Up a Snapshot Voting Integration
Setting Up a Snapshot Voting Integration
This guide explains how to integrate Snapshot's off-chain voting with on-chain execution, enabling gasless governance proposals that can trigger smart contract actions.
The core component for execution is the Snapshot executor. This is an address (either an Externally Owned Account or a smart contract) authorized to submit transactions that enact a proposal. When creating a proposal on Snapshot, you specify the executor and define the execution data. The execution data is typically one or more encoded function calls to your protocol's smart contracts. For example, a proposal might encode a call to a Treasury contract to transferERC20(token, recipient, amount). Voters see this intended action when they cast their votes.
After the voting period ends, the designated executor can trigger the execution. They call a function, often execute on a dedicated executor contract, providing the Snapshot proposal ID. The contract then verifies the proposal's status and the final vote tally against the rules defined in the Space settings (like quorum and voting threshold). If all checks pass, the contract decodes and executes the stored call data. Using a smart contract as the executor, rather than a multi-sig, allows for fully automated, permissionless execution once vote conditions are met.
To set this up, you first need to deploy an executor contract. A common and audited choice is the SafeSnap module developed by Gnosis Guild, which works with Gnosis Safe. The setup involves: 1) Deploying the Module contract and enabling it on your Safe, 2) Configuring your Snapshot Space to use the Module's address as the executor, and 3) Using tools like the Snapshot SDK or CLI to create proposals with the correct encoded execution payload. The payload can be generated using libraries like ethers.js or viem.
Security is paramount. The executor holds significant power. Best practices include: using a timelock on the executor contract to allow a delay before execution, implementing multi-sig guardians for the executor role even in automated setups, and thoroughly testing all proposal payloads on a testnet. Always verify that your Space's voting strategies and thresholds accurately reflect your community's governance model before linking them to on-chain assets. Resources like the Snapshot documentation and SafeSnap guide provide detailed implementation steps.
Prerequisites and Setup
A step-by-step guide to preparing your environment for building a Snapshot voting integration, covering essential tools, dependencies, and initial configuration.
Before writing any integration code, you need to establish a foundational development environment. This requires a modern Node.js runtime (version 18 or later) and a package manager like npm or yarn. You will also need a web3 provider to interact with the blockchain; popular choices include Alchemy, Infura, or a local node. For testing and signing messages, a browser wallet like MetaMask or a programmatic wallet using a private key is essential. Finally, ensure you have a code editor (VS Code is recommended) and basic familiarity with JavaScript/TypeScript and Ethereum concepts.
The core dependency for any Snapshot integration is the official @snapshot-labs/snapshot.js SDK. Install it in your project using npm install @snapshot-labs/snapshot.js. This library provides all necessary methods to query proposals, cast votes, and interact with Snapshot's GraphQL API. For enhanced type safety in TypeScript projects, you may also want the associated types package. Additionally, you'll need an Ethereum library for signing and sending transactions; ethers.js (v6) or viem are the standard choices. Install your chosen library alongside the Snapshot SDK.
Your first configuration step is to connect to Snapshot's Hub. The Hub is the central service that indexes all space data, proposals, and votes. You'll need to instantiate the Snapshot client, providing your web3 provider. For example, using ethers and a public RPC URL: const snapshot = new SnapshotClient(provider);. For reading data, you can often use a public provider, but writing actions (like voting) require a signer. Configure your signer object from your wallet's private key or browser extension to enable these transactions.
To interact with a specific community, you must identify its Space ID. This is a unique ENS name (e.g., ens.eth) or an immutable IPFS hash that points to a space's configuration. You can find a space's ID on its Snapshot interface URL. You will use this ID in almost every SDK call to fetch proposals, strategies, and votes for that specific space. It's crucial to verify this ID is correct, as it dictates the voting rules and strategies that will be applied to any vote you cast through your integration.
Finally, set up a basic script to test the connection. Use the client to fetch a list of proposals for a known space (like yam.eth) to verify your environment is working. A successful call returning proposal data confirms your SDK installation, provider configuration, and network connectivity are correct. This test establishes a baseline before you proceed to more complex operations like constructing vote messages, handling different voting types (single-choice, weighted, quadratic), and submitting votes on-chain or via EIP-712 signatures.
Step 1: Create and Configure a Snapshot Space
The first step to integrating on-chain voting is to create your project's dedicated governance hub on Snapshot, where proposals are posted and votes are cast.
A Snapshot Space is your project's dedicated governance page on the Snapshot protocol. It acts as the central hub where token holders can view proposals, cast votes, and see results. Creating a space is free and does not require on-chain deployment; it's a configuration process that defines your governance parameters and branding. You'll need a web3 wallet like MetaMask to sign the creation transaction, which occurs on the Gnosis Chain for minimal gas fees.
The core configuration happens in the Settings tab of your new space. Here, you define the voting strategies that determine voter eligibility. The most common is the erc20-balance-of strategy, which checks a user's balance of your governance token at a specific block number. You can also add strategies for erc721 (NFT) holders or custom logic. Crucially, you must specify the network (e.g., Ethereum Mainnet, Arbitrum) and the token contract address for each strategy.
Next, configure the voting system. Snapshot supports several types: single-choice (one option), basic (yes/no/abstain), weighted (allocate voting power across choices), and quadratic voting. For most DAOs, single-choice or basic is standard. You also set the vote duration (e.g., 3-7 days) and define proposal thresholds, such as a minimum token balance required to submit a proposal, which helps prevent spam.
A critical security step is setting up admins and moderators. The wallet that creates the space is the default admin with full control. You should add at least one other admin wallet as a backup. Moderators can edit proposal content but not space settings. For maximum security, consider using a multisig wallet (like Safe) as the admin, requiring multiple signatures for sensitive changes to the space configuration.
Finally, customize the Profile section with your project's logo, cover image, and description. This branding appears on the Snapshot explorer. Before going live, use the Test Proposals feature to create a dummy proposal and simulate the voting process end-to-end. Verify that vote weighting works correctly with your token and that the results display as expected. Once configured, your space is ready to host its first official governance proposal.
Step 2: Define the Voting Power Strategy
A voting strategy determines how a user's voting power is calculated for each proposal, based on their on-chain assets or activity.
The voting strategy is the core logic that maps a voter's on-chain state to a numerical voting power. Instead of one-person-one-vote, DAOs use strategies like token-weighted voting, where power is proportional to token holdings, or delegation-based systems like those used by Compound or Uniswap. In Snapshot, you define this logic in a strategy file, which is executed off-chain to compute a score for each address when a vote is cast. This allows for complex, custom governance models beyond simple token counts.
Strategies are written in JavaScript/TypeScript and must export a single strategy function. This function receives the parameters space, network, provider, addresses, and options, and returns a Promise resolving to an object mapping voter addresses to their calculated power (e.g., { '0x123...': '100.5' }). The provider gives you read-only access to blockchain data via the Ethers.js v5 library, enabling queries to token contracts, staking vaults, or NFT ownership. You can implement multi-chain strategies by checking the network parameter.
Here is a basic example of a token-weighted strategy for an ERC-20:
javascriptasync function strategy(space, network, provider, addresses, options) { const contract = new ethers.Contract( options.address, ['function balanceOf(address) view returns (uint256)'], provider ); const scores = {}; for (const address of addresses) { const balance = await contract.balanceOf(address); scores[address] = balance.toString(); } return scores; }
The options object is defined in your space's settings and can pass contract addresses, block numbers, or multiplier values to the strategy.
For advanced use cases, you can combine multiple strategies using Snapshot's validation feature or create a composite strategy. For instance, you might calculate power as: (ERC-20 balance * 1) + (veToken balance * 2) + (NFT holder ? 100 : 0). Strategies can also consider historical states by using a specific block number provided in the proposal creation. Always test your strategy thoroughly on a testnet using Snapshot's Playground before deploying it to a live space.
Common strategy patterns include: ERC-20/ERC-721 balances, delegated votes (checking a contract like COMP or UNI), staking/locking (e.g., veCRV), multi-chain balances (summing holdings across networks), and quadratic voting (where power is the square root of the balance). The strategy is the foundation of your governance model, so its design directly impacts security, fairness, and voter engagement.
Common Snapshot Voting Strategies
Comparison of core voting strategies available for Snapshot proposals, detailing their mechanics and use cases.
| Strategy | Mechanism | Use Case | Gas Cost | Complexity |
|---|---|---|---|---|
Basic Voting | 1 token = 1 vote | Simple governance for ERC-20/ERC-721 | None (off-chain) | Low |
Quadratic Voting | sqrt(token balance) = voting power | Reduce whale dominance, fund allocation | None (off-chain) | Medium |
Weighted Voting | Custom weights per address or token | Multi-token DAOs, delegated voting | None (off-chain) | Medium |
ERC-721 Voting | 1 NFT = 1 vote (with rarity traits) | NFT-based membership DAOs | None (off-chain) | Low |
ERC-1155 Voting | Voting power based on token ID balances | Multi-asset DAOs, game item governance | None (off-chain) | Medium |
Delegation | Votes delegated from token holders | Representative democracy models | On-chain delegation tx | High |
Whitelist Voting | Only pre-approved addresses can vote | Core team proposals, gated access | None (off-chain) | Low |
Step 3: The Proposal Creation and Voting Lifecycle
This guide details the technical process of creating proposals and managing votes using Snapshot, from initial setup to final execution.
A Snapshot voting integration allows your DAO to create and vote on proposals off-chain, eliminating gas fees for voters. The core components are the Snapshot Hub, which hosts the frontend and API, and the IPFS network, which stores proposal data immutably. Votes are signed messages (like EIP-712 signatures) stored on IPFS, not on a blockchain. The final tally is calculated by the Hub based on a predefined voting strategy, which determines voter eligibility (e.g., token balance at a specific block).
To begin, you must configure your space on Snapshot. This is your DAO's dedicated governance page. Key configuration includes setting the admin wallet, choosing a network (like Ethereum Mainnet or Arbitrum), and defining your voting strategies and plugins. The most common strategy is the erc20-balance-of strategy, which checks a user's token balance at a historical block number (a snapshot block). This prevents users from buying tokens to manipulate an ongoing vote.
Creating a proposal involves submitting a transaction to the Snapshot Hub. The proposal data—title, body, choices, and voting parameters—is posted to IPFS, returning a unique content hash (CID). Your frontend then calls the propose method on the Hub's API, passing the space name, the IPFS CID, and the signature from the proposal creator's wallet. A successful transaction creates the proposal on the Snapshot interface, where it enters a pending state before the voting period begins.
During the voting period, users connect their wallets to the Snapshot interface and cast their vote by signing a message. The signature payload includes the space name, proposal hash, choice, and timestamp. This signed vote is sent to the Snapshot Hub and pinned to IPFS. The Hub validates the vote by checking the signature and ensuring the voter meets the criteria of the voting strategy (e.g., owned tokens at the snapshot block). Votes can use different types like single choice, approval voting, or quadratic voting.
Once the voting period ends, anyone can trigger the finalization process. The Snapshot Hub fetches all vote messages from IPFS, validates them, and tallies the results according to the voting strategy. The final score for each choice is displayed on the proposal page. For on-chain execution, the results are typically used by a multisig or a governance module (like OpenZeppelin's Governor) to execute the approved transactions. The proposal's IPFS CID serves as the immutable record of both the proposal details and the voting history.
Step 4: Bridging to On-Chain Execution
This step details how to connect an off-chain Snapshot vote result to an on-chain smart contract for automated execution, completing the governance lifecycle.
After a proposal passes on Snapshot, the result exists as off-chain, signed data. To execute the intended action—like transferring treasury funds or upgrading a contract—this data must be securely relayed and validated on-chain. This is the role of the relayer, a trusted entity or a decentralized network of keepers. The relayer's job is to fetch the final vote data from Snapshot's GraphQL API, package it into a transaction, and submit it to your protocol's executor contract. This contract contains the logic to verify the vote and execute the approved action.
The core security mechanism is signature verification. Snapshot uses the EIP-712 standard for typed structured data signing. When a user votes, they sign a message containing the proposal ID, their chosen choice, and other metadata. The executor contract must replicate this signing process to verify each vote's authenticity. It reconstructs the EIP-712 hash and uses ecrecover to validate the signature against the voter's address. A common optimization is to verify a single, aggregated signature from the Snapshot hub, which confirms the entire vote outcome, rather than verifying thousands of individual signatures on-chain.
Your executor contract needs a specific interface to receive and process the proposal data. A basic function might look like executeProposal(bytes32 proposalId, uint256 choice, bytes calldata executionData). The contract must check that the proposal is in an executable state (e.g., passed the quorum and voting period) and that the provided choice matches the approved outcome. Only then should it call the internal function encoded in the executionData, such as treasury.transfer(address, amount). It's critical to implement replay protection, ensuring the same proposal result cannot be executed more than once.
For production systems, consider using a decentralized relayer network like the OpenZeppelin Defender Sentinels or Gelato Network. These services monitor your Snapshot space for passed proposals and automatically submit the execution transactions. They handle gas fees, transaction speed, and reliability. Alternatively, you can run your own relayer script using the Snapshot.js library. The script would periodically check for new proposals, fetch the results, and call the executeProposal function on your contract, paying for gas from a designated relayer wallet.
Always conduct thorough testing before mainnet deployment. Use a test Snapshot space (like https://testnet.snapshot.org) and deploy your executor contract to a testnet like Goerli or Sepolia. Simulate the full flow: create a proposal, vote on it, and trigger execution via your relayer. Key security audits should focus on the signature verification logic, access controls on the executor contract, and the safety of the arbitrary execution payload. This integration closes the loop, transforming community sentiment into concrete, on-chain action.
Executor Contract Example
A practical guide to building a smart contract that executes on-chain actions based on off-chain Snapshot votes.
An executor contract is a smart contract that performs on-chain transactions when triggered by a successful off-chain vote on Snapshot. This pattern separates the lightweight, gas-free voting process from the potentially expensive execution, allowing DAOs to coordinate decisions efficiently. The core logic involves verifying a valid Snapshot proposal hash and the winning vote outcome before executing predefined functions, such as transferring funds from a treasury or upgrading a protocol. This tutorial uses a Solidity example for Ethereum.
The contract must first validate the proposal. It does this by storing a mapping of approved proposal hashes. A trusted entity, like a DAO's multisig, must pre-approve a proposal by calling a function like registerProposal(bytes32 proposalHash). This step ensures the executor only acts on legitimate, reviewed proposals. The execution function then requires the caller to provide the proposal data and the final vote tally, which must match the stored hash and meet predefined conditions (e.g., quorum and majority).
Here is a simplified core of an executor contract. It uses a require statement to check if the provided proposalHash has been registered and if the yesVotes meet a threshold.
soliditycontract SnapshotExecutor { mapping(bytes32 => bool) public approvedProposals; address public treasury; uint256 public quorum = 1000; // Example quorum in vote power function registerProposal(bytes32 proposalHash) external { // Add access control (e.g., onlyOwner) in production approvedProposals[proposalHash] = true; } function executeTransfer( bytes32 proposalHash, uint256 yesVotes, address recipient, uint256 amount ) external { require(approvedProposals[proposalHash], "Proposal not approved"); require(yesVotes >= quorum, "Quorum not met"); // Execute the action (bool success, ) = treasury.call( abi.encodeWithSignature("transfer(address,uint256)", recipient, amount) ); require(success, "Transfer failed"); // Optionally, mark proposal as executed to prevent replay approvedProposals[proposalHash] = false; } }
For production use, critical security enhancements are necessary. The registerProposal function must have strict access control, typically allowing only a DAO's timelock or multisig to approve executions. To prevent replay attacks, the contract should invalidate a proposal hash after its first successful execution. Furthermore, consider using EIP-712 typed structured data hashing for proposal validation to ensure the off-chain data structure matches exactly what was voted on. Always audit the contract's interaction with the treasury or other target contracts.
Integrating this with your frontend involves listening for Snapshot proposal events. When a proposal passes, your app should fetch the final results and the EIP-712 hash. A UI button can then trigger the executeTransfer function, prompting the user's wallet (like MetaMask) to sign the transaction. The gas cost for execution is borne by the transaction submitter, which is often incentivized through a reward mechanism or handled by a designated bot. Tools like the Snapshot.js library can help fetch and verify proposal data off-chain.
This pattern is widely used by DAOs such as Uniswap and Aave to manage treasury operations. The key takeaway is the separation of concerns: Snapshot handles complex, expressive voting with no gas costs, while a secure, minimal executor contract handles the irreversible on-chain action. Always test thoroughly on a testnet, consider adding a timelock delay for high-value actions, and ensure your community understands the execution process.
Essential Resources and Tools
Key tools, protocols, and reference implementations for integrating Snapshot voting into a Web3 application. These resources focus on proposal creation, vote signing, score calculation, and data ingestion using Snapshot's off-chain governance model.
Frequently Asked Questions
Common technical questions and solutions for developers implementing Snapshot voting on their platform.
A Snapshot strategy is a smart contract that defines how voting power is calculated for a proposal. It queries on-chain data to determine user balances at a specific block number (the snapshot block). You must choose a strategy that matches your token's logic.
Common strategies include:
erc20-balance-of: For standard ERC-20 tokens.erc20-with-balance: For tokens with a minimum holding requirement.erc721: For NFT-based voting (1 NFT = 1 vote).delegation: For systems with token delegation (e.g., Compound, Uniswap).
You can also create a custom strategy by forking the Snapshot strategy template. The strategy is specified in the space settings or directly in the proposal creation payload.
Security Considerations and Best Practices
Integrating Snapshot for on-chain governance requires careful attention to security to protect your community's votes and treasury. This guide covers essential practices for developers and DAO administrators.
The primary security model of Snapshot is based on off-chain signing. Users sign messages with their wallets to cast votes, which are then recorded on IPFS and indexed by Snapshot's infrastructure. This design prevents gas costs for voters but shifts the trust to the integrity of the proposal data and the signature validation process. Always verify that your integration uses the official Snapshot.js library or the GraphQL API to fetch proposals and validate signatures, as manual implementation errors can lead to spoofed votes.
Proposal creation is a critical attack vector. Malicious proposals can use misleading titles, execute arbitrary transactions via the executor field, or target vulnerable contracts. Implement a multisig or timelock process for proposal submission in your space settings. Use the validation strategy to whitelist specific proposal creators or require a minimum token balance. For high-stakes votes, consider using the veto plugin to allow a trusted committee to cancel malicious proposals before the voting period ends.
Vote manipulation through sybil attacks or token flash loans is a common concern. Mitigate this by carefully choosing your voting strategy. The erc20-balance-of strategy is vulnerable to flash loans; use a time-weighted strategy like erc20-votes (from OpenZeppelin) or a block-number based snapshot to record balances at a specific block before the vote. For more complex governance, composite strategies can combine multiple token contracts or delegate voting power.
Ensure the security of the data source for your voting strategy. If your strategy reads from a custom smart contract, that contract must be audited and implement a function that returns a user's voting power at a given block. A common vulnerability is a contract that allows the voting power to be manipulated after the snapshot block. The function should be view-only and its logic should be resistant to reentrancy and manipulation.
When displaying proposals and results in your dApp, guard against frontend phishing. Always fetch proposal data directly from the verified Snapshot Hub GraphQL endpoint (https://hub.snapshot.org/graphql) or via the official client. Do not rely on unverified third-party APIs that could serve tampered data. Clearly display the proposal's IPFS hash and link to the Snapshot page, allowing users to verify the information on a trusted interface.
Finally, maintain a crisis playbook. This includes knowing how to pause your space, disable a specific strategy, or use the flagged status to invalidate a proposal in case of an exploit. Keep your community informed through multiple channels if a security incident occurs. Regular dry runs of your governance process and staying updated with Snapshot's security announcements are essential for long-term safety.
Conclusion and Next Steps
Your Snapshot integration is now operational. This section summarizes the key components and provides resources for extending your voting system.
You have successfully built a foundational Snapshot integration. The core components are in place: a Space configuration defining your voting rules, a Strategy contract that queries on-chain data to determine voter eligibility and weight, and a frontend interface using the @snapshot-labs/snapshot.js SDK to create and display proposals. Remember that the integrity of your vote depends on the Strategy; it must accurately and securely reflect the governance token holdings or other metrics you've chosen for your DAO.
To enhance your setup, consider these advanced features. Implement multichain voting by creating a strategy that aggregates token balances across multiple networks like Ethereum, Arbitrum, and Polygon. Add vote delegation by integrating with protocols like SafeSnap or building custom logic to allow token holders to delegate their voting power. For real-time updates, subscribe to the Snapshot Hub's GraphQL endpoint for events like new proposals or cast votes. Always test strategy updates thoroughly on a testnet Space before deploying to production.
The next step is to integrate this voting mechanism into your broader governance workflow. Connect the Snapshot proposal outcomes to on-chain execution via a timelock contract or a multisig using tools like Zodiac's Reality Module. Explore Snapshot's plugins for features like erc20-balance-of, whitelist, or contract-call for more complex voting logic. For ongoing development, refer to the official Snapshot documentation, the Snapshot.js GitHub repository, and engage with the community in the Snapshot Discord for support and to stay updated on new features.