# Identify integrator activity Source: https://docs.snapshot.box/developer-guides/identify-integrator-activity To help integrators track and identify activity originating from their applications, Snapshot supports an optional `app` parameter. This parameter can be included when users cast votes or create proposals, allowing you to tag these actions with your application's identifier. ### **Using `app` with Snapshot.js** If you're integrating Snapshot's voting and proposal functionalities directly into your platform using [Snapshot.js](https://github.com/snapshot-labs/snapshot.js), you can include the `app` parameter in your function calls: ```javascript theme={null} import { Web3Provider } from '@ethersproject/providers'; const web3 = new Web3Provider(window.ethereum); const [account] = await web3.listAccounts(); const receipt = await client.vote(web3, account, { space: 'yam.eth', proposal: '0x21ea31e896ec5b5a49a3653e51e787ee834aaf953263144ab936ed756f36609f', type: 'single-choice', choice: 1, reason: 'Choice 1 makes a lot of sense', app: 'yourappname' }); ``` ### **Adding `app` to Snapshot URLs** For platforms that redirect users to Snapshot, you can append the `app` parameter to the URL. This tags the action with your application's identifier when the user interacts on Snapshot. **URL format:** ``` https://snapshot.box/#/{network}:{space}/proposal/{proposalId}?app={yourappname} ``` **Example:** ``` https://snapshot.box/#/s:ens.eth/proposal/0xfa54ff2b55f0495c96ec2d8645241bcff48ca6afe1f4925fb51f29c4667252df?app=boardroom ``` **Parameter requirements** * **Format:** Lowercase alphanumeric string * **Maximum length:** 24 characters * **Purpose:** The `app` parameter will be stored with the vote or proposal data on Snapshot. This enables you to identify and analyze activities originating from your application. By using the `app` parameter, you can seamlessly integrate Snapshot into your platform while maintaining clear visibility over user interactions related to your application. # Add a network Source: https://docs.snapshot.box/developer-guides/networks To add a new network to Snapshot, you need to subscribe to the Network plan. The Network plan is tailored for projects seeking to integrate their EVM compatible chain network in Snapshot. This plan facilitates governance across your networks by supporting both a mainnet and a testnet network. ### **How to get started** Refer to [https://snapshot.box/#/network](https://snapshot.box/#/network) # Create a validation strategy Source: https://docs.snapshot.box/developer-guides/validation-strategy Create a validation strategy and use it in your own space To add your own voting validation strategy on Snapshot you need to fork the **score-api** repository and create a pull request. [https://github.com/snapshot-labs/score-api](https://github.com/snapshot-labs/score-api) ### 1. Navigate to the **`src\validations.`** ```bash theme={null} └── src └── validations └── basic ``` ### 2. Create a copy of the **`basic`** strategy folder and rename it to the name of your voting validation. ### 3. Customize the logic of your voting validation. If you are not sure about the `Validation` class, have a look at its definition: [https://github.com/snapshot-labs/score-api/blob/master/src/strategies/validations/validation.ts](https://github.com/snapshot-labs/score-api/blob/master/src/strategies/validations/validation.ts) The validation name has to be included in the[ `index.ts`](https://github.com/snapshot-labs/score-api/blob/master/src/strategies/validations/index.ts) in the `validationClasses` variable. ### 4. Test your validation To make sure your validation passes all tests, run: ```javascript theme={null} npm run test --validation= // replace ``` ### 4. Make sure you pass the checklist Have a look here on the requirements for adding a new validation strategy and make sure you fulfill the points in the checklist: [https://github.com/snapshot-labs/score-api#checklist-for-adding-a-new-strategy](https://github.com/snapshot-labs/score-api#checklist-for-adding-a-new-strategy) ### 5. Create a pull request The team will then review your PR and after it's approved and merged it will be available to choose in your space settings. # Create a voting strategy Source: https://docs.snapshot.box/developer-guides/voting-strategy Learn how to create a new custom voting strategy. If you can't find a strategy that fits your the needs of your space you can create a new custom one. Follow the steps below to learn how to do that: ### **1. Fork the score-api repository** Create a fork of the score-api repository: [https://github.com/snapshot-labs/score-api](https://github.com/snapshot-labs/score-api) ### **2. Duplicate the `erc20-balance-of` strategy folder** Navigate to `src/strategies/strategies` directory, duplicate the `erc20-balance-of` directory and rename it to the chosen name for your new strategy. ```bash theme={null} └── src └── strategies └── strategies └── erc20-balance-of ``` ### **3. Write the logic for your strategy** There are several files you need to edit: #### a. `index.ts` This file defines the logic for calculation of the voting power. As an example, the `erc20-balance-of` is taking as parameters `space`, `network`, `provider`, `addresses`, `options` and `snapshot` in order to be able to retrieve the balances of the token specified in the `options` parameter for the provided addresses: ```typescript theme={null} import { BigNumberish } from '@ethersproject/bignumber'; import { formatUnits } from '@ethersproject/units'; import { Multicaller } from '../../utils'; export const author = 'bonustrack'; export const version = '0.1.1'; const abi = [ 'function balanceOf(address account) external view returns (uint256)' ]; export async function strategy( space, network, provider, addresses, options, snapshot ): Promise> { const blockTag = typeof snapshot === 'number' ? snapshot : 'latest'; const multi = new Multicaller(network, provider, abi, { blockTag }); addresses.forEach((address) => multi.call(address, options.address, 'balanceOf', [address]) ); const result: Record = await multi.execute(); return Object.fromEntries( Object.entries(result).map(([address, balance]) => [ address, parseFloat(formatUnits(balance, options.decimals)) ]) ); } ``` #### b. `schema.json` Describe the structure of your strategy by editing the `properties`, `required` and `additionalProperties` key-value pairs according to the logic from `index.ts` file: ```json theme={null} { "$schema": "http://json-schema.org/draft-07/schema#", "$ref": "#/definitions/Strategy", "definitions": { "Strategy": { "title": "Strategy", "type": "object", "properties": { "symbol": { "type": "string", "title": "Symbol", "examples": ["e.g. UNI"], "maxLength": 16 }, "address": { "type": "string", "title": "Contract address", "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"], "pattern": "^0x[a-fA-F0-9]{40}$", "minLength": 42, "maxLength": 42 }, "decimals": { "type": "number", "title": "Decimals", "examples": ["e.g. 18"] } }, "required": ["address", "decimals"], "additionalProperties": false } } } ``` #### d. `./score-api/src/strategies/strategies/index.ts` Import and declare you new strategy in the [`index.ts`](https://github.com/snapshot-labs/score-api/blob/master/src/strategies/strategies/index.ts) file: ```typescript theme={null} import * as yourStrategyName from './your-strategy-name'; ``` #### d. `examples.json` Provide an example for the custom strategy setup which will be displayed on [https://snapshot.box](https://snapshot.box) on the strategy's details page. Make sure to **include all the parameters you defined** above and a list of addresses to test against: ```json theme={null} [ { "name": "Example query", "strategy": { "name": "erc20-balance-of", "params": { "address": "0x6b175474e89094c44da98b954eedeac495271d0f", "symbol": "DAI", "decimals": 18 } }, "network": "1", "addresses": [ "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11", "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad", "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C", "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e", "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4", "0x1f254336E5c46639A851b9CfC165697150a6c327", "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030", "0x4AcBcA6BE2f8D2540bBF4CA77E45dA0A4a095Fa2", "0x4F3D348a6D09837Ae7961B1E0cEe2cc118cec777", "0x6D7f23A509E212Ba7773EC1b2505d1A134f54fbe", "0x07a1f6fc89223c5ebD4e4ddaE89Ac97629856A0f", "0x8d5F05270da470e015b67Ab5042BDbE2D2FEFB48", "0x8d07D225a769b7Af3A923481E1FdF49180e6A265", "0x8f60501dE5b9b01F9EAf1214dbE1924aA97F7fd0", "0x9B8e8dD9151260c21CB6D7cc59067cd8DF306D58", "0x17ea92D6FfbAA1c7F6B117c1E9D0c88ABdc8b84C", "0x38C0039247A31F3939baE65e953612125cB88268" ], "snapshot": 11437846 } ] ``` #### e. `README.md` Write the description of how the strategy works and provide an example of the setup. It will be displayed on the strategy's details page. ``` This is the most common strategy, it returns the balances of the voters for a specific ERC20 token. Here is an example of parameters: { "address": "0x6b175474e89094c44da98b954eedeac495271d0f", "symbol": "DAI", "decimals": 18 } ``` ### **4. Test the strategy locally** Once you saved all the files run the below command with the name of your new strategy: ```javascript theme={null} npm run test:strategy --strategy= // replace ``` It will trigger the tests which you can find in [this file](https://github.com/snapshot-labs/score-api/blob/master/test/strategies/unit/strategy.test.ts). If you get any errors read them carefully as they should point directly to the problem. ### **5. Review the checklist** Ensure you meet the requirements for adding a new strategy by reviewing the checklist for adding a new strategy which can be found on [this documentation](https://github.com/snapshot-labs/score-api/blob/master/STRATEGIES.md#checklist-for-new-strategies) ### **6. Create a pull request** Create a Pull Request with the above changes on the original [score-api](https://github.com/snapshot-labs/score-api/) repo. The review can take the team up to 72 hours, so please be patient 🙏 After the PR has been merged, you will need to wait for the release of a new version of [Snapshot](https://snapshot.box) which can take a couple of days. Once it's deployed you can move on to the next step. ### 7. Try it out! Head to the strategy's details page and click **Playground** button. Follow the instructions from [#testing-a-voting-strategy](/user-guides/voting-strategies#testing-a-voting-strategy) to see if it works as you intended. Congrats, you've just added a new custom voting strategy. # Build with Snapshot Source: https://docs.snapshot.box/developers/index Integrate governance into your apps using Snapshot's APIs, SDKs, and protocol. Snapshot provides a complete set of tools for developers to integrate decentralized governance into their applications. Whether you're querying voting data, building custom strategies, or deploying fully onchain governance, you'll find everything you need here. JavaScript client library for interacting with Snapshot: create proposals, cast votes, and manage spaces. Query spaces, proposals, votes, and more with Snapshot's public GraphQL API. Fully onchain governance protocol deployed on Ethereum and Starknet. Create custom voting and validation strategies to tailor governance to your needs. Subscribe to proposal and voting events in real-time to keep your app in sync. ### Quick links * [GitHub](https://github.com/snapshot-labs) * [Discord](https://discord.snapshot.org) * [Snapshot.js on npm](https://www.npmjs.com/package/@snapshot-labs/snapshot.js) # FAQ Source: https://docs.snapshot.box/faq Frequently asked questions about Snapshot. ## General A snapshot is a record of the blockchain's state at a specific block number. It shows exactly who owned which tokens at that precise moment. By doing this, voting rights are based on actual holdings fixed at the time of a proposal. Without a snapshot, a user could vote, transfer their tokens to another account they control, and vote again, effectively double spending their voting power. This process of locking the data at a single point in time to prevent such abuse is what gives Snapshot its name. Yes, Snapshot itself is free to use and does not charge any fees for setting up a space, creating proposals, casting votes, or executing decisions. However, to create a space, you need an ENS domain, which you must purchase from [ens.domains](https://ens.domains). While Snapshot's core features are offchain and do not require gas fees, deploying a Snapshot X space may involve paying gas fees. If you decide to sponsor gas costs for users on Snapshot X, you will also need to cover those fees. Despite these extra costs arising from external factors, Snapshot itself remains free. Yes, Snapshot is fully open source under the MIT license, meaning anyone can see how it's built and contribute to its development. All code repositories are available on [GitHub](https://github.com/snapshot-labs). Snapshot facilitates gasless voting through the use of offchain signed messages. This approach allows users to participate in governance without the need for onchain transactions, avoiding gas fees while ensuring verifiability. No. Snapshot uses the universal web3 login: you need to have a wallet account like MetaMask or Coinbase to connect to Snapshot. Snapshot maintains several mirror sites to ensure you can always access the platform: * **Primary domain:** [snapshot.box](https://snapshot.box) * **Alternative mirrors:** * [snapshot.org](https://snapshot.org) (Netlify hosting) * [snapshot.eth.limo](https://snapshot.eth.limo) (Fleek with IPFS) All mirrors display identical content. Your votes, proposals, and data remain synchronized across every domain. You can also host Snapshot yourself, the full source code is available at [github.com/snapshot-labs/sx-monorepo](https://github.com/snapshot-labs/sx-monorepo). Yes. The message you sign in your wallet to cast a vote doesn't affect your holdings or web3 identity. Some spaces may be flagged with a warning badge if we suspect them to be fake, but there is no risk to your funds associated with signing a Snapshot vote. Read more about [badges and warnings](/user-guides/spaces/badges-and-warnings). Yes. Limiting the number of followed spaces to 25 is one of our anti-scam and anti-spam measures. Wait for around 10 minutes and try again. If the error persists, open the console panel (right click > `Inspect` > `Console` tab), take a screenshot, and contact our support on [Help center](/faq/support-and-feedback) with the error details and what action you were trying to perform. The reason behind this error is that the time configured on your device isn't accurate. [Refer here to troubleshoot](https://github.com/snapshot-labs/snapshot/discussions/769#discussioncomment-1400619). ## Voting You need to connect to Snapshot with your wallet and fulfil the requirements defined by the voting strategies used by a specific space. For example, you might be required to hold a specific amount of the organization's token. You can see the voting strategies directly on a proposal's page in the **Information** section. If your voting power is `0`, it's most likely because you did not hold the required tokens at the time of proposal creation. Tokens acquired after the proposal was created are not taken into account. If you are eligible to vote, select your choice(s) and confirm your vote. You will be asked to sign a message which doesn't create any gas fees or affect your holdings. That's it! No. Your eligibility to vote depends on the voting strategies defined by the space and usually requires holding the organization's token. Some spaces allow anyone to vote, however this is a rare case. Read more about [voting strategies](/user-guides/voting-strategies). Yes, you can change your vote as long as the voting period is still active. Simply cast a new vote on the same proposal, and it will overwrite your previous vote. Yes. You can find more details in [Using Safe multi-sig](/user-guides/using-safe-multi-sig). Make sure that your wallet is on the same network as the Space. Go to the Space page > Settings > Strategies to check what the Space's network is. Yes, apart from proposals using shielded voting. You can add a short explanation when casting a vote. You can remove your delegations by going to [snapshot.box/#/delegate](https://snapshot.box/#/delegate) and clicking the remove button for the delegations you wish to remove. If you want to override your delegate's vote on a proposal using the delegation strategy, you can simply cast your own vote and it will override the delegate's vote. If the delegation happened onchain, head to the delegation portal of the project you're looking for and redelegate there. Go to the proposal page and click the download icon to get a CSV file. You can open it directly in Excel or import it into Google Sheets. This issue is related to either space settings or the failure of the external infrastructure that Snapshot relies on. Wait 15 minutes and try again. If the error persists, please contact our support on [Help center](/faq/support-and-feedback) with the URL of the proposal and the error message. This is related to either space settings or a failure of external infrastructure. Wait 15 minutes and try again. If the error persists, contact our support on [Help center](/faq/support-and-feedback) with the proposal URL and the error message. No. Snapshot calculates the voting power at the time of proposal creation. If the token was not in your wallet before the proposal was created, it will not be counted. ## Proposals It depends on the space settings. Spaces can set up a validation strategy which defines who is eligible to create a proposal, for example you need to hold at least 1 ETH. Moreover, spaces can specify a list of users who can create proposals by listing their wallet addresses. Read more about [proposal validation](/user-guides/validation-strategies). Each proposal's description is limited to **10,000 characters**. An address can have up to **10 active proposals** across spaces at any time. Verified spaces can create up to **10 proposals daily** and **50 monthly**, unverified spaces are limited to **3 daily** and **15 monthly**, while flagged spaces can only create **1 daily** and **5 monthly**. With the [Snapshot Pro](/user-guides/spaces/pro) plan, the character limit increases to **50,000 characters**, and proposal creation limits expand to **100 proposals per day** and **500 per month**. Yes, using Snapshot's API or SDK, you can automate proposal submissions. Check the [developer documentation](/tools/snapshot-js) for specific methods and requirements. If a proposal does not reach the required quorum, it is considered as not passed. However, the outcome depends on the governance rules of the specific project. Some projects might still consider the proposal for further discussion, while others might require resubmission with modifications. Due to an increased amount of scam attacks on Snapshot, shortened URLs are not allowed in the proposal description. Make sure you provide a full URL. Yes. Have a look at the [Quadratic Voting](/proposals/voting-types) system which reduces the impact of large token holders. Due to multiple spam attacks on Snapshot, all spaces are required to set up a Proposal Validation. Head to [validation strategies](/user-guides/validation-strategies#how-to-use-validation-strategies) to learn how. You can submit your request by following the process described in the [token verification](/v1-interface/token-verification) section. ## Voting strategies A voting strategy is a set of conditions used to calculate a user's voting power. Strategies enable Snapshot to calculate the final result of voting on a given proposal. Read more in our [voting strategies](/user-guides/voting-strategies) documentation. You can use the playground on Snapshot available from any strategy's detail page by clicking the **Playground** button. You need to set up a Basic voting validation which allows you to select a specific strategy and define the minimum threshold required. Learn more in [validation strategies](/user-guides/validation-strategies). There are a few common reasons: * **No required assets.** You don't hold the tokens or meet the criteria required by the space's voting strategies. * **Delegation required.** For tokens using ERC-20 Votes (EIP-5805), you must delegate your tokens — to yourself or another address — before they count as voting power. * **Snapshotted at proposal start.** Voting power is taken at the time the proposal was created. Tokens acquired after that point don't count for that proposal. You can also check [this discussion](https://github.com/snapshot-labs/snapshot/discussions/767) for further reasons. If the space is using the delegation strategy and the address which delegated its voting power to you casts a vote, your total voting power is reduced by the delegation you received. Example (using the `delegation` strategy): * B has voting power of 20 of its own * A has voting power of 10 and delegates it to B * B now has voting power of 30 * If A does not vote, B's voting power is 30 * If A does vote, B's voting power is 20 (30 reduced by 10) You can use the [ticket](https://snapshot.box/#/strategy/ticket) strategy. It's a two step process: **1. Voting validation:** Set up a `Basic` voting validation in the space settings. Define the `strategies` for the tokens and the `minScore` required for voters to be eligible. **2. Voting strategy:** Use the [ticket](https://snapshot.box/#/strategy/ticket) strategy to give voting power equal to `1` to any user who passed the voting validation. You can use sub-spaces on Snapshot. This allows you to link different spaces and set different settings for each of them. Learn more in [sub-spaces](/user-guides/spaces/sub-spaces). Yes. You can create an issue on [snapshot-labs/score-api](https://github.com/snapshot-labs/score-api/issues) and then post it in the [#developers](https://discord.com/channels/707079246388133940/747050056422785055) channel on Discord. ## Space settings Yes. Navigate to [app.safe.global](https://app.safe.global) and select Snapshot in the `Apps` tab. Your multisig address will connect to Snapshot and allow you to create spaces, proposals and vote. If you are still a controller of the space you can apply to delete your space or migrate it to another one with a different ENS. Learn more in [migrate or delete](/user-guides/spaces/migrate-or-delete). Yes, you can do it in the space settings. Do not confuse it with changing the `ID` or the ENS domain: for that, you need to migrate the space. Learn more in [migrate or delete](/user-guides/spaces/migrate-or-delete). There is a list of requirements you have to meet. Learn more in [get verified](/user-guides/spaces/get-verified). The process can take up to 72 hours. Verification status is valid as long as the space meets all requirements. Make sure you meet all requirements defined in [get verified](/user-guides/spaces/get-verified#how-to-get-your-space-verified) and contact our support on [Help center](/faq/support-and-feedback) to regain the space verification. You cannot invalidate existing votes. However you can delete the proposal (if you are an admin of the space or the proposal's creator), change the space settings, and recreate the proposal from scratch. Yes, you can customize the appearance by setting a custom skin, which allows you to change the colors of your space. This feature is available for spaces using custom domains. If your space is using only a ticket Voting Strategy, you are required to set a Voting Validation to minimize the risk of spam votes. Learn how to set it up in [validation strategies](/user-guides/validation-strategies#voting-validation-in-space-settings). Deleting a space does not release its attached ENS name, so you cannot create a new space with the same ENS name. If you wish to retain a similar domain, you could create a new space under a subdomain of your previous ENS name. ## Quorum A proposal passes if it has more "For" votes than "Against" votes, and the total of "For" plus "Abstain" plus "Against" votes reaches or exceeds the quorum. All votes, whether in favor, against, or abstaining, count toward meeting the required participation level. Once that level is met, the proposal's success depends on having a clear majority of "For" votes over "Against". A proposal passes if it meets two conditions: it must have more "For" votes than "Against" votes, and the total of "For" plus "Abstain" votes must reach or exceed a set quorum. In other words, there needs to be enough participation and a clear majority in favor for the proposal to succeed. ## Networks Snapshot supports EVM-compatible networks. To add a new network, you need to subscribe to the network plan. Once subscribed, Snapshot will take care of adding your network to the platform. For more details, visit [snapshot.box/#/network](https://snapshot.box/#/network). For non-EVM networks, please contact the Snapshot team directly. Snapshot supports many networks. Some have full support ("premium") while others have limited support ("non-premium"). Premium networks include: Ethereum, Anime, APECHAIN, Avalanche, Base, Blast, BNB Chain, Cyber, Fraxtal, Gnosis Chain, Horizen EON, Immutable zkEVM, Ink, Kava, Linea, Manta Network, Mantle Network, Merlin Chain, Metis, opBNB, Optimism, peaq, Sei, Shibarium, SNAXchain, Sonic, Story, Vana, and zk.Link. If you want to upgrade a network from non-premium to premium, follow the instructions at [snapshot.box/#/network](https://snapshot.box/#/network). ## Wallet compatibility Yes, Snapshot supports EIP-1271, enabling smart contract wallets to participate directly in governance. EIP-1271 allows smart contracts to implement their own method for signature verification. If you manage a smart contract account, you can use it to create proposals or vote on Snapshot. Make sure your smart contract account is on the same base network as the Snapshot space you want to interact with. Yes, Snapshot supports EIP-6492, a standard for undeployed smart contract wallets (counterfactual wallets). EIP-6492 provides a standardized method to verify signatures from smart contract wallets before they are deployed onchain, enabling users to participate in governance without initially deploying their wallet contracts. A release of the MetaMask Chrome extension (version 12.15.1) introduced a bug affecting message signing. The MetaMask team fixed the problem in version 12.15.2. Please ensure your extension is updated: 1. Open your MetaMask Chrome extension 2. Click on the top-right three-dot menu > Settings > **About** 3. If the version is **12.15.2 or higher**, you're good to go 4. To update manually: go to `chrome://extensions`, find MetaMask, enable **Developer mode**, and click **Update** ## Migrations UMA has announced that they are ending support for oSnap on **December 15, 2025**. After this date, oSnap will no longer execute transactions from your DAO's Safe treasury. If your DAO relies on oSnap, you must take action before this deadline. You can either migrate to [Snapshot X](/snapshot-x/user-guides/create-a-space) (recommended for fully onchain governance) or to [SafeSnap](/v1-interface/plugins/safesnap-reality) (for optimistic execution similar to oSnap). The "delegation" strategy is being replaced by "with-delegation" (effective August 15, 2025). The key difference is that "delegation" works like liquid democracy where a delegator can reclaim power by voting, while "with-delegation" combines both delegated and own voting power and requires explicit undelegation. To migrate, replace your standalone voting strategies and "delegation" strategy with a single "with-delegation" strategy. The "multichain" strategy is being deprecated (effective August 15, 2025). Since Snapshot now has native multichain support, you should add each strategy individually with the appropriate network, then delete the old "multichain" strategy. You must migrate before August 2025 or you won't be able to create new proposals. ## Support and feedback **Basic support** (free, available to everyone): * Live chat accessible from any page in the Snapshot app * Average reply time under 5 minutes * Email follow-ups and community Discord **Premium support** (for Premium networks or Snapshot Pro spaces): * All basic support features * Dedicated Telegram group with direct access to the technical team * Prioritized feature development If you've discovered a vulnerability or security issue, please send an email to **[security@snapshot.org](mailto:security@snapshot.org)** with as much information as possible. Share your suggestions and upvote existing feature requests at [snapshot.canny.io/feature-requests](https://snapshot.canny.io/feature-requests). Please fill out the [partnership form](https://tally.so/r/w2axeK) and we'll get in touch with you soon. Before reporting, browse through this FAQ and our documentation. If you still have an issue, contact our support on [Help center](/faq/support-and-feedback) with the following details: * Detailed description of the issue (what action were you trying to perform, what error did you get) * Screenshots of the error * Applicable URLs (proposal or space URL) ## Developers You can find the networks' IDs here: [snapshot-relayer check.ts](https://github.com/snapshot-labs/snapshot-relayer/blob/master/src/check.ts#L9) No, it's not needed. Yes. You can use [testnet.snapshot.box](https://testnet.snapshot.box) with an ENS domain on Sepolia Testnet. Snapshot uses IPFS to pin the receipts of the votes. Read more in the [IPFS case study](https://blog.ipfs.tech/2022-08-25-snapshot-ipfs-case-study/). Yes, however we encourage everyone to apply for a key as the limits for keyless access are much lower. Learn more in [API keys](/tools/api/api-keys). Yes. Without an API Key: 100 requests per minute. With an API Key: 2 million requests per month. Learn how to apply in [API keys](/tools/api/api-keys). Have a look at our [webhooks documentation](/tools/webhooks). Browse existing staking strategies at [snapshot.box](https://snapshot.box/#/?type=strategies\&q=stake). If none work for you, you can use the `erc20-balance-of` strategy if your contract has a `balanceOf` method, or the `contract-call` strategy if the balance method has a different name. Yes. If it's not supported yet you can create a custom voting strategy. Learn more in [voting strategy development](/developer-guides/voting-strategy). It usually takes around 72 hours. Once the PR is merged, you will also have to wait for a new release which can take another couple of days. # Getting started Source: https://docs.snapshot.box/index ## What is Snapshot? Snapshot is a voting platform that allows DAOs, DeFi protocols, and NFT communities to vote easily and without gas fees. It supports flexible voting strategies, multiple voting systems, and fully customizable governance, all offchain and open source. ## Browse by topic Create and manage your organization's governance space. Create proposals and cast votes on decisions. Configure how voting power is calculated. Fully onchain governance on EVM and Starknet. Delegation, Safe voting, notifications, and more. Answers to the most common questions. # Edit a proposal Source: https://docs.snapshot.box/proposals/edit-a-proposal Learn how to edit a proposal before the voting period starts. If you're the author of a proposal on Snapshot, you can edit it while it's still in the **pending phase**, before the voting period starts. Look for the three-dot menu next to the proposal title to make your edits. Once the voting period has started, the proposal can no longer be edited. # Voting types Source: https://docs.snapshot.box/proposals/voting-types Learn more about the different voting schemes on Snapshot. Snapshot supports a number of different voting types and we plan to support many more in the future. If you would like to request a new voting type, please open a [feature request](https://snapshot.canny.io/feature-requests). ## What is a voting type? Voting type defines how users can cast their votes and how the final result is calculated. Do not mistake it with the voting strategy though - a [voting strategy](/user-guides/voting-strategies) is used to calculate the **individual voting power** of a user while the voting type calculates the **outcome of the proposal**. Voting type is defined in the space settings or at the level of an individual proposal (unless it has been already defined in the space settings) and can allow the users to: * Choose only **one option**, [#single-choice-voting](#single-choice-voting) * Spread their votes over **multiple options**, [#weighted-voting](#weighted-voting) * **Approve** a certain number of options, [#approval-voting](#approval-voting) * Spread their votes over multiple options using the [**quadratic voting**](https://en.wikipedia.org/wiki/Quadratic_voting) **formula**, [#quadratic-voting](#quadratic-voting) * Rank the different choices in their **order of preference**, [#ranked-choice-voting-instant-runoff-voting](#ranked-choice-voting-instant-runoff-voting) * **Abstain** from voting while still participating in quorum, [#basic-voting](#basic-voting) Let's have a look at each voting type in the next section. ## Voting types ### Single choice voting Each user can select only one option. The results will reflect these votes as percentages of the total voting power of all voting participants cast on the specific choice. Ideal for choosing one option from many. **Pros**: Most simple and common voting type which is easy to set up and use.\ **Cons**: Voters can pick only one option. [**Try it yourself**](https://snapshot.box/#/s:pistachiodao.eth/proposal/0x02c3fcd64e86157d07c88e5a715ac08f57655917f8bfd5be30a99092136511ec) ### Weighted voting Each user can spread their voting power across any number of choices, from one to all. Their voting power will be divided between their chosen options according to how much weight they attribute to each option by increasing or decreasing the voting power fraction. **Pros**: Allows voters to express support for more than one option and specify how much they support each of them.\ **Cons**: Requires more time and adds complexity to the voting process. This voting method was first introduced by Float Protocol with [Scattershot](https://github.com/FloatProtocol/scattershot) (a fork of Snapshot). [**Try it yourself**](https://snapshot.box/#/s:pistachiodao.eth/proposal/0xf93f1ac80e22cc930b1eef1d20bd34671ccc33b88b04695479c9de364451d77f) ### Approval voting Each user can select (*approve*) any number of choices, each selected choice will receive equal voting power, i.e. if user selects two choices, each choice will receive the total voting power of the user. **Pros**: Ask for approval of several decisions in a single proposal.\ **Cons**: There is no way to manifest *how much* you support each choice. It’s a simple yes or no. [**Try it yourself**](https://snapshot.box/#/s:pistachiodao.eth/proposal/0x08c3bd2960700525770a1d634f8599ba967e55fcc05b6c1649d984d88253769d) ### Quadratic voting Snapshot's Quadratic Voting (QV) type goes beyond the conventional approach of simply calculating the square root of each voter's voting power. It presents a more nuanced and democratic framework for decision-making. One of the main features of our QV type is its **emphasis on the number of individual voters** rather than the size of their voting power. By doing so, it ensures that every voice counts, thereby enhancing collective decision-making and preventing power concentration. Additionally, Snapshot's QV type provides voters with the flexibility to **distribute their voting power across multiple choices**. This feature allows for a more precise representation of a voter's diverse opinions, all without any additional cost. Drawing key principles from the Quadratic Funding model, our QV type **fosters greater participation and effectively balances influence**. It represents a significant advancement over simpler voting mechanisms that rely on basic square root calculations of voting power. #### **Let's have a look at an example:** Let's consider there are 3 voters and two choices, A and B: * Alice with 9 tokens * Bob with 4 tokens * Maria with 1 token * John with 1 token Here's how they allocate their tokens: * Alice allocates all her 9 tokens to A * Bob allocates all his 4 tokens to B * Maria allocates her 1 token to B * John allocates his 1 token to B This results in: * A having 9 tokens * B having 6 tokens Now, let's calculate the individual square root contributions for each choice: * A: the square root of Alice's 9 tokens is √9 = 3. * B: the square root of Bob's 4 tokens is √4 = 2, the square root of Maria's 1 token is √1 = 1, and the square root of John's 1 token is √1 = 1. Next, we add up the square root contributions for each choice and square the result: * A: we square the 3, so A gets 3^2 = 9. * B: we add 2, 1, and 1, giving us 4, and square it, so B gets 4^2 = 16. The total amount of tokens is 9 + 16 = 25. So the percentages for each choice are: * A: 9 / 25 = 36% * B: 16 / 25 = 64% As a last step, we match these percentages with the total voting power of 14 tokens: * A: 36% of 15 = 5.4 tokens * B: 64% of 15 = 9.6 tokens All in all, you don't have to understand each step of the calculation. Yet, it should give you an idea of how Alice with 9 tokens was not able to push through his choice despite having the majority of Voting Power in the group as our QV model emphasizes the **number of individual contributors** rather than the amount contributed. This voting type may encourage whales to create multiple wallets and split their holdings. It is important to also implement a [validation strategy](/user-guides/validation-strategies) providing Sybil resistance. **Pros**: Dilutes the whales' voting power in favor of smaller holders. Individuals will matter more than the number of tokens.\ **Cons**: This voting type needs to be accompanied by a [Sybil-resistance mechanism](/user-guides/validation-strategies) that prevents whales from splitting funds across different wallets. [**Try it yourself**](https://snapshot.box/#/s:pistachiodao.eth/proposal/0x21f64875abbca71762a980efae43ab62b546d54f19a208d0e61a5d7cee571a35) ### Ranked choice voting (Instant Runoff Voting) Each user has to rank all choices in a desired order. In the **first step** votes are counted for each voter's number one choice. If a choice receives more than 50% votes (cast on number one choices of each user), that choice wins. The result will show the percentages reflecting how users voted for their **first choice only**. In the **second step** if the first-choice candidate doesn't get over 50% of the total votes the choice with the **fewest** number one votes is **eliminated**. Voters who had chosen the defeated choice as number one now have their number two choice **counted as their number one** choice. The process continues over multiple rounds until a choice has more than half (> 50%) of the total votes. For more details, read [this discussion](https://github.com/snapshot-labs/snapshot/discussions/1624). Some options will show up as if they had **not received any votes** because they were **eliminated** in the ranked-choice voting process. **Pros**: Favors the option with the strongest support and reduces wasted votes.\ **Cons**: Complex to understand. Will only determine one winner, doesn’t work well to select 2 or more winning options. [**Try it yourself**](https://snapshot.box/#/s:pistachiodao.eth/proposal/0x5003da0f03e718b461e53fe10a998b60172e2e108472153282fcef781c300f23) ### Basic voting Each user can select one of three options: `For`, `Against`, `Abstain`. The votes cast on the `Abstain` choice are counted in calculating if the necessary quorum has been reached for a proposal to pass. **Pros**: Results are easy to interpret and hard to contest.\ **Cons**: Choices are predefined and cannot be edited. [**Try it yourself**](https://snapshot.box/#/s:pistachiodao.eth/proposal/0x38c654c0f81b63ea1839ec3b221fad6ecba474aa0c4e8b4e8bc957f70100e753) # Overview Source: https://docs.snapshot.box/snapshot-x/overview Snapshot X is a fully onchain voting protocol. Use these docs to learn how to create spaces, submit proposals, cast votes, and integrate with the protocol. **Non-technical documentation for the platform.** Go here if you want to create a space, proposal or cast a vote. **Technical documentation for Snapshot X protocol.** Go here if you want to understand the protocol architecture. **Overview of the services built for a better UX when interacting with the protocol.** Go here if you want to integrate Snapshot X in your platform. ## What is Snapshot X? **Snapshot X** is an onchain voting protocol that utilizes modular smart contracts for all operations, offering censorship resistance, onchain voting power computation, and trustless proposal execution. The difference with the original [Snapshot](https://snapshot.box) is that Snapshot X is **fully onchain**. What this means is: * **The protocol is censorship resistant**: Anyone can cast a vote. The protocol runs without any reliance on offchain or centralized services which have the power to censor votes. \[1] * **Voting power is computed onchain**: The voting logic is fully onchain and auditable so you can be sure of the logic used to compute voting power and decide on the outcome of proposals. * **The execution is trustless**: Proposal transactions are automatically executed following the passing of a proposal. Say you create a new proposal which, if it passes, will transfer 1 ETH to `vitalik.eth`. If the proposal passes, the 1 ETH will automatically get sent to `vitalik.eth`, without any further human action needed. Snapshot can be found on EVM-chains and on Starknet. Both the [EVM implementation](https://github.com/snapshot-labs/sx-evm) and the [Cairo implementation](https://github.com/snapshot-labs/sx-starknet) are open source. The Starknet's specific details can be found on [this dedicated page](/snapshot-x/protocol/starknet-specifics). If anything in these docs is unclear or you would like more detail, do not hesitate to reach out on [Discord](https://discord.snapshot.org). \[1] We note that there are in fact offchain services (eg the relayer Mana) built for use with Snapshot X, but these are not mandatory and therefore cannot lead to censorship. # Audits Source: https://docs.snapshot.box/snapshot-x/protocol/audits Snapshot X has completed a full audit with [ChainSecurity](https://chainsecurity.com) and [OpenZeppelin](https://openzeppelin.com). | Scope | Company | Date | Report | | ------------------- | ----------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | | Snapshot X EVM | [ChainSecurity](https://www.chainsecurity.com/) | 03-07-23 | [Download](https://github.com/snapshot-labs/sx-evm/blob/main/audits/ChainSecurity_Snapshot_Snapshot_X_audit.pdf) | | Snapshot X Starknet | [OpenZeppelin](https://www.openzeppelin.com/) | 10-31-23 | [Download](https://github.com/snapshot-labs/sx-starknet/blob/develop/audits/OpenZeppelin-SnapshotX-Audit.pdf) | | Snapshot X EVM | [Cairo Security Clan](https://cairosecurityclan.com/) | 16-06-25 | [Download](https://github.com/snapshot-labs/sx-evm/blob/main/audits/CSC_SnapshotX_audit.pdf) | # Authenticators Source: https://docs.snapshot.box/snapshot-x/protocol/authenticators Authenticators are the contracts in charge of **authenticating users** to create proposals and cast votes. **All proposal creation, proposal update, and vote transactions must be sent to the relevant DAO's space contract via an authenticator.** DAOs are free to write their own custom authenticators that suit their own needs however we provide the following approaches: ### Ethereum signature authenticator Will authenticate a user based on a message signed by an Ethereum private key. Users create an EIP712 signature for the transaction which is checked for validity in this contract. ```solidity theme={null} function authenticate( uint8 v, bytes32 r, bytes32 s, uint256 salt, address target, bytes4 functionSelector, bytes calldata data ) external; ``` * `v`,`r`,`s`: ECDSA Signature * `salt` : The salt used in the signature to prevention replays (only required for proposal creation and updating). * `target`: The destination space contract. * `functionSelector`: The function selector of the desired action. * `data`: The ABI encoded transaction payload for the action. Refer to the [Space actions](/snapshot-x/protocol/space-actions) section for more information on the payload contents. This can work in conjunction with a meta transaction relayer to allow proposal creation or vote costs to be sponsored by the DAO, providing a free end user experience. ### Ethereum transaction authenticator Will authenticate a user by checking if the caller address corresponds to the `author` or `voter` address. ```solidity theme={null} function authenticate(address target, bytes4 functionSelector, bytes calldata data) external; ``` * `target`: The destination space contract. * `functionSelector`: The function selector of the desired action. * `data`: The ABI encoded transaction payload for the action. Refer to the [Space actions](/snapshot-x/protocol/space-actions) section for more information on the payload contents. The core use case for this authenticator is to allow **smart contract accounts such as multisigs to use Snapshot X** as they have no way to generate a signature and therefore cannot authenticate via signature verification. ### And more! Our modular approach here allows spaces to **authenticate users via other authentication methods without any changes to the space contract**. Please note, that if you wanted to add sybil resistance to your governance process, this should not be handled by Authenticators, but by [Proposal validations](/snapshot-x/protocol/proposal-validations) or [Voting strategies](/snapshot-x/protocol/voting-strategies). # Execution strategies Source: https://docs.snapshot.box/snapshot-x/protocol/execution-strategies Execution Strategies have two key roles: 1. **Determining the status of a proposal at any time**, 2. **Executing the payload of a proposal if it has been accepted**. Each proposal should have an Execution strategy set when it is created. This consists of a strategy contract address, along with an execution payload. This page provides more details on the Execution strategies along with the implementations that we offer. ## Proposal status Every execution strategy should have a public view function `getProposalStatus` with the following interface: ```solidity theme={null} function getProposalStatus( Proposal memory proposal, uint256 votesFor, uint256 votesAgainst, uint256 votesAbstain ) external view returns (ProposalStatus); ``` This function takes the proposal state as input and returns the proposal status defined by a `ProposalStatus` enum. This enum contains all of the possible states the proposal can be in: ```solidity theme={null} enum ProposalStatus { VotingDelay, VotingPeriod, VotingPeriodAccepted, Accepted, Executed, Rejected, Cancelled } ``` Note that because of having separate `minVotingDuration` and `maxVotingDuration` , we have two separate statuses for inside the voting period: `VotingPeriod` and `VotingPeriodAccepted`. Abstracting status functionality outside of the space contract allows far greater flexibility in governance mechanisms since users can define their own logic on exactly how the status is determined. We provide the following implementations: ### [Simple Quorum](https://github.com/snapshot-labs/sx-evm/blob/main/src/execution-strategies/SimpleQuorumExecutionStrategy.sol) A proposal is accepted if `FOR` votes exceed `AGAINST` votes and the total votes (`FOR` + `AGAINST` + `ABSTAIN` ) exceed the `quorum`. Has a single parameter `quorum` which is set when the strategy is deployed. This is the standard approach that we expect to be used most often. ### [Optimistic Quorum](https://github.com/snapshot-labs/sx-evm/blob/main/src/execution-strategies/OptimisticQuorumExecutionStrategy.sol) A proposal is rejected if `AGAINST` votes exceed the `quorum`, otherwise, it is accepted. It has a single parameter `quorum` that is set when the strategy is deployed. This approach unlocks **optimistic governance mechanisms** where proposals are assumed to be accepted unless DAO members choose otherwise by voting against. This can lead to a far higher level of governance efficiency as it reduces the number of onchain transactions for 'non-controversial' proposals. ### [Emergency Quorum](https://github.com/snapshot-labs/sx-evm/blob/main/src/execution-strategies/EmergencyQuorumExecutionStrategy.sol) It has two parameters: `quorum` and `emergencyQuorum`, where `emergencyQuorum` should be set greater than `quorum`. If the total votes are less than `emergencyQuorum`, then the proposal status is computed in the same way as Simple Quorum with the `quorum` parameter. However if the higher `emergencyQuorum` is met, then the `minVotingDuration` is **ignored** and a proposal can be **executed early.** This can be useful for emergency actions in response to critical events such as hacks, where one can expect much higher participation in the governance vote than during normal processes, and therefore a 'tradeoff' between proposal duration and proposal participation can be made. ## Proposal execution If the proposal status is `Accepted` (or `VotingPeriodAccepted`) the Execution strategy should then execute the proposal payload. We provide the following payload executor implementations: ### [Safe module (Zodiac)](https://github.com/snapshot-labs/sx-evm/blob/main/src/execution-strategies/AvatarExecutionStrategy.sol) An execution strategy that allows proposals to execute transactions from a specified `target` [Avatar](https://github.com/gnosis/zodiac/blob/master/contracts/interfaces/IAvatar.sol) contract, the most popular one being a Safe. To use this strategy in a proposal, you must whitelist the space in the strategy, this can either be done at deployment or using the `enableSpace` function. The proposal payload should consist of an ABI encoded array of type `MetaTransaction[]` with the following format: ```solidity theme={null} struct MetaTransaction { address to; uint256 value; bytes data; Enum.Operation operation; uint256 salt; } ``` ### [Timelock](https://github.com/snapshot-labs/sx-evm/blob/main/src/execution-strategies/timelocks/TimelockExecutionStrategy.sol) An execution strategy that is itself a Timelock contract. A `timelockDelay` is specified when the Timelock is deployed. When a proposal with this strategy is executed, the proposal transactions are queued in the Timelock for `timelockDelay` seconds before they can be executed. The proposal payload should consist of an ABI encoded array of type `MetaTransaction[]` with the following format: ```solidity theme={null} struct MetaTransaction { address to; uint256 value; bytes data; Enum.Operation operation; uint256 salt; } ``` There is also an optional `vetoGuardian` role that has the power **to `veto` a queued proposal.** ### And more! Feel free to create your own Execution strategies! The interface of a strategy can be found [here](https://github.com/snapshot-labs/sx-evm/blob/main/src/interfaces/IExecutionStrategy.sol). # Overview Source: https://docs.snapshot.box/snapshot-x/protocol/overview Snapshot X's main contract is the **Space** contract. Proposals are created, votes are cast, and all of the proposal state is stored here. Beyond this however, the majority of the protocol functionality has been abstracted out of the Space contract. These abstractions consist of: * [Who can create a proposal](/snapshot-x/protocol/proposal-validations). * [How users are authenticated](/snapshot-x/protocol/authenticators). * [How the voting power is determined](/snapshot-x/protocol/voting-strategies). * [How a proposal passes and how it's being executed](/snapshot-x/protocol/execution-strategies). This approach allows a far higher degree of modularity and customisability than what would be possible if all of this logic was handled directly by the Space contract itself. Creating a space on Snapshot X is **like creating a new character in a game**. First you need to select an armour to protect your character, then you need to decide on a weapon (spear, sword, bow). Finally, you need to choose a mount for your character to get him to travel from town to town faster. With that analogy in mind, think about the **Space** contract like the character, and the [**Authenticators**](/snapshot-x/protocol/authenticators), [**Proposal validation strategies**](/snapshot-x/protocol/proposal-validations), [**Voting strategies**](/snapshot-x/protocol/voting-strategies)**,** and [**Execution strategies**](/snapshot-x/protocol/execution-strategies) like the different add-ons you need to decide on. We refer to them collectively as **Snapshot X Modules**. Let's go over those briefly: **Authenticators** \ Determine how the participants can be *authenticated* to create a proposal or cast a vote. In general this will be some kind of signature or transaction caller verification. **Proposal validation strategies**\ Determine how an author is validated to create a proposal. A common approach is checking that the author exceeds a certain threshold proposition power calculated through a set of strategies. **Voting strategies**\ Determine how the voting power is computed. Voting strategies are custom contracts that allow you to determine how you compute the voting power of each participant in the vote. Arbitrary logic can be implemented here to create complex and expressive governance mechanisms. The most common way of voting is voting with your tokens (1 token = 1 voting power, so if you have 100 tokens you get a total voting power of 100). This is a voting strategy. But you could imagine other voting strategies: for example, only give voting power to the owners of a specific NFT. Or maybe use a quadratic model, where users only get the square root of their token balance (to minimise the power of whales). **Execution strategies**\ Have two key roles: 1\) Determine the *status* of a proposal at any time during its lifetime. 2\) Determine *what should happen* once a proposal passes. This latter role will generally consist of executing a set of transactions linked to the proposal. The strategy can control which contracts are authorised to execute the transactions or alternatively execute them directly itself. This modularity allows anyone to extend the basic protocol to suit their requirements. We provide a set of pre-built and audited modules but **we invite you to write your own!** ## So what does the flow look like? The full usage flow looks like this: 1. A DAO deploys a **Space** contract and defines space settings along with a set of authenticators, voting strategies, and a proposal validation strategy. 2. A user can create a **proposal** in the **Space** via a whitelisted authenticator. The proposal validation strategy is queried to check that the user is eligible to create the proposal. \ To create a proposal, the proposer has to specify an execution strategy contract along with an execution payload that will contain the encoded set of transactions inside the proposal. 3. Users can vote on the proposal by authenticating through one of the whitelisted authenticator contracts. 4. Once the voting period has ended, anyone can execute the proposal permissionlessly. This will forward the execution payload to the execution strategy and execute the transactions included. The below diagram showcases an example of how the contracts that make up Snapshot X fit together along with various actions users can take to create proposals, vote, execute proposals, and update settings. [https://whimsical.com/snapshot-x-evm-onchain-architecture-M3d8bU1V2Z7r6kbVUM9g2w](https://whimsical.com/snapshot-x-evm-onchain-architecture-M3d8bU1V2Z7r6kbVUM9g2w) If you are planning to integrate Snapshot X into your own product, head to [SX.js](/snapshot-x/services/sx-js) section to learn about our SDK. # Proposal validations Source: https://docs.snapshot.box/snapshot-x/protocol/proposal-validations **Proposal validation strategy is used to determine whether a particular `author` is allowed to create a proposal.** Each Space has to set a proposal validation strategy which consists of an address and a set of `params` that are stored in the space. These strategies should have the following interface: ```solidity theme={null} function validate(address author, bytes calldata params, bytes calldata userParams) external returns (bool); ``` * `author`: The author of the proposal. * `params`: Parameter array set in the space contract that is the same for every user of the Strategy. * `userParams`: Parameter array submitted by the `author` when they create a proposal, and can therefore be different for each user. There is no requirement to use either of these parameter arrays in your strategy, in which case you can just pass an empty array. DAOs are free to write their own custom strategies that suit their own needs however we provide the following approaches: ### Proposition power A strategy that validates an `author` to create a proposal if their `propositionPower` calculated from a set of Voting strategies exceeds a `proposalThreshold` value. It means that if the proposal threshold is set to `5`, the author needs to have at least `5` Proposition power to create a proposal. This strategy uses the same logic as [Voting strategies](/snapshot-x/protocol/voting-strategies), so refer to that section for more information. When calling this strategy, `params` should be: ```solidity theme={null} uint256 proposalThreshold = ... Strategy[] allowedStrategies ... params = abi.encode(proposalThreshold, allowedStrategies); ``` `userParams` should contain the indexed strategies that the `author` has power with: ```solidity theme={null} IndexedStrategy[] userStrategies = ... userParams = abi.encode(userStrategies); ``` ### Active proposal limiter A strategy that validates an `author` to create a proposal if the `author` has not exceeded a limit of `maxActiveProposals`. Each time an `author` creates a proposal, a counter is incremented up to a limit of `maxActiveProposals`. After this point, no more proposals can be created until a `cooldown` period has elapsed since the most recent proposal they created. Using this strategy can help to prevent proposal creation spam in your space. ### And more! These strategies can be combined and extended to provide flexible Proposal validation mechanisms. The interface for Proposal validation strategies can be found [here](https://github.com/snapshot-labs/sx-evm/tree/main/src/proposal-validations). # Space actions Source: https://docs.snapshot.box/snapshot-x/protocol/space-actions [Space](https://github.com/snapshot-labs/sx-starknet/blob/develop/starknet/src/space/space.cairo) is the core contract: it's in charge of tracking proposals, votes, and general settings. In this section we will go into detail on how to deploy a space, and the various user actions that can take place. ### Deploying a space Space should be deployed via the **Proxy Factory contract**, which emits deployment events that will get indexed by the [SX-API](/snapshot-x/services/api). To reduce deployment costs and to enable upgradability, we use ERC-1967 proxies and follow Open Zeppelin's [UUPS Upgradability pattern](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable) on the space contract implementation. After deploying the proxy you should call the `initialize` function with the initial set of Space settings. ```solidity theme={null} function initialize( address owner, uint32 votingDelay, uint32 minVotingDuration, uint32 maxVotingDuration, Strategy memory proposalValidationStrategy, string memory proposalValidationStrategyMetadataURI, string memory daoURI, string memory metadataURI, Strategy[] memory votingStrategies, string[] memory votingStrategyMetadataURIs, address[] memory authenticators ) external; ``` * `owner`: The address of the account that controls the Space contract. This account will have permissions to update space settings, cancel a proposal, and authorize an upgrade to the implementation. If you would like to remove this trust assumption, the owner can be renounced. Refer to the [Space controller actions](/snapshot-x/protocol/space-controller-actions) section for more information. * `votingDelay`: The delay between when a proposal is created, and when the voting starts. A value of 0 means that as soon as the proposal is created anyone can vote whilst a value of 3600 means that voting will start 3600 seconds after the proposal is created. * `minVotingDuration`: The minimum duration of the voting period. It is **impossible to execute a proposal before** **this period** has elapsed. * `maxVotingDuration`: The maximum duration of the voting period, it is **impossible to cast a vote after this period** has passed. The `minVotingDuration` must be less than or equal to this value. * `proposalValidationStrategy`: A strategy that validates whether an author can create a proposal. The strategy consists of a contract address where the strategy lives, along with a set of parameters that configure it for the particular usage. More information in the [Proposal validation strategy](/snapshot-x/protocol/proposal-validations) section. * `proposalValidationStrategyMetadataURI`: A metadata URI corresponding to the `proposalValidationStrategy`. * `daoURI`: A metadata URI as defined in ERC-4824. * `metadataURI`: The metadata URI for the space (its name, description, social tags and treasury address). * `votingStrategies`: An array of voting strategies selected for the space. The voting power of each user will be calculated as the sum of voting powers returned for each strategy in the list for that user. More information in the [Voting strategies](/snapshot-x/protocol/voting-strategies) section. * `votingStrategyMetadataURIs`: An array of metadata URIs corresponding to the `votingStrategies` array. * `authenticators`: An array of whitelisted authenticators. These are the ways in which a user can authenticate themselves in order to vote or propose. More information in the [Authenticators](/snapshot-x/protocol/authenticators) section. Each DAO on Snapshot X will have at least one space, however a DAO might choose to have multiple spaces if they want to create different rules for individual proposals. As an example one space can use an Ethereum signature as an authentication, another an Ethereum transaction. ### Creating a proposal Once a space has been created, users can create new proposals by calling the `propose` method. Inside `propose`, the Proposal Validation Strategy is called to validate `author` upon creating a proposal. ```solidity theme={null} function propose( address author, string calldata metadataURI, Strategy calldata executionStrategy, bytes calldata userProposalValidationParams ) external; ``` * `author`: The address of the proposal author. * `metadataURI`: The metadata URI of the proposal. * `executionStrategy`: The execution strategy that should be used to execute a proposal if it is accepted. The strategy consists of a contract address where the strategy lives, along with an encoded execution payload. More information in the [Execution strategies](/snapshot-x/protocol/execution-strategies) section. * `userProposalValidationParams`: An array of parameters that will be passed to the Proposal Validation Strategy for the Space. Since the proposal is created via an authenticator, the exact external interface for creating a proposal will depend on the interface of the chosen authenticator. ### Updating a proposal Provided that the `votingDelay` has not elapsed yet, the proposal `author` can update the proposal metadata and execution strategy. This allows mistakes in the initial proposal to be fixed. ```solidity theme={null} function updateProposal( address author, uint256 proposalId, Strategy calldata executionStrategy, string calldata metadataURI ) external; ``` * `author`: The address of the proposal author. * `proposalId`: The unique ID of the proposal in the space. IDs are assigned incrementally to proposals based on when the proposal was created. * `executionStrategy`: The updated execution strategy that should be used to execute a proposal if it is accepted. The strategy consists of a contract address where the strategy lives, along with an encoded execution payload. More information in the [Execution strategies](/snapshot-x/protocol/execution-strategies) section. * `metadataURI`: The updated metadata URI of the proposal. Since the proposal is updated via an authenticator, the exact external interface for updating a proposal will depend on the interface of the chosen authenticator. ### Casting a vote Once a proposal has been created, and the `votingDelay` has elapsed, users can then vote for the proposal. ```solidity theme={null} function vote( address voterAddress, uint256 proposalId, Choice choice, IndexedStrategy[] calldata userVotingStrategies, string calldata metadataURI ) external; ``` * `voter`: The address of the voter. * `proposalId`: The unique ID of the proposal in the space. IDs are assigned incrementally to proposals based on when the proposal was created. * `choice`: The vote choice: `FOR`, `AGAINST`, or `ABSTAIN`. * `userVotingStrategies`: An array of voting strategies that should be iterated through to calculate the voter's voting power. The strategies in this array must be whitelisted by the space however there is no requirement to pass all of the whitelisted strategies. This could be useful when a user knows that they only have voting power on a subset of the whitelisted strategies and therefore can save gas by only passing strategies that they know they have non zero voting power on. * `metadataURI`: The metadata URI for the vote. Since the vote is cast via an authenticator, the exact external interface for casting a vote will depend on the interface of the chosen authenticator. ### Execute a proposal Calling the `execute` function on the space will call the execution strategy with the proposal state along with the execution payload. The execution strategy will then use the proposal state to compute the status of the proposal. If the proposal status is deemed to be `Accepted,` then the payload will be automatically executed by the strategy. If the proposal status is not `Accepted` (or `VotingPeriodAccepted`), then the transaction will revert. Note that there is no caller authentication on `execute`, simply call the`execute` method on the Space contract directly: ```solidity theme={null} function execute(uint256 proposalId, bytes calldata executionPayload) external; ``` * `proposalId`: The ID of the proposal. * `executionPayload`: The payload of the execution. This must be the same as the payload passed when a proposal was created. We require the payload to be resubmitted because we don't store it inside the proposal state, instead, we just store its hash. ### Querying the proposal status We provide the following view function to access the proposal status at any time. ```solidity theme={null} function getProposalStatus(uint256 proposalId) external view returns (ProposalStatus proposalStatus); ``` The status of a proposal is actually defined by the chosen Execution strategy rather than the space itself, therefore this query actually makes an internal call to the Execution strategy of the proposal. Refer to the [Execution strategies](/snapshot-x/protocol/execution-strategies) section for more information. * `proposalId`: The ID of the proposal to query. * `voter`: The address of the voter to query. * `proposal`: The proposal state struct object, defined [here](https://github.com/snapshot-labs/sx-evm/blob/aaed4d0dd2ad915e05fb7bad094f587fed113f7b/src/types.sol#L8). * `proposalStatus`: The status of a proposal at the timestamp when queried. This function will send an internal call to the `getProposalStatus` method on the Execution strategy. Refer to the [Execution strategies](/snapshot-x/protocol/execution-strategies) section for more information. ### ERC-4824: Decentralized Autonomous Organizations The Space contract implements ERC-4824 which is a standard URI and JSON schema for DAOs, focusing on relating onchain and offchain representations of membership and proposals. More information can be found [here](https://ethereum-magicians.org/t/erc-4824-decentralized-autonomous-organizations/8362). The standard adds a `daoURI` string to the space settings which can be queried using the following interface: ```solidity theme={null} interface EIP4824 { function daoURI() external view returns (string _daoURI); } ``` # Space controller actions Source: https://docs.snapshot.box/snapshot-x/protocol/space-controller-actions In this section we will go over the actions that can be made by the Space Controller. Note that we use Open Zeppelin's [`OwnableUpgradable`](https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/master/contracts/access/OwnableUpgradeable.sol) module to gate access to this functions, and therefore at the contract level we use the term `owner` instead of `controller`. ### Cancel a proposal A proposal can be cancelled as long as it has not already been executed. ```solidity theme={null} function cancel(uint256 proposalId) external; ``` * `proposalId`: The ID of the proposal This can be used to prevent damage from malicious or broken proposals. ### Update space settings All Space settings can be updated using the `updateSettings` function: ```solidity theme={null} function updateSettings(UpdateSettingsInput calldata input) external; struct UpdateSettingsInput { uint32 minVotingDuration; uint32 maxVotingDuration; uint32 votingDelay; string metadataURI; string daoURI; Strategy proposalValidationStrategy; string proposalValidationStrategyMetadataURI; address[] authenticatorsToAdd; address[] authenticatorsToRemove; Strategy[] votingStrategiesToAdd; string[] votingStrategyMetadataURIsToAdd; uint8[] votingStrategiesToRemove; } ``` A single entrypoint is used instead of separate ones for each value so that a single transaction can be used to update a large number of settings. This improves the UX while also preventing undesired behaviour that may arise if proposals are created half way though the settings update process. If one does not want to update a certain value, then the following placeholder values can be used in the function call (arrays can just be left empty): ```solidity theme={null} /// @dev Evaluates to: `0xf2cda9b13ed04e585461605c0d6e804933ca828111bd94d4e6a96c75e8b048ba`. bytes32 NO_UPDATE_HASH = keccak256(abi.encodePacked("No update")); /// @dev Evaluates to: `0xf2cda9b13ed04e585461605c0d6e804933ca8281`. address NO_UPDATE_ADDRESS = address(bytes20(keccak256(abi.encodePacked("No update")))); /// @dev Evaluates to: `0xf2cda9b1`. uint32 NO_UPDATE_UINT32 = uint32(bytes4(keccak256(abi.encodePacked("No update")))); Strategy NO_UPDATE_STRATEGY = Strategy(NO_UPDATE_ADDRESS, new bytes(0)); ``` Note that Space setting updates will not affect the functioning of ongoing proposals at the time of the settings update since we store the necessary settings data inside the state of each proposal. ### Upgrade implementation A Space contract's implementation can be upgraded to a new version using the following methods: ```solidity theme={null} function upgradeTo(address newImplementation) external; function upgradeToAndCall(address newImplementation, bytes memory data) external; ``` * `newImplementation`: The address of the new space implementation. * `data`: A encoded function call to initialize the new implementation. Refer to [Open Zeppelin's UUPS guide](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable) for more information. ### Manage ownership ```solidity theme={null} function transferOwnership(address newOwner) external; function renounceOwnership() external; ``` The `owner` can be transferred to a new address or renounced entirely. # Starknet specifics Source: https://docs.snapshot.box/snapshot-x/protocol/starknet-specifics ### What's special here? Starknet having its own language (Cairo) and being a ZK Rollup, there are some Starknet specific details. The Snapshot X Starknet implementation allows DAOs to govern their L1 DAO trustlessly, with very little fees and no user friction. Indeed, users do not have to bridge their tokens, nor do they need to create and install a Starknet wallet. This is all handled seamlessly by the Snapshot X protocol by taking advantage of the existing native bridge, some storage proofs, and a transaction relayer. ### Overview This diagram represents the flow of a proposal that will get executed on L1, with voters using their regular EVM account to vote.\\ [https://whimsical.com/storageproofs-7kjLqMvsR3okzFgMrad9Fu](https://whimsical.com/storageproofs-7kjLqMvsR3okzFgMrad9Fu) ### Storage proofs In a nutshell, storage proofs are cryptographic proofs that a user had this token or that NFT in their wallet at a specific moment in time. We use them to compute the voting power of any user at the moment the proposal started, without the user having to bridge their tokens. This technology developed in collaboration with [Herodotus](https://herodotus.dev/), allows for a trustless balance verification that we can use to compute the voting power of users. The exact code for verifying a storage value can be found [here](https://github.com/snapshot-labs/sx-starknet/blob/develop/starknet/src/utils/single_slot_proof.cairo). 1. A user creates a proposal. The space registers it and [stores the start timestamp](https://github.com/snapshot-labs/sx-starknet/blob/03041771ba701a6ce0d5a95b4ead290add115348/starknet/src/space/space.cairo#L259). 2. When the proposal starts, the space caches the block number on L1 that corresponds to the stored `start timestamp`. To do that, we use [Herodotus' timestamp remapper contract](https://docs.herodotus.dev/herodotus-docs/protocol-design/timestamp-to-block-number-mapper). 3. Later on, when a user vote, he provides a storage proof of his balance at the block cached by the space. The space then provides the `storage_proof`, the `block_number`, and the `l1_contract_address` to [Herodotus' Facts Registry](https://docs.herodotus.dev/herodotus-docs/developers/contract-addresses) for verification. 4. If the verification succeeds, the space can safely accept the vote, the `voting_power` of the user having been correctly proven and verified. For OpenZeppelin ERC20Votes tokens, the function `get_past_votes` is reproduced in [this voting strategy](https://github.com/snapshot-labs/sx-starknet/blob/03041771ba701a6ce0d5a95b4ead290add115348/starknet/src/voting_strategies/oz_votes_storage_proof.cairo#L29) by proving two values: we first verify the checkpoint `c` containing the number of delegated power ; and then verify that the checkpoint `c + 1` is empty (ensuring that `c` was indeed the latest checkpoint). As a last detail, the `Checkpoint` structure got changed by OpenZeppelin to a Checkpoint 208, so we [also support this version](https://github.com/snapshot-labs/sx-starknet/blob/develop/starknet/src/voting_strategies/oz_votes_trace_208_storage_proof.cairo). ### Transaction relayer In order to avoid the overhead of installing and managing a Starknet account, we ask users to sign their vote with their Ethereum wallet, and then simply relay the transaction on the Starknet network using our transaction relayer [Mana](/snapshot-x/services/mana). Once the transaction is relayed, and by leveraging the modularity of our authenticators system, a [special authenticator](https://github.com/snapshot-labs/sx-starknet/blob/develop/starknet/src/authenticators/eth_sig.cairo) verifies the provided signature and lets the vote be counted in! But who pays for the relaying fees? It is expected that DAOs who want to provide their users with this ease of vote would be the paying those fees, which should be minimal anyway! ### L1 execution Once the proposal has ended, and if the proposal has been accepted, then the transaction can be broadcast back to our [L1 Avatar Execution Strategy](https://github.com/snapshot-labs/sx-starknet/blob/develop/ethereum/src/execution-strategies/L1AvatarExecutionStrategy.sol) contract. This module works hand in hand with Zodiac's Avatar (e.g a Safe), and allows the transactions attached to the proposal to be executed directly on the avatar! # Voting strategies Source: https://docs.snapshot.box/snapshot-x/protocol/voting-strategies Voting strategies are the contracts used to **determine the voting power (VP) of users**. Voting strategies can be created in a **permissionless** way, however, to use one, one must whitelist the strategy in the space contract for your DAO. The most common example is using the ERC-20 token balance of a user to determine their voting power. But you could imagine other voting strategies: owning a specific NFT, owning NFT of collection X and another NFT of collection Y, having participated in protocol XYZ... the possibilities are endless! We are fans of the [Turing Complete Governance](https://baby.mirror.xyz/O7a922A-9zT4C4UwssRExkftdHywJ-13sR2rxQ-t__k), the concept of a governance system with arbitrary programmability. This will allow complex and expressive mechanisms of coordination to be seamlessly integrated into governance decisions. Snapshot X reaches this standard, and we are excited to see what people build! **All voting strategies must have the following** [**interface**](https://github.com/snapshot-labs/sx-evm/blob/main/src/interfaces/IVotingStrategy.sol)**:** ```solidity theme={null} function getVotingPower( uint32 blockNumber, address voterAddress, bytes calldata params, bytes calldata userParams ) external returns (uint256); ``` `getVotingPower` is called internally by the space contract when a vote is cast. `timestamp` is the snapshot at which voting power is calculated for all users. We use a **timestamp rather than a block number** to better enable multi-chain applications since a timestamp is universal to all chains. There are two sets of parameters for each voting strategy, `params` and `userParams`. * `params` are set in the space contract and are the same for every user who calls that strategy in the space, they act as configuration parameters for a strategy. An example here is a token contract address that will be queried, or a constant scaling factor that should be applied to the VP returned for every user. * `userParams` are submitted by users when they create a proposal or cast a vote, and can therefore be different for each user. An example here is storage proof. There is no requirement to use either of these parameter arrays in your strategy, in which case you can just pass an empty array. When a user casts a vote, the array of whitelisted Voting strategies will be iterated through and `getVotingPower` will be called on each. The VP of the user will be the aggregate of the voting power from each strategy. The aggregate Voting power for all users is also stored inside a `Uint256`, therefore when writing or selecting voting strategies, it is **important to consider the likelihood of overflow**. DAOs are free to write their own custom strategies that suit their own needs however we provide the following approaches: ### [Compound Style Checkpoint Token](https://github.com/snapshot-labs/sx-evm/blob/main/src/voting-strategies/CompVotingStrategy.sol) A strategy that allows delegated balances of Compound style checkpoint tokens to be used as Voting power. To use this strategy, your token must have the following interface exposed: ```solidity theme={null} function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96); ``` When calling this strategy, `params` should be: ```solidity theme={null} params = abi.encodePacked(tokenAddress) ``` `userParams` is not needed. ### [OpenZeppelin Style Checkpoint Token](https://github.com/snapshot-labs/sx-evm/blob/main/src/voting-strategies/OZVotesVotingStrategy.sol) A strategy that allows delegated balances of OpenZeppelin style checkpoint tokens to be used as Voting power. To use this strategy, your token must have the following interface exposed: ```solidity theme={null} function getPastVotes(address account, uint256 blockNumber) external view returns (uint256); ``` When calling this strategy, `params` should be: ```solidity theme={null} params = abi.encodePacked(tokenAddress) ``` `userParams` is not needed. ### [Whitelist](https://github.com/snapshot-labs/sx-evm/blob/main/src/voting-strategies/WhitelistVotingStrategy.sol) A strategy that returns a specified Voting power for addresses that are within a whitelist, and zero otherwise. Having a customisable VP for each user allows more fine-grained control than enforcing a VP of 1 for everyone - which can still be achieved by just setting the value to 1 for each member of the list. Each member of the whitelist should be represented as the struct: ```solidity theme={null} struct Member { address addr; uint256 vp; } ``` When calling this strategy, `params` should then be an ABI encoded array of type `Member[]` where the members are sorted into ascending order based on their `addr`. ``` Member[] members = ... params = abi.encode(members) ``` `userParams` is not needed. ### And more! Feel free to create your own strategies! We hope that the flexibility of the system will unlock a new era of programmable onchain governance. The interface of a strategy can be found [here](https://github.com/snapshot-labs/sx-evm/blob/main/src/interfaces/IVotingStrategy.sol). # API Source: https://docs.snapshot.box/snapshot-x/services/api The API indexes Snapshot X data. Specifically, it monitors events emitted by spaces and space factories so that votes, proposals, and the deployment of new spaces can be tracked. Anyone can run the API. It uses [Checkpoint](https://checkpoint.snapshot.box) and support multiple chains. #### GitHub [https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/api](https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/api) #### Public APIs Mainnets: [https://api.snapshot.box](https://api.snapshot.box/)\ Testnets: [https://testnet-api.snapshot.box](https://testnet-api.snapshot.box/) # Overview Source: https://docs.snapshot.box/snapshot-x/services/architecture **Snapshot X** is a fully onchain voting protocol deployed on EVM chains and Starknet. It consists of modular smart contracts that handle proposal creation, voting, and execution without reliance on any centralized service. Key properties: * **Censorship resistant:** the protocol runs entirely onchain with no centralized dependencies. * **Onchain voting power:** voting logic is fully onchain and auditable. * **Trustless execution:** proposal transactions are automatically executed when a proposal passes. Both the [EVM implementation](https://github.com/snapshot-labs/sx-evm) and the [Starknet implementation](https://github.com/snapshot-labs/sx-starknet) are open source. ## Services To enhance the user experience of interacting with the protocol, Snapshot Labs provides a number of additional offchain services. All are fully open source and completely optional - they do not introduce any censorship concerns. * [**API**](/snapshot-x/services/api): A fast indexer for all protocol data. * [**SX.js**](/snapshot-x/services/sx-js): A TypeScript SDK for building integrations with the protocol. * [**UI**](/snapshot-x/services/ui): An interface for interacting with the protocol. * [**Mana**](/snapshot-x/services/mana): A meta-transaction relayer that sponsors proposal creation and vote costs. # Mana Source: https://docs.snapshot.box/snapshot-x/services/mana Mana is a meta-transaction relayer that allows users to interact with Snapshot X without paying gas fees. This service works in conjunction with signature based Snapshot X [authenticators](/snapshot-x/protocol/authenticators). Users sign a meta transaction in the SX UI and then the relayer submits each encoded transaction along with its signature to the signature authenticator. If the signature is valid, then the transaction will be forwarded to the relevant space contract. The system included a mechanism that allows DAOs to fund the transactions sent by the relayer, enabling the free end user experience. However importantly, users can directly interact with Snapshot X and therefore do not rely on trusting the relayer to not censor votes. Additionally, Anyone can run a relayer if they want to take over the sponsoring of transactions. Note that currently Mana only supports proposal creation, proposal updates, and vote casting transactions. It does not currently support proposal execution or space controller actions. #### GitHub [https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/mana](https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/mana) #### Public API [https://mana.snapshot.box](https://mana.snapshot.box) # SX.js Source: https://docs.snapshot.box/snapshot-x/services/sx-js SX.js is the official Typescript SDK to interact with Snapshot X. The SDK includes a client for Mana, the meta transaction relayer. #### GitHub [https://github.com/snapshot-labs/sx-monorepo/tree/master/packages/sx.js](https://github.com/snapshot-labs/sx-monorepo/tree/master/packages/sx.js) ## Quick start ### Installation Install the latest version of the beta release: ```bash theme={null} npm install @snapshot-labs/sx@beta ``` ```bash theme={null} yarn add @snapshot-labs/sx@beta ``` ```bash theme={null} bun add @snapshot-labs/sx@beta ``` ### Configuration #### Clients Everything happens thanks to the `clients` objects. Depending on the specific Space or Proposal setup, you may need to use a Transaction or Signature Client. This quick guide demonstrates how to easily set all of them up, both for Ethereum and StarkNet: ```typescript theme={null} import { clients, evmSepolia } from '@snapshot-labs/sx'; const clientConfig = { networkConfig: evmSepolia } const client = new clients.EthereumTx(clientConfig); const ethSigClient = new clients.EthereumSig(clientConfig); ``` ```typescript theme={null} import { clients } from '@snapshot-labs/sx'; import { Provider, constants } from 'starknet'; const ethUrl = 'https://rpc.snapshot.org/1'; const manaUrl = 'https://mana.pizza'; const starkProvider = new Provider({ sequencer: { network: constants.NetworkName.SN_SEPOLIA } }); const clientConfig = { starkProvider, manaUrl, ethUrl }; const client = new clients.StarkNetTx(clientConfig); const starkSigClient = new clients.StarkNetSig(clientConfig); ``` To learn more about StarkNet's `Provider` object, have a look [here](https://www.starknetjs.com/docs/guides/connect_network). ## Usage Make sure to use the right client for your use case. The examples below use Sepolia network and its config is imported from the `sx.js` package. ### Cast a vote ```typescript theme={null} import { clients, evmSepolia } from '@snapshot-labs/sx'; import type { Web3Provider } from '@ethersproject/providers'; const clientConfig = { networkConfig: evmSepolia } const client = new clients.EthereumTx(clientConfig); const web3 = new Web3Provider(window.ethereum); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', strategies: [ { address: '0xc1245c5dca7885c73e32294140f1e5d30688c202', index: 0 } ], proposal: 7, choice: 1, metadataUri: '' }; const receipt = await client.vote({ signer: web3.getSigner(), envelope: { data } }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients, evmSepolia } from '@snapshot-labs/sx'; import type { Web3Provider } from '@ethersproject/providers'; const clientConfig = { networkConfig: evmSepolia } const ethSigClient = new clients.EthereumSig(clientConfig); const web3 = new Web3Provider(window.ethereum); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', strategies: [ { address: '0xc1245c5dca7885c73e32294140f1e5d30688c202', index: 0 } ], proposal: 7, choice: 1, metadataUri: '' }; const receipt = await ethSigClient.vote({ signer: web3.getSigner(), data }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients } from '@snapshot-labs/sx'; import { Provider, constants } from 'starknet'; const web3 = window.starknet.provider; const ethUrl = 'https://rpc.snapshot.org/1'; const manaUrl = 'https://mana.pizza'; const starkProvider = new Provider({ sequencer: { network: constants.NetworkName.SN_SEPOLIA } }); const clientConfig = { starkProvider, manaUrl, ethUrl }; const client = new clients.StarkNetTx(clientConfig); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', strategies: [ { address: '0xc1245c5dca7885c73e32294140f1e5d30688c202', index: 0 } ], proposal: 7, choice: 1, metadataUri: '' }; const receipt = await client.vote(web3.provider.account, { data }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients } from '@snapshot-labs/sx'; import { Provider, constants } from 'starknet'; const web3 = window.starknet.provider; const ethUrl = 'https://rpc.snapshot.org/1'; const manaUrl = 'https://mana.pizza'; const starkProvider = new Provider({ sequencer: { network: constants.NetworkName.SN_SEPOLIA } }); const clientConfig = { starkProvider, manaUrl, ethUrl }; const client = new clients.StarkNetSig(clientConfig); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', strategies: [ { address: '0xc1245c5dca7885c73e32294140f1e5d30688c202', index: 0 } ], proposal: 7, choice: 1, metadataUri: '' }; const envelope = await client.vote({ signer: web3.provider.account, data }); const receipt = await client.send(envelope); console.log('Receipt', receipt); ``` ### Create a proposal ```typescript theme={null} import { clients, evmSepolia } from '@snapshot-labs/sx'; import type { Web3Provider } from '@ethersproject/providers'; const clientConfig = { networkConfig: evmSepolia } const client = new clients.EthereumTx(clientConfig); const web3 = new Web3Provider(window.ethereum); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', strategies: [ { address: '0xc1245c5dca7885c73e32294140f1e5d30688c202', index: 0 } ], executionStrategy: { addr: '0x0000000000000000000000000000000000000000', params: '0x' }, metadataUri: '' }; const receipt = await client.propose({ signer: web3.getSigner(), envelope: { data } }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients, evmSepolia } from '@snapshot-labs/sx'; import type { Web3Provider } from '@ethersproject/providers'; const clientConfig = { networkConfig: evmSepolia } const client = new clients.EthereumSig(clientConfig); const web3 = new Web3Provider(window.ethereum); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', strategies: [ { address: '0xc1245c5dca7885c73e32294140f1e5d30688c202', index: 0 } ], executionStrategy: { addr: '0x0000000000000000000000000000000000000000', params: '0x' }, metadataUri: '' }; const receipt = await ethSigClient.propose({ signer: web3.getSigner(), data }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients } from '@snapshot-labs/sx'; import { Provider, constants } from 'starknet'; const web3 = window.starknet.provider; const ethUrl = 'https://rpc.snapshot.org/1'; const manaUrl = 'https://mana.pizza'; const starkProvider = new Provider({ sequencer: { network: constants.NetworkName.SN_SEPOLIA } }); const clientConfig = { starkProvider, manaUrl, ethUrl }; const client = new clients.StarkNetTx(clientConfig); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', strategies: [ { address: '0xc1245c5dca7885c73e32294140f1e5d30688c202', index: 0 } ], executionStrategy: { addr: '0x0000000000000000000000000000000000000000', params: '0x' }, metadataUri: '' }; const receipt = await client.propose(web3.provider.account, { data }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients } from '@snapshot-labs/sx'; import { Provider, constants } from 'starknet'; const web3 = window.starknet.provider; const ethUrl = 'https://rpc.snapshot.org/1'; const manaUrl = 'https://mana.pizza'; const starkProvider = new Provider({ sequencer: { network: constants.NetworkName.SN_SEPOLIA } }); const clientConfig = { starkProvider, manaUrl, ethUrl }; const client = new clients.StarkNetSig(clientConfig); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', strategies: [ { address: '0xc1245c5dca7885c73e32294140f1e5d30688c202', index: 0 } ], executionStrategy: { addr: '0x0000000000000000000000000000000000000000', params: '0x' }, metadataUri: '' }; const envelope = await client.propose({ signer: web3.provider.account, data }); const receipt = await client.send(envelope); console.log('Receipt', receipt); ``` ### Update a proposal ```typescript theme={null} import { clients, evmSepolia } from '@snapshot-labs/sx'; import type { Web3Provider } from '@ethersproject/providers'; const clientConfig = { networkConfig: evmSepolia } const client = new clients.EthereumTx(clientConfig); const web3 = new Web3Provider(window.ethereum); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', proposal: 1, // proposalId authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', executionStrategy: { addr: '0x0000000000000000000000000000000000000000', params: '0x' }, metadataUri: '' }; const receipt = await client.updateProposal({ signer: web3.getSigner(), envelope: { data } }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients, evmSepolia } from '@snapshot-labs/sx'; import type { Web3Provider } from '@ethersproject/providers'; const clientConfig = { networkConfig: evmSepolia } const client = new clients.EthereumSig(clientConfig); const web3 = new Web3Provider(window.ethereum); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', proposal: 1, // proposalId authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', executionStrategy: { addr: '0x0000000000000000000000000000000000000000', params: '0x' }, metadataUri: '' }; const receipt = await ethSigClient.updateProposal({ signer: web3.getSigner(), envelope: { data } }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients } from '@snapshot-labs/sx'; import { Provider, constants } from 'starknet'; const web3 = window.starknet.provider; const ethUrl = 'https://rpc.snapshot.org/1'; const manaUrl = 'https://mana.pizza'; const starkProvider = new Provider({ sequencer: { network: constants.NetworkName.SN_SEPOLIA } }); const clientConfig = { starkProvider, manaUrl, ethUrl }; const client = new clients.StarkNetTx(clientConfig); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', proposal: 1, // proposalId authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', executionStrategy: { addr: '0x0000000000000000000000000000000000000000', params: '0x' }, metadataUri: '' }; const receipt = await client.updateProposal(web3.provider.account, { data }); console.log('Receipt', receipt); ``` ```typescript theme={null} import { clients } from '@snapshot-labs/sx'; import { Provider, constants } from 'starknet'; const web3 = window.starknet.provider; const ethUrl = 'https://rpc.snapshot.org/1'; const manaUrl = 'https://mana.pizza'; const starkProvider = new Provider({ sequencer: { network: constants.NetworkName.SN_SEPOLIA } }); const clientConfig = { starkProvider, manaUrl, ethUrl }; const client = new clients.StarkNetSig(clientConfig); const data = { space: '0x012b261effbf548f2b9a495d50b81a8a7c1dd941', proposal: 1, // proposalId authenticator: '0xba06e6ccb877c332181a6867c05c8b746a21aed1', executionStrategy: { addr: '0x0000000000000000000000000000000000000000', params: '0x' }, metadataUri: '' }; const envelope = await client.updateProposal({ signer: web3.provider.account, data }); const receipt = await client.send(envelope); console.log('Receipt', receipt); ``` ### Get voting power Address' voting power depends on the strategy used. Below you can see an example of getting voting power for whitelist Voting strategy on Starknet. ```typescript theme={null} import { starknetSepolia1, getStarknetStrategy, } from '@snapshot-labs/sx'; const clientConfig = { starkProvider, ethUrl, networkConfig: starknetSepolia1 }; const address = '0xe3ca14dcb7862116bbbe4331a9927c6693b141aa8936bb76e2bdfa4b551a52'; const voterAddress = '0x556B14CbdA79A36dC33FcD461a04A5BCb5dC2A70'; const strategyMetadata = { "tree": [ { "type": 1, "address": "0x556B14CbdA79A36dC33FcD461a04A5BCb5dC2A70", "votingPower": "10" } ] } const timestamp = 1703090187 const strategyParams = [ "0xe3ca14dcb7862116bbbe4331a9927c6693b141aa8936bb76e2bdfa4b551a52" ] const strategy = getStarknetStrategy(address, clientConfig.networkConfig); const vp = await strategy.getVotingPower( address, voterAddress, strategyMetadata, timestamp, strategyParams, clientConfig ); ``` More details coming soon. # UI Source: https://docs.snapshot.box/snapshot-x/services/ui The UI is the official interface for Snapshot X. #### GitHub [https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/ui](https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/ui) #### UI [https://snapshot.box](https://snapshot.box/) # Create a space Source: https://docs.snapshot.box/snapshot-x/user-guides/create-a-space Learn how to create a new space for your organization. To create a new space on Snapshot X: * For mainnet networks, visit: [snapshot.box/#/create/snapshot-x](https://snapshot.box/#/create/snapshot-x) * For testnet networks, visit: [testnet.snapshot.box/#/create/snapshot-x](https://testnet.snapshot.box/#/create/snapshot-x) Follow the steps below: ## 1. Organization's details Provide the profile information like `name`, `description`, social links or handles and `treasury`. In order to set your Treasury select the network first. **Treasury** is the address of your organisation's main account. ## 2. Select network Choose the network for your organisation. At the moment Snapshot X works with Ethereum. Starknet implementation is coming soon. ## 3. Voting strategies Voting Strategies calculate the **Voting Power** of each user. If you are not familiar with what a Voting Strategy is, head to [voting strategies](/snapshot-x/protocol/voting-strategies) to learn more. Select a Voting Strategy from the list of available options and provide the required details. For example for the **Delegated Compound Token** you have to provide the token contract address, its decimals and the symbol: ## 4. Authenticators Choose how users will be authenticated in order to create a proposal or cast a vote. If you are not familiar with what Authenticators are, head to [authenticators](/snapshot-x/protocol/authenticators) to learn more. The two authenticators provided by Snapshot X are: #### Ethereum signature It will authenticate a user based on a message signed by an Ethereum private key. #### Ethereum transaction Users have to submit a transaction on Ethereum. Authentication is proving that the sender's address is valid. ## 5. Proposal validation Proposal Validation is setting **requirements** that user needs to meet in order to **create a new proposal.** If you are not familiar with what a Proposal Validation is, head to [proposal validations](/snapshot-x/protocol/proposal-validations) to learn more. Define the minimum Voting Power required to create a new proposal. We highly recommend setting a high threshold to avoid scam proposals. Once you have defined the required Voting Power, you can then select the Voting Strategies which will be used to calculate the voting power of proposal creators and assess the eligibility to create a new one: ## 6. Execution Execution Strategies have two key roles: 1. determining **the status** of a proposal at any time, 2. and **executing the payload** of a proposal if it has been accepted. If you are not familiar with what Execution Strategies are, head to [execution strategies](/snapshot-x/protocol/execution-strategies) to learn more. When selecting the execution strategy you have to provide a **quorum**, the minimum number of votes required for a proposal to pass. ### Execution strategies Currently Snapshot X provides two Execution Strategies: #### Avatar -> ideal solution for treasuries on [Safe](https://safe.global/) -> executes transactions on an [Avatar contract](https://github.com/gnosis/zodiac/blob/master/contracts/interfaces/IAvatar.sol) -> uses simple quorum Avatar contract is any contract which implements the `IAvatar` interface. **Safe** is an example of an Avatar contract. To set the Avatar Execution Strategy up fully you have to enable it on your Safe account after you have created your space. **Follow this guide to complete the setup:** [Safe execution setup](/snapshot-x/user-guides/safe-execution-setup) #### Timelock -> adds additional **security layer** to review or cancel transactions before they get executed -> executes transactions after a delay specified in **seconds** -> uses simple quorum When a proposal with this strategy is executed, the proposal transactions are queued in the Timelock for `timelockDelay` seconds before they can be executed. ### 7. Voting Customize the setup for voting which will affect **all proposals** in your space: * **Voting delay:** The delay between when a proposal is created, and when the voting starts. A value of 0 means that as soon as the proposal is created anyone can vote while a value of 3600 means that voting will start 3600 seconds after the proposal is created. * **Minimum voting duration:** The minimum duration of the voting period. It is **impossible to execute a proposal before** **this period** has elapsed. * **Maximum voting duration:** The maximum duration of the voting period, it is **impossible to cast a vote after this period** has passed. The **`minimum voting duration`** must be less than or equal to this value. We highly recommend implementing a Voting Delay to allow more time to review proposal's content and identify malicious proposals before voting starts. ### 7. Controller Space controller is a user that has a **full control over the space settings.** The address can differ from your own, you can for example set a multisig account as the controller. ### 8. Sign transaction(s) Click **Create** and sign transaction(s) in your wallet. You will have to sign multiple transactions, each for individual contracts to be deployed: * Space contract * Execution strategy contracts - each space has its execution strategy deployed as an individual contract Your space is now created. # Gasless voting Source: https://docs.snapshot.box/snapshot-x/user-guides/gasless-voting To enable gasless voting and proposals on a Snapshot X space, first add the **Ethereum signature authenticator** (or the **Starknet signature authenticator** if your space uses Starknet). When you create a Snapshot X space, a dedicated wallet is automatically generated to facilitate gasless interactions. You can find the address of this dedicated wallet in the **Authenticator** page within your space settings. To activate gasless voting and proposal submissions, simply top up this wallet with ETH (or the relevant gas token). If the wallet balance drops below **0.01 ETH**, the gasless functionality will be disabled. ## What is Mana? Mana is a meta-transaction relayer that enables gasless governance on Snapshot X. Instead of requiring users to pay gas fees for proposals and votes, Mana sponsors those fees by accepting signed messages and broadcasting them onchain. This eliminates the need for users to hold or spend ETH for governance actions, making participation more accessible. The source code is available at [github.com/snapshot-labs/sx-monorepo/tree/master/apps/mana](https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/mana). # Proposals Source: https://docs.snapshot.box/snapshot-x/user-guides/proposals Learn what a proposal is and how to create one. ## What is a proposal? Proposal is the key element of the voting system. It presents a change suggestion related to a specific organization and enables eligible users to cast their vote. Voting power for each user is calculated on the basis of the voting strategies selected in the space settings. Each proposal has the same voting system which provides three choices: **accept**, **reject**, **abstain**. ## Who can create a proposal? Users who: * authenticate themselves via Authenticators whitelisted by the space, * and are eligible according to the [Proposal validation strategy](#user-content-fn-2) selected by the space. ## Create a proposal ### 1. Open space settings. Head to the space which you wish to create your proposal for. Make sure the connected wallet is **where you hold the tokens relevant** to the specific space. ### 2. Click the New proposal icon Click the new proposal icon in the top right corner: ### 3. Provide proposal details Provide the necessary details - title, description and discussion link if there is one. ### 4. Choose the execution strategy Select the [Execution strategy](#user-content-fn-3) you would like to use: ### 5. Specify transaction details Define what should happen if the proposal passes by choosing one of the execution options: A modal will open with the **transaction details** to fill in: ### 6. Publish your proposal Click `Publish` and authenticate yourself via your wallet. Depending on the space settings you will have to sign a gasless Ethereum message and/or sign a transaction to confirm your action. When you create a proposal by default the **timestamp** will be populated with the latest block synced from our node. The voting power of each user will be calculated for the **state of the blockchain at this specific timestamp**. It means that if user acquires required tokens **after** the proposal has been created, they will not be taken into account for their voting power calculation. ### 7. Give it a minute.. Wait for the transaction to succeed. You can see it's pending in the top right corner, just next to your avatar: Once the icon with the pending transaction disappears, you can reload the page to view your proposal. Your proposal has been created. ### Executing proposals Once the proposal has reached a quorum and passed, anyone can execute the transactions specified for it by clicking the **Execute transactions** button: # Safe execution setup Source: https://docs.snapshot.box/snapshot-x/user-guides/safe-execution-setup If you added the Safe execution strategy to your space you have to enable it on your Safe account after creating the space. ## 1. Copy the address of your Safe execution Head to your space settings and scroll down to the bottom of the page and click the Safe address on the right side of the screen. A new tab will open on the Etherscan explorer. **Copy the address of the contract:** ## 2. Open Safe dashboard of your Treasury account Click the `App` tab in the sidebar to open the list of available Safe applications. ## 3. Select the Zodiac app ## 4. Add your Safe execution strategy contract Click on `Custom Module.` Paste the contract's address you copied in the first step in the `Module Address` field. As a last step you will have to sign a transaction. Your proposals can now be executed. # Storage proofs Source: https://docs.snapshot.box/snapshot-x/user-guides/storage-proofs Storage proofs are a way to verify data from one blockchain on another blockchain without accessing the entire state. Snapshot X collaborates with Herodotus to integrate storage proofs into its voting strategies on Starknet. This integration allows Snapshot X to calculate voting power based on data from Ethereum mainnet or Sepolia testnet contracts. Here's how it works: 1. A block hash from Ethereum (L1) is sent to Starknet (L2) 2. Using this block hash, the state of any contract from L1 can be verified on L2 at that specific block 3. The voting strategy accesses and verifies data (like token balances) from the Ethereum contract 4. Voting power is then calculated using the verified data This approach enables secure cross-chain governance, combining Ethereum's security with Starknet's scalability. It allows projects to use Ethereum data for voting on Starknet efficiently and securely. Currently, this feature is only available on Starknet. ### Learn more * [Herodotus: Proving Ethereum's State Using Storage Proofs on Starknet](https://www.herodotus.dev/) * [Storage Proofs | Herodotus Docs](https://docs.herodotus.dev/) # Voting Source: https://docs.snapshot.box/snapshot-x/user-guides/voting Learn how you can cast a vote on a proposal. ## Who can vote on proposals? There are several aspects that define if you are eligible to vote on a specific proposal. ### Voting strategies Each space specifies their [Voting strategies](/snapshot-x/protocol/voting-strategies) in its settings. You can see the custom setup by opening the space settings. This setup can define if you are eligible to take part in the voting and what is your Voting Power calculated at the timestamp of proposal creation. One of the most common questions we receive on our support channels is **Why can't I vote?** More often than not the answer is - **you did not hold the sufficient amount of specified token at the time of proposal creation.** ## Cast a vote ### 1. Connect your wallet Click the `Connect wallet` button in the top right corner. Connect with the wallet provider where you **hold the tokens relevant for the space** you want to vote in. ### 2. Find the proposal Go to the space page on Snapshot. You can vote directly from this view or go to the proposal you are interested in to read more details before you vote. In the proposal page you can see your Voting Power. If it shows `0` it means you cannot vote on the selected proposal. ### 3. Vote! Select the option you want to vote for - **Accept, Reject, Abstain**. Depending on the space settings you will have to sign a gasless Ethereum message and/or sign a transaction to confirm your action. If you are using MetaMask you'll need to scroll to the end of the signature and click on the arrow down for the Sign button to become active. Voting on Snapshot doesn't affect your account or the funds that are associated to it. You will notice that a new icon has appeared in the top right corner, just next to your avatar: The number indicates the number of pending transactions. Once it disappeared you can reload to page to view your vote. You have successfully cast your vote. # Voting power Source: https://docs.snapshot.box/snapshot-x/user-guides/voting-power ## Why do I have 0 voting power? There are a few reasons why you might have 0 voting power on Snapshot: * **No required assets:** You might not hold the necessary assets or meet the criteria based on the voting strategies used by the space. Each space can have different requirements. * **Delegation required:** For some tokens (like those using ERC-20 Votes), you need to delegate your tokens to gain voting power. * **Proposal start requirements:** Voting power is determined at the time a proposal starts. If you didn't meet the requirements when the proposal started, you won't have voting power for that proposal. You can check your current voting power on the space's proposals page to see if you meet the criteria. ## Why do I need to wait for my voting power to be collected from Ethereum? If you see the message "Please allow a few minutes for the voting power to be collected from Ethereum," it means your space uses a voting strategy that requires data from another blockchain. This process takes about 10 to 15 minutes. During this time, the state is sent from the other chain to the current space chain, where your voting power is verified using storage proofs. Please be patient, and you'll soon be able to see your voting power and cast your vote. # Voting strategies Source: https://docs.snapshot.box/snapshot-x/user-guides/voting-strategies-sx Snapshot X provides several onchain voting strategies for calculating voting power. ## Whitelist The Whitelist strategy assigns specific voting power to chosen addresses. It uses a format of `address:voting_power` and a Merkle tree for onchain verification. Only the tree's root is stored onchain. During voting, users prove their inclusion in the list using Merkle proofs. This approach saves gas, allows easy updates, and provides precise control over voting power distribution. Example format: ``` 0x1234567890123456789012345678901234567890:100 0xabcdefabcdefabcdefabcdefabcdefabcdefabcd:50 0x9876543210987654321098765432109876543210:75 ``` ## ERC-20 Votes (EIP-5805) The ERC-20 Votes strategy assigns voting power based on delegated token balances. It works with ERC-20 or ERC-721 tokens that implement the EIP-5805 Votes extension from OpenZeppelin. This strategy allows token holders to participate in governance by delegating their voting power, either to themselves or to other addresses. ## EVM slot value The EVM slot value strategy uses values stored in specific slots on an EVM chain as voting power. It retrieves data such as ERC-20 token balances or ERC-721 token ownership from Ethereum mainnet or Sepolia testnet and leverages storage proofs to verify data integrity on layer 2. Currently, this strategy is only available on Starknet. To set up this strategy: 1. Add the Ethereum mainnet or Sepolia testnet contract address 2. Find the storage slot where balances are stored using [storage.herodotus.dev](https://storage.herodotus.dev) 3. Enter the contract address and select the network 4. Locate the index where the balances are stored For example, for the ApeCoin (APE) ERC-20 token, the balances are stored in slot index 0. ## Vanilla The Vanilla strategy is a simple voting strategy used for testing purposes, available only on testnets. It assigns 1 voting power to each participant, regardless of their token holdings. ## OZ Votes storage proof The OZ Votes storage proof strategy uses delegated balances stored in specific slots on an EVM chain as voting power. This strategy focuses on verifying delegated voting power for tokens using ERC-20 Votes or ERC-721 Votes, utilizing the trace 224 or 208 format. It leverages storage proofs to ensure data integrity on layer 2. Currently, this strategy is only available on Starknet. To set up this strategy: 1. Add the Ethereum mainnet or Sepolia testnet contract address 2. Find the storage slot where delegated balances are stored using [storage.herodotus.dev](https://storage.herodotus.dev) 3. Enter the contract address and select the network 4. Locate the index where the delegated balances are stored For example, for the ENS ERC-20 token, the delegated balances are stored in slot index 7. If you need help setting up any voting strategy, contact our support team through the [Help center](/faq/support-and-feedback). # API Source: https://docs.snapshot.box/tools/api You can use the Hub GraphQL API to create flexible queries for the data you need to integrate with Snapshot. There is a limit of 60 requests per minute with the API, to get higher limits please apply for an API Key by following this guide: [api-keys](/tools/api/api-keys) ## Hub GraphQL API: Explorer You can run queries on Snapshot data using a GraphQL Explorer. We have exposed an integrated development environment in the browser that includes docs, syntax highlighting, and validation errors. Click the link below to access the interface. [https://hub.snapshot.org/graphql](https://hub.snapshot.org/graphql) ## Endpoints Production hub ``` https://hub.snapshot.org/graphql ``` Demo hub ``` https://testnet.hub.snapshot.org/graphql ``` ## Queries ### Get a single space #### Arguments id `string` #### Example ```graphql theme={null} query { space(id: "yam.eth") { id name about network symbol members } } ``` ```javascript theme={null} { "data": { "space": { "id": "yam.eth", "name": "Yam Finance", "about": "", "network": "1", "symbol": "YAM", "members": [ "0x683A78bA1f6b25E29fbBC9Cd1BFA29A51520De84", "0x9Ebc8AD4011C7f559743Eb25705CCF5A9B58D0bc", "0xC3edCBe0F93a6258c3933e86fFaA3bcF12F8D695", "0xbdac5657eDd13F47C3DD924eAa36Cf1Ec49672cc", "0xEC3281124d4c2FCA8A88e3076C1E7749CfEcb7F2" ] } } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?query=query%20%7B%0A%20%20space\(id%3A%20%22yam.eth%22\)%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%20%20about%0A%20%20%20%20network%0A%20%20%20%20symbol%0A%20%20%20%20members%0A%20%20%7D%0A%7D) ### Get multiple spaces #### Arguments first `number`\ skip `number`\ where:\ \- id`string`\ \- id\_in`array`\ orderBy `string`\ orderDirection `asc` or `desc` #### Example ```graphql theme={null} query { spaces( first: 20, skip: 0, orderBy: "created", orderDirection: asc ) { id name about network symbol strategies { name params } admins members filters { minScore onlyMembers } plugins } } ``` ```javascript theme={null} { "data": { "spaces": [ { "id": "bonustrack.eth", "name": "Fabien", "about": "", "network": "1", "symbol": "TICKET", "strategies": [ { "name": "erc20-balance-of", "params": { "symbol": "DAI", "address": "0x6B175474E89094C44Da98b954EedeAC495271d0F", "decimals": 18 } } ], "admins": [], "members": [ "0x24A12Fa313F57aF541d447c594072A992c605DCf" ], "filters": { "minScore": 0, "onlyMembers": false }, "plugins": { "quorum": { "total": 500, "strategy": "static" } } } ] } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?query=%0Aquery%20Spaces%20%7B%0A%20%20spaces\(%0A%20%20%20%20first%3A%2020%2C%0A%20%20%20%20skip%3A%200%2C%0A%20%20%20%20orderBy%3A%20%22created%22%2C%0A%20%20%20%20orderDirection%3A%20asc%0A%20%20\)%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%20%20about%0A%20%20%20%20network%0A%20%20%20%20symbol%0A%20%20%20%20strategies%20%7B%0A%20%20%20%20%20%20name%0A%20%20%20%20%20%20params%0A%20%20%20%20%7D%0A%20%20%20%20admins%0A%20%20%20%20members%0A%20%20%20%20filters%20%7B%0A%20%20%20%20%20%20minScore%0A%20%20%20%20%20%20onlyMembers%0A%20%20%20%20%7D%0A%20%20%20%20plugins%0A%20%20%7D%0A%7D) ### Get a single proposal #### Arguments id `string`‌ #### Example ```graphql theme={null} query { proposal(id:"QmWbpCtwdLzxuLKnMW4Vv4MPFd2pdPX71YBKPasfZxqLUS") { id title body choices start end snapshot state author created scores scores_by_strategy scores_total scores_updated plugins network strategies { name network params } space { id name } } } ``` ```javascript theme={null} { "data": { "proposal": { "id": "QmWbpCtwdLzxuLKnMW4Vv4MPFd2pdPX71YBKPasfZxqLUS", "title": "Select Initial Umbrella Metapool", "body": "Eventually, we hope that anyone will be able to create a metapool and fund a protection market for their project, but right now we want to start small and pick one pool that we will debut as a beta launch for Umbrella that will help us gather information and insight into the state of the market. In the future we can have all of these and more. Here are the choices:\n### Option 1: BlueChips MetaPool\n\nYou might consider this the safest of the pools. It contains a collection of different “blue-chip projects” across multiple verticals that have proven track records and are considered industry leaders. These include:\n\n* (3) Bluechip protocols: MakerDAO, Compound, and Uniswap. These are commonly seen as the most battletested and trusted DeFi projects on Ethereum.\n* (2) Centralized exchanges: Coinbase and Binance. These are the most popular and generally considered to be most reputable exchanges around. *note: Payout occurs only if Safu funds or the exchange’s insurance do not cover losses.\n* (2) Hardware Wallet companies, Ledger and Trezor, including the Ledger Nano S and X, and the Trezor Model T and One. This would cover large scale exploits in their hardware or firmware and would not cover individual loss due to phishing or poor security.\n\n### Option 2: Hot New Projects MetaPool\n\nThis pool targets newer projects on Ethereum that are considered reputable and have high TVL but are less battle tested and therefore may be more risky. While they may be more risky, this may mean that there is more demand for coverage for them in the market. This list is preliminary but internal discussions considered including:\n\n * Alchemix\n* OHM\n* Liquity\n* FEI\n* Integral\n* Reflexer\n\n### Option 3: Integrated DegenV2 MetaPool\n\nThis last option focuses more closely on YAM products, specifically DegenV2 and the constituent protocols that it uses. This option would let us insure our own users and potentially test out our products in a more limited environment. The covered protocols would be:\n\n * UMA\n * Sushiswap/Uniswap depending on where our pools live\n * Any YAM contracts that are used\n * Any future contracts included in future versions of Degen.\n\n### Choose wisely!\n", "choices": [ "Option 1: BlueChips MetaPool", "Option 2: Hot New Projects MetaP", "Option 3: Integrated DegenV2 Met" ], "start": 1620676800, "end": 1620806400, "snapshot": "12408670", "state": "closed", "author": "0xEC3281124d4c2FCA8A88e3076C1E7749CfEcb7F2", "space": { "id": "yam.eth", "name": "Yam Finance" } } } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?operationName=Proposal\&query=query%20Proposal%20%7B%0A%20%20proposal\(id%3A%22QmWbpCtwdLzxuLKnMW4Vv4MPFd2pdPX71YBKPasfZxqLUS%22\)%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%20%20body%0A%20%20%20%20choices%0A%20%20%20%20start%0A%20%20%20%20end%0A%20%20%20%20snapshot%0A%20%20%20%20state%0A%20%20%20%20author%0A%20%20%20%20space%20%7B%0A%20%20%20%20%20%20id%0A%20%20%20%20%20%20name%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D) ### Get proposals #### Arguments first `number`\ skip `number`\ where:\ \- id`string`\ \- id\_in`array`\ \- space:`string`\ \- space\_in:`array`\ \- author:`string`\ \- author\_in:`array`\ \- network: `string`\ \- network\_in: `array`\ \- state: `array`\ orderBy `string`\ orderDirection `asc` or `desc` #### Example ```graphql theme={null} query { proposals ( first: 20, skip: 0, where: { space_in: ["yam.eth"], state: "closed" }, orderBy: "created", orderDirection: desc ) { id title body choices start end snapshot state scores scores_by_strategy scores_total scores_updated author space { id name } } } ``` ```javascript theme={null} { "data": { "proposals": [ { "id": "QmWbpCtwdLzxuLKnMW4Vv4MPFd2pdPX71YBKPasfZxqLUS", "title": "Select Initial Umbrella Metapool", "body": "Eventually, we hope that anyone will be able to create a metapool and fund a protection market for their project, but right now we want to start small and pick one pool that we will debut as a beta launch for Umbrella that will help us gather information and insight into the state of the market. In the future we can have all of these and more. Here are the choices:\n### Option 1: BlueChips MetaPool\n\nYou might consider this the safest of the pools. It contains a collection of different “blue-chip projects” across multiple verticals that have proven track records and are considered industry leaders. These include:\n\n* (3) Bluechip protocols: MakerDAO, Compound, and Uniswap. These are commonly seen as the most battletested and trusted DeFi projects on Ethereum.\n* (2) Centralized exchanges: Coinbase and Binance. These are the most popular and generally considered to be most reputable exchanges around. *note: Payout occurs only if Safu funds or the exchange’s insurance do not cover losses.\n* (2) Hardware Wallet companies, Ledger and Trezor, including the Ledger Nano S and X, and the Trezor Model T and One. This would cover large scale exploits in their hardware or firmware and would not cover individual loss due to phishing or poor security.\n\n### Option 2: Hot New Projects MetaPool\n\nThis pool targets newer projects on Ethereum that are considered reputable and have high TVL but are less battle tested and therefore may be more risky. While they may be more risky, this may mean that there is more demand for coverage for them in the market. This list is preliminary but internal discussions considered including:\n\n * Alchemix\n* OHM\n* Liquity\n* FEI\n* Integral\n* Reflexer\n\n### Option 3: Integrated DegenV2 MetaPool\n\nThis last option focuses more closely on YAM products, specifically DegenV2 and the constituent protocols that it uses. This option would let us insure our own users and potentially test out our products in a more limited environment. The covered protocols would be:\n\n * UMA\n * Sushiswap/Uniswap depending on where our pools live\n * Any YAM contracts that are used\n * Any future contracts included in future versions of Degen.\n\n### Choose wisely!\n", "choices": [ "Option 1: BlueChips MetaPool", "Option 2: Hot New Projects MetaP", "Option 3: Integrated DegenV2 Met" ], "start": 1620676800, "end": 1620806400, "snapshot": "12408670", "state": "closed", "author": "0xEC3281124d4c2FCA8A88e3076C1E7749CfEcb7F2", "space": { "id": "yam.eth", "name": "Yam Finance" } }, ... ] } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?operationName=Proposals\&query=query%20Proposals%20%7B%0A%20%20proposals%20\(%0A%20%20%20%20first%3A%2020%2C%0A%20%20%20%20skip%3A%200%2C%0A%20%20%20%20where%3A%20%7B%0A%20%20%20%20%20%20space_in%3A%20%5B%22yam.eth%22%5D%2C%0A%20%20%20%20%20%20state%3A%20%22closed%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20orderBy%3A%20%22created%22%2C%0A%20%20%20%20orderDirection%3A%20desc%0A%20%20\)%20%7B%0A%20%20%20%20id%0A%20%20%20%20title%0A%20%20%20%20body%0A%20%20%20%20choices%0A%20%20%20%20start%0A%20%20%20%20end%0A%20%20%20%20snapshot%0A%20%20%20%20state%0A%20%20%20%20author%0A%20%20%20%20space%20%7B%0A%20%20%20%20%20%20id%0A%20%20%20%20%20%20name%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D) ### Get a single vote Choices are indexed 1-based. The first choice has index 1. #### Arguments id `string`‌ #### Example ```graphql theme={null} query { vote ( id: "QmeU7ct9Y4KLrh6F6mbT1eJNMkeQKMSnSujEfMCfbRLCMp" ) { id voter vp vp_by_strategy vp_state created proposal { id } choice space { id } } } ``` ```javascript theme={null} { "data": { "vote": { "id": "QmeU7ct9Y4KLrh6F6mbT1eJNMkeQKMSnSujEfMCfbRLCMp", "voter": "0x96176C25803Ce4cF046aa74895646D8514Ea1611", "created": 1621183227, "proposal": { "id": "QmPvbwguLfcVryzBRrbY4Pb9bCtxURagdv1XjhtFLf3wHj" }, "choice": 1, "space": { "id": "spookyswap.eth" } } } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?operationName=Vote\&query=query%20Vote%20%7B%0A%20%20vote%20\(%0A%20%20%20%20id%3A%20%22QmeU7ct9Y4KLrh6F6mbT1eJNMkeQKMSnSujEfMCfbRLCMp%22%0A%20%20\)%20%7B%0A%20%20%20%20id%0A%20%20%20%20voter%0A%20%20%20%20created%0A%20%20%20%20proposal%20%7B%0A%20%20%20%20%20%20id%0A%20%20%20%20%7D%0A%20%20%20%20choice%0A%20%20%20%20space%20%7B%0A%20%20%20%20%20%20id%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D) ### Get votes Choices are indexed 1-based. The first choice has index 1. #### Arguments first `number`\ skip `number`\ where:\ \- id`string`\ \- id\_in`array`\ \- space:`string`\ \- space\_in:`array`\ \- voter:`string`\ \- voter\_in:`array`\ \- proposal: `string`\ \- proposal\_in: `array`\ orderBy `string`\ orderDirection `asc` or `desc` #### Example ```graphql theme={null} query { votes ( first: 1000 skip: 0 where: { proposal: "QmPvbwguLfcVryzBRrbY4Pb9bCtxURagdv1XjhtFLf3wHj" } orderBy: "created", orderDirection: desc ) { id voter vp vp_by_strategy vp_state created proposal { id } choice space { id } } } ``` ```javascript theme={null} { "data": { "votes": [ { "id": "QmeU7ct9Y4KLrh6F6mbT1eJNMkeQKMSnSujEfMCfbRLCMp", "voter": "0x96176C25803Ce4cF046aa74895646D8514Ea1611", "created": 1621183227, "proposal": { "id": "QmPvbwguLfcVryzBRrbY4Pb9bCtxURagdv1XjhtFLf3wHj" }, "choice": 1, "space": { "id": "spookyswap.eth" } }, { "id": "QmZ2CV86QH6Q6z7L6g7yJWS3HfgD9aQ3uTYYMXkMa5trHf", "voter": "0x2686EaD94C5042e56a41eDde6533711a4303CC52", "created": 1621181827, "proposal": { "id": "QmPvbwguLfcVryzBRrbY4Pb9bCtxURagdv1XjhtFLf3wHj" }, "choice": 1, "space": { "id": "spookyswap.eth" } }, ... ] } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?operationName=Votes\&query=query%20Votes%20%7B%0A%20%20votes%20\(%0A%20%20%20%20first%3A%201000%0A%20%20%20%20skip%3A%200%0A%20%20%20%20where%3A%20%7B%0A%20%20%20%20%20%20proposal%3A%20%22QmPvbwguLfcVryzBRrbY4Pb9bCtxURagdv1XjhtFLf3wHj%22%0A%20%20%20%20%7D%0A%20%20%20%20orderBy%3A%20%22created%22%2C%0A%20%20%20%20orderDirection%3A%20desc%0A%20%20\)%20%7B%0A%20%20%20%20id%0A%20%20%20%20voter%0A%20%20%20%20created%0A%20%20%20%20proposal%20%7B%0A%20%20%20%20%20%20id%0A%20%20%20%20%7D%0A%20%20%20%20choice%0A%20%20%20%20space%20%7B%0A%20%20%20%20%20%20id%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A) ### Get voting power #### Arguments voter `string`\ space `string`\ proposal `string` #### Example ```graphql theme={null} query { vp ( voter: "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7" space: "fabien.eth" proposal: "0x4903dd16990de740b7dc7effe1a0bc8bd49a510a04992bc30596c9a0d0f69455" ) { vp vp_by_strategy vp_state } } ``` ``` { "data": { "vp": { "vp": 1, "vp_by_strategy": [ 1 ], "vp_state": "final" } } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?query=query%20%7B%0A%20%20vp%20\(%0A%20%20%20%20voter%3A%20%220xeF8305E140ac520225DAf050e2f71d5fBcC543e7%22%0A%20%20%20%20space%3A%20%22fabien.eth%22%0A%20%20%20%20proposal%3A%20%220x4903dd16990de740b7dc7effe1a0bc8bd49a510a04992bc30596c9a0d0f69455%22%0A%20%20\)%20%7B%0A%20%20%20%20vp%0A%20%20%20%20vp_by_strategy%0A%20%20%20%20vp_state%0A%20%20%7D%20%0A%7D%0A) ### Get follows #### Arguments first `number`\ skip `number`\ where:\ \- id`string`\ \- id\_in`array`\ \- space:`string`\ \- space\_in:`array`\ \- follower:`string`\ \- follower\_in:`array`\ orderBy `string`\ orderDirection `asc` or `desc` #### Example ```graphql theme={null} query { follows( first: 10, where: { follower: "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7" } ) { follower space { id } created } } ``` ```javascript theme={null} { "data": { "follows": [ { "follower": "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", "space": { "id": "gnosis.eth" }, "created": 1629732280 }, { "follower": "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", "space": { "id": "aavegotchi.eth" }, "created": 1629725098 }, { "follower": "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", "space": { "id": "yam.eth" }, "created": 1629723970 }, { "follower": "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7", "space": { "id": "balancer.eth" }, "created": 1629723960 } ] } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?query=query%20%7B%0A%20%20follows\(%0A%20%20%20%20first%3A%2010%2C%0A%20%20%20%20where%3A%20%7B%0A%20%20%20%20%20%20follower%3A%20%220xeF8305E140ac520225DAf050e2f71d5fBcC543e7%22%0A%20%20%20%20%7D%0A%20%20\)%20%7B%0A%20%20%20%20follower%0A%20%20%20%20space%20%7B%0A%20%20%20%20%20%20id%0A%20%20%20%20%7D%0A%20%20%20%20created%0A%20%20%7D%0A%7D) ### Get users #### Arguments first `number`\ skip `number`\ where:\ \- id:`string`\ \- id\_in:`array`\ orderBy `string`\ orderDirection `asc` or `desc` #### Example ```graphql theme={null} query { users(first: 10, where: { id_in: ["0xF78108c9BBaF466dd96BE41be728Fe3220b37119"] }) { id name about avatar } } ``` ``` { "data": { "users": [ { "id": "0xF78108c9BBaF466dd96BE41be728Fe3220b37119", "name": "John Doe", "about": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Assumenda asperiores a quis accusamus tenetur sed", "avatar": "ipfs://QmNXTswsKJEHHEmGCgQKEAqbq3ib1eBCd4U8SRPgcuVJBX" } ] } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?query=query%20%7B%0A%20%20users\(first%3A%2010%2C%20where%3A%20%7B%20id_in%3A%20%5B%220xF78108c9BBaF466dd96BE41be728Fe3220b37119%22%5D%20%7D\)%20%7B%0A%20%20%20%20id%0A%20%20%20%20name%0A%20%20%20%20about%0A%20%20%20%20avatar%0A%20%20%7D%0A%7D%0A) ### Get roles #### Arguments first `number`\ skip `number`\ where:\ \- address:`string`\ orderBy `string`\ orderDirection `asc` or `desc` ```graphql theme={null} query { roles(where:{address:"0xE029Ef62e47E394BC852EFf633eB5aa4A223ECa6"}) { space permissions } } ``` ```graphql theme={null} { "data": { "roles": [ { "space": "fabien.eth", "permissions": [ "moderator" ] }, { "space": "zuzazuber.eth", "permissions": [ "moderator" ] } ] } } ``` Try on GraphiQL ### Aliases TBD ### Get messages Messages are all the actions (votes, proposals, space settings etc..) that was confirmed on Snapshot, it also include the order on which these actions were confirmed with the field "mci". These messages can be used to replay the whole Snapshot hub API. #### Arguments first `number`\ skip `number`\ where:\ \- timestamp`string`\ \- space`array`\ \- space\_in:`array`\ \- type:`string`\ \- type\_in:`string`\ orderBy `string`\ orderDirection `asc` or `desc` #### Example ```graphql theme={null} query { messages ( first: 20 where: { space: "ens.eth" } orderBy: "mci" orderDirection: desc ) { id address ipfs receipt type mci } } ``` ```javascript theme={null} { "data": { "messages": [ { "id": "0x7d99e11ffe3a333229bdcda59866bc5b66b0b7e5c4b5353862a3b8ccbfa26c83", "address": "0xdbB1740e424C41E935599634828f5E5c4dF23D43", "ipfs": "bafkreiduchkd35btpv3uttrg3f2kx3g52uh44tiruawip6xlgvjbcuo4w4", "receipt": "0xaa4d557085872ed82b8bfba5af4247650395eda46ed61672d940eabc74bcde67415db974361f4b24361565d2f9ee6ee636ae935a1dc830c207204ea29ad498741b", "type": "follow" "mci": 1345000 }, ... ] } } ``` Try on [GraphiQL](https://hub.snapshot.org/graphql?query=query%20%7B%0A%20%20messages%20\(%0A%20%20%20%20first%3A%2020%0A%20%20%20%20where%3A%20%7B%20space%3A%20%22ens.eth%22%20%7D%0A%20%20%20%20orderBy%3A%20%22timestamp%22%0A%20%20%20%20orderDirection%3A%20desc%0A%20%20\)%20%7B%0A%20%20%20%20id%0A%20%20%20%20address%0A%20%20%20%20ipfs%0A%20%20%20%20receipt%0A%20%20%20%20type%0A%20%20%7D%0A%7D%0A) # API keys Source: https://docs.snapshot.box/tools/api/api-keys To obtain higher rate limits on the Hub API use, apply for an API Key. We want to ensure that we limit the risk of API downtime and provide a reliable and continuous service. Therefore we decided to implement API Keys to ensure that the requests come from genuine users. You can use **the same API key** for different Snapshot APIs. Limits are **counted individually** per each API Service. ## Limits **🔓 No API Key:** 100 requests per minute. **🔑 With the API Key:** 2 million requests per month. If you are getting **close to 2M requests a month** contact our support on [Help center](/faq/support-and-feedback) ## How can I get an API Key? ### 1. Apply via the request form If you haven’t already please **fill in the below form** or submit it via [the direct link](https://tally.so/r/3laKWp). #### API Key Request Form [https://tally.so/r/3laKWp](https://tally.so/r/3laKWp) ### 2. Wait 72 hours We will review your submission and **whitelist the address** you provided in the form. Once whitelisted, you will **receive an email with your API key**. If you haven't received an email after 72 hours, please contact our support on [Help center](/faq/support-and-feedback). ## How to structure the query with my key? The only change you need to make is to add the API Key in the headers of your request: ```bash theme={null} curl 'https://hub.snapshot.org/graphql?' \ -H 'content-type: application/json' \ -H 'x-api-key: ' \ --data-raw '{"query":"\n{\n space(id:\"snapshot.dcl.eth\"){\n id\n name\n members\n}\n}","variables":null}' \ --compressed ``` Alternatively, you can use the `apiKey` param in the query string with your key as a value: ```bash theme={null} curl 'https://hub.snapshot.org/graphql?apiKey=' \ -H 'content-type: application/json' \ --data-raw '{"query":"\n{\n space(id:\"snapshot.dcl.eth\"){\n id\n name\n members\n}\n}","variables":null}' \ --compressed ``` If you are using the GraphQL interface you need to provide your key in the `headers` tab: # Bots Source: https://docs.snapshot.box/tools/bots Setup a bot to receive Snapshot notifications. Snapshot has several bot integrations which can be set up for your organization in order to receive Snapshot specific notifications. Have a look at the list below to see what is currently possible: ### Snapshot Discord bot **For:** Discord\ **Status:** Ready\ **Install:** [Invite the Discord bot](https://discord.com/oauth2/authorize?client_id=892847850780762122\&permissions=83968\&scope=bot) Type `/` to see the commands (require administrator role) Need help? Contact our support on [Help center](/faq/support-and-feedback). ### Domino **For:** Discord, Telegram, X, Email, Slack, Webhook, and many more custom automations\ **Status:** Ready\ **Install:** [https://domino.run/explore/apps/snapshot-tmkg6ni3l3r](https://domino.run/explore/apps/snapshot-tmkg6ni3l3r) If you would like to set up another bot integration, contact us on [Discord](https://discord.snapshot.org) with the details! # Mobile notifications Source: https://docs.snapshot.box/tools/mobile-notifications You can now subscribe to mobile notifications from the Spaces you follow on Snapshot! ## Lighthouse [Lighthouse](https://lighthouse.cx/download?utm_source=snapshot_org) connects you to all your Snapshot spaces and keeps you updated with push notifications whenever a proposal is created or ending soon. Read proposals and comments, and even vote without leaving the app. Everything is cryptographically verified, allowing you to focus on the content that matters. If you own or manage a space, Lighthouse also enables you to securely communicate with your voters, analyze engagement, and more. # Snapshot.js Source: https://docs.snapshot.box/tools/snapshot-js The official JavaScript client for implementing Snapshot's functionality in other apps. ## **Overview** **`Snapshot.js`** is an open source JavaScript client which comes with the core functions of the Snapshot's offchain voting system. It was designed to work both in the browser and with `Node.js`. [**Snapshot.js Repository**](https://github.com/snapshot-labs/snapshot.js) ## Get started ### **Installation** **Node.js** To install Snapshot.js on Node.js, open your terminal and run: ```shell theme={null} npm i @snapshot-labs/snapshot.js ``` **Browser** You can create an index.html file and include Snapshot.js with: ```html theme={null} ``` ### Development **Install dependencies** ```shell theme={null} yarn ``` **Build package** ```shell theme={null} yarn build ``` ## Usage #### Base features #### Init client ```javascript theme={null} import snapshot from '@snapshot-labs/snapshot.js'; const hub = 'https://hub.snapshot.org'; // or https://testnet.hub.snapshot.org for testnet const client = new snapshot.Client712(hub); ``` #### Cast a vote ```javascript theme={null} import { Web3Provider } from '@ethersproject/providers'; const web3 = new Web3Provider(window.ethereum); const [account] = await web3.listAccounts(); const receipt = await client.vote(web3, account, { space: 'yam.eth', proposal: '0x21ea31e896ec5b5a49a3653e51e787ee834aaf953263144ab936ed756f36609f', type: 'single-choice', choice: 1, reason: 'Choice 1 make lot of sense', app: 'my-app' }); ``` #### Create proposal ```javascript theme={null} import { Web3Provider } from '@ethersproject/providers'; const web3 = new Web3Provider(window.ethereum); const [account] = await web3.listAccounts(); const receipt = await client.proposal(web3, account, { space: 'yam.eth', // your space id type: 'single-choice', // define the voting system title: 'Test proposal using Snapshot.js', body: 'This is the content of the proposal', choices: ['Alice', 'Bob', 'Carol'], start: 1636984800, // change it according to your space settings end: 1637244000, // change it according to your space settings snapshot: 13620822, // Use a latest block plugins: JSON.stringify({}), labels: [], privacy: '', // Either '' or 'shutter' app: 'my-app' // provide the name of your project which is using this snapshot.js integration }); ``` #### **Create or edit a space** ```javascript theme={null} import { Web3Provider } from '@ethersproject/providers'; const web3 = new Web3Provider(window.ethereum); const [account] = await web3.listAccounts(); const receipt = await client.space(web3, account, { "space":"pistachiodao.eth", "settings": `{ "name":"pistachiodao.eth", "avatar":"", // IPFS address of space avatar "about":"", "network":"1", "symbol":"XYZ", "website": "", "twitter": "", "github": "", "coingecko": "", "domain":"", // custom domain address "skin":"", // custom skin when custom domain is set "guidelines":"", // guidelines for proposal creation "template":"", // template for new proposals "private": false, // visibility in the space list "moderators":[], // list of space Moderators "members": [], // list of Authors "admins":[], "categories":[ "social", "media" ], "plugins":{ "hal":{} }, "parent":"", // main space ID "children":[], // list of sub-spaces "voting": { "delay":0, // voting delay in seconds "hideAbstain":false, "period":0, // voting duration in seconds "quorum":0, "type":"", // define the default voting system "privacy":"" // pass "shutter" for shielded voting }, "strategies":[{ "name":"ticket", "network":"1", "params":{"symbol":"TICKET"} }], // provide up to 8 strategies with their configuration "validation":{ "name":"basic", "params":{} }, // provide one proposal validation strategy "filters":{ "onlyMembers": false // enable Authors only to create proposals }, "voteValidation":{ "name":"any", "params":{} }, // provide one voting validation strategy "treasuries":[] // provide the organization's treasury account(s) }` }); ``` #### Join a space ```javascript theme={null} import { Web3Provider } from '@ethersproject/providers'; const web3 = new Web3Provider(window.ethereum); const [account] = await web3.listAccounts(); const receipt = await client.follow(web3, account, { "space":"pistachiodao.eth" }); ``` ### **Utils** The below methods are sending a request to Score API. Same as [Hub API](/tools/api) it requires an API Key for **higher usage limits**. You can see we require it for the **`apiKey`** variable. If you already have an API Key for Hub API, you can reuse it for Score. In case you don't have an API Key, follow the instructions here: [API keys](/tools/api/api-keys) #### **getScores** Calculate voting power for a list of voters. ```javascript theme={null} import snapshot from '@snapshot-labs/snapshot.js'; const space = 'yam.eth'; const strategies = [ { name: 'erc20-balance-of', params: { address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', symbol: 'DAI', decimals: 18 } } ]; const network = '1'; const voters = [ '0xa478c2975ab1ea89e8196811f51a7b7ade33eb11', '0xeF8305E140ac520225DAf050e2f71d5fBcC543e7', '0x1E1A51E25f2816335cA436D65e9Af7694BE232ad' ]; const snapshot = 11437846; const apiKey = 'your_api_key_here' // get an API Key for higher limits const url = `https://score.snapshot.org/?apiKey=${apiKey}` snapshot.utils.getScores( space, strategies, network, voters, snapshot, url ).then(scores => { console.log('Scores', scores); }); ``` #### getVp Retrieve voting power for a specific address using given strategies. ```javascript theme={null} import snapshot from '@snapshot-labs/snapshot.js'; const address = '0xa478c2975ab1ea89e8196811f51a7b7ade33eb11'; const network = '1'; const strategies = [ { name: 'erc20-balance-of', params: { address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', symbol: 'DAI', decimals: 18 } } ]; const snapshot = 11437846; const space = 'yam.eth'; const delegation = false; const apiKey = 'your_api_key_here' // get an API Key for higher limits const options = { url: `https://score.snapshot.org/?apiKey=${apiKey}` } snapshot.utils.getVp(address, network, strategies, snapshot, space, delegation, options).then(vp => { console.log('Voting Power', vp); }); js ``` #### validate Validate an address using a given validation strategy. ```javascript theme={null} import snapshot from '@snapshot-labs/snapshot.js'; const validationName = 'basic'; const author = '0xa478c2975ab1ea89e8196811f51a7b7ade33eb11'; const spaceId = 'yam.eth'; const networkId = '1'; const snapshot = 11437846; const validationParams = { minScore: 100 }; const options = {}; const apiKey = 'your_api_key_here' // get an API Key for higher limits const url = `https://score.snapshot.org/?apiKey=${apiKey}` snapshot.utils.validate(validationStrategyName, author, spaceId, networkId, snapshot, validationParams, options, url).then(result => { console.log('Validation Result', result); }); ``` #### **getProvider** Return a Ethers.js JsonRPCProvider connected to an archive node. ```javascript theme={null} import snapshot from '@snapshot-labs/snapshot.js'; const network = '1'; const provider = snapshot.utils.getProvider(network); ``` # Snapshot MCP Source: https://docs.snapshot.box/tools/snapshot-mcp Connect Claude, ChatGPT, Cursor and other AI assistants to Snapshot through the Model Context Protocol. A hosted [MCP](https://modelcontextprotocol.io) server that connects AI assistants to Snapshot governance. Ask an assistant to find proposals, check voting power, cast votes, create proposals, or follow spaces, all in plain language. Connect in under a minute. No install and no API key required. ``` https://mcp.snapshot.box ``` Reading governance data needs nothing but the connection. Write actions (voting, proposing, following) require a wallet you can sign with, authorized once. See [Authentication](#authentication). Building an agent? Point it at [llms.txt](https://docs.snapshot.box/llms.txt) for a machine-readable index of these docs, or [llms-full.txt](https://docs.snapshot.box/llms-full.txt) for their full contents. ## Connect One-click setup, then authorize the connector. Or add it manually: 1. Open **Settings > Integrations > Add integration**. 2. Paste the URL: `https://mcp.snapshot.box` 3. In any chat, click the integration and select **Connect**. Available on Pro, Team, and Enterprise plans. Add the server to your config file: * macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` * Windows: `%APPDATA%\Claude\claude_desktop_config.json` ```json theme={null} { "mcpServers": { "snapshot": { "type": "http", "url": "https://mcp.snapshot.box" } } } ``` Restart Claude Desktop. ```bash theme={null} claude mcp add snapshot --transport http https://mcp.snapshot.box ``` Verify with `claude mcp list`. The button opens the new app modal directly. Or open **Settings > Apps** and click **Create** (enable **Developer mode** under Advanced settings if prompted). Then fill in the details: * **Name**: `Snapshot` * **MCP Server URL**: `https://mcp.snapshot.box` * **Authentication**: `OAuth` Check the risk acknowledgment box, click **Create**, and complete the OAuth flow to authorize the app. Custom apps require Developer mode and are available on Plus, Pro, Business, Enterprise, and Edu plans. Or open **Settings > MCP > Add new MCP server**, choose **HTTP**, paste `https://mcp.snapshot.box`. Or add it from your terminal: ```bash theme={null} code --add-mcp '{"name":"snapshot","url":"https://mcp.snapshot.box"}' ``` You can also run **MCP: Add Server** from the Command Palette and enter the URL `https://mcp.snapshot.box`. Add the server to `~/.codex/config.toml`: ```toml theme={null} [mcp_servers.snapshot] url = "https://mcp.snapshot.box" transport = "http" ``` ## Try it Once connected, ask your assistant in plain language. Start simple, then move to actions: * *"What proposals are active in `ens.eth` right now?"* (read) * *"What's the current voting power of `vitalik.eth` in `ens.eth`?"* (read) * *"Summarize the last 5 closed proposals on `aave.eth` and tell me which passed."* (read) * *"Follow `gitcoindao.eth` for me."* (write) * *"Vote 'For' on proposal `0xabc...` in `ens.eth`."* (write) The AI chains tools automatically: `snapshot-query` to discover data, then `snapshot-vote`, `snapshot-propose`, or `snapshot-follow` to act. Read actions run instantly. Write actions ask you to authorize once (see [Authentication](#authentication)). ## Authentication All write operations (voting, proposing, following) are signed by a Snapshot [alias](/user-guides/aliases), a lightweight signer wallet that can act on your behalf without exposing your main private key. The first time the AI calls a write tool, an OAuth window opens so you can authorize an alias. This is a one-time step. The alias is created and managed by [Coinbase CDP](https://docs.cdp.coinbase.com).