Only this pageAll pages
Powered by GitBook
1 of 85

snapshot

Loading...

User guides

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Developer Guides

Loading...

Loading...

Loading...

Tools

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Snapshot X

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

V1 interface

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Community

Spaces

NFT voting

Learn how to use Voting Strategies to calculate users' Voting Power based on the NFTs they hold.

NFTs are a type of token that represents something unique or indivisible.

Unlike ERC-20 tokens, NFTs are non-interchangeable, meaning each token has its own distinct value and properties. NFTs are designed to represent ownership or proof of authenticity for a specific digital or physical asset. These assets can include digital art, collectibles, virtual real estate, in-game items, and more.

So, can you give someone Voting Power if they hold an NFT from your collection?

Sure thing, read on!

Most common: ERC-721Multi-token: ERC-1155POAPhttps://github.com/snapshot-labs/snapshot-docs/blob/master/user-guides/spaces/space-handbook/nft-voting/broken-reference/README.md

Snapshot Pro

Snapshot Pro plan is designed specifically for spaces seeking advanced governance tools. By upgrading to Pro, you gain access to a powerful set of features that streamline proposal creation, voting, and oversight, letting you focus on what really matters: effective, transparent governance.

Learn more: https://snapshot.box/#/pro

Welcome to Snapshot docs

What is Snapshot?

Snapshot is a voting platform that allows DAOs, DeFi protocols, or NFT communities to vote easily and without gas fees.The tool allows high customization of the voting process to cater to the diverse needs of the users and organizations. Customization includes different aspects like calculation of the users' voting power, selection of the voting mechanism, proposal and vote validation, and many more.In short, Snapshot is an off-chain gasless multi-governance client which results are easy to verify and hard to contest.

Key features

  • Gasless: Create proposals and cast your votes without any gas fees.

  • Flexible voting strategies: Customize how the voting power is calculated through single or combined strategies that enable voting with ERC20s, NFTs, other contracts, and more.

  • Proposal and voting validation: Use custom logic to define who can create a proposal or cast a vote.

  • Multiple voting systems:

How does it work?

Snapshot’s protocol involves three core elements: spaces, proposals, and votes. A space functions like an organization’s profile, and every proposal and vote is tied to that space. To create a space, the only requirement is to have an ENS domain. Once a space is set up, users can submit proposals and cast their votes on the space’s page. Administrators of the space can tailor the rules for creating proposals and voting by configuring various voting and validation strategies. For instance, a space might require users to hold a minimum of 500 tokens to create a proposal, or it might assign voting power proportionally to the token balance in a user’s wallet.

What are you looking for?

Sub-spaces

Create a link between multiple spaces of your organization

Organizations can create multiple spaces and link them together. This is useful for enforcing different space settings for proposal creation, voting, and execution. For example, instead of having to change the space settings each time you create a proposal, you could create additional spaces with some of your most frequently used settings.

Setup

You can define a main space, or multiple subspaces, in the Sub-spaces section of the space settings.

Do this for both your main space and all subspaces to link them to each other. If you have setup everything correctly they will be displayed on the space page.

Limits

  • A space can add upto 16 sub-spaces

Space hibernation

Read more to learn about space hiberation, why spaces get hibernated, and how to activate them.

Space Hibernation is placing inactive or incorrectly configured spaces into hibernation mode. This process aims to enhance the user experience.

Space Controller and admins will see this warning message if space has gone into hibernation mode:

When does a Space hibernate?

A space will automatically enter hibernation mode if it meets any of the following conditions:

Space verification

This page explains what a space verification is and how to get verified.

Verification for legitimacy

Space verification is a process of checking if the space is real and can be trusted.

Verified spaces have a special badge next to their name, as presented on the screenshot below.

Single choice, approval voting, quadratic voting, and more.
  • Signed messages: Votes are cast through signed messages easily verifiable.

  • Custom branding - spaces can use their own branding, color schemes and domain name.

  • Fully open-source: Snapshot is fully open source with MIT license, the code is available on GitHub at https://github.com/snapshot-labs.​

  • User guides

    I want to create a space, proposal or cast a vote.

    Dev guides

    I want to contribute to Snapshot.

    Tools

    I want to integrate Snapshot's functionality in my own product.

    • Inactivity for the past 12 months.

    Implications of Hibernation

    Once a space is in hibernation mode it is impossible to create new proposals.

    Reactivating a Space

    Navigate to the space settings and click "Reactivate space" at the top of the page.

    Don't worry if you voted on a proposal of a fake space - signing a message doesn't cause any implications for you.

    How to get your space verified?

    Make sure to follow all steps from the below checklist:

    • Space has an avatar image uploaded

    • Space has a minimum threshold for proposal validation (or limited to authors)

    • There are at least 5 closed proposals

    • A link from the official website, the GitHub organization or a tweet to confirm the ownership of the Snapshot space

    • Space is not using a testnet or network that has no recognition

    • Ask to get verified by contacting our support on

    Verification process can last up to 72 hours.

    Verification status is valid as long as the space meets all requirements.

    Space roles

    Learn what the space roles are and how you can assign them to users.

    What is a role?

    Role is a set of permissions related to managing your space and its proposals which an account (wallet address) can be granted.

    Role permissions

    Controller

    Controller of space has a full control over the space settings including managing the list of admins.

    Admin

    Admin can edit space settings with the exception of the list of admin users and archive proposals.

    Moderator

    Moderator can manage proposals within the Space and create new ones.

    Author

    Author can create proposals regardless of their voting power and the proposal validation strategy.

    Assign a role to the user

    Controller

    The controller is first assigned during the process of space creation. By default, it is the ENS domain controller. To learn more about this step, head to .

    There can be only one controller per space and can be updated only by the current controller.

    To update the controller role head to the space settings on Snapshot and the Advanced tab. At the bottom of the page you'll find the Danger zone where you can change the controller:

    Paste the address of the account you want to set as space controller in the pop-up window and click Set. It will trigger your wallet browser extension and ask you to sign a transaction with a gas fee.

    Admins, moderators, and authors

    To update the admins and authors lists head to the space settings on Snapshot and the Members tab.

    You can add addresses one by one and choose their applicable role:

    Alternative way to create a space

    If you do not want to have the wallet control your settings, you can follow the steps below to create a space on Snapshot.

    Drawbacks

    • You will not be able to change settings from the UI

    • Every time you want to change the settings, you will need to broadcast a new transaction

    How to Create Space

    • Create a JSON file for your settings on Snapshot. The format of the JSON file could be as follows:

    • Store the JSON file on IPFS

    • Use the IPFS link on the ENS text record. This will make the ENS owner the only controller of the settings.

    • You can check if your space is valid from the GitHub link below

    • Once the above transaction is successful, go to the below link to update the space in Snapshot:

      https://hub.snapshot.org/api/spaces/<ENS DOMAIN>/poke

    Add a custom domain

    By adding a custom domain to your space, you can whitelabel the Snapshot website and present your community with a fully branded governance experience. This feature allows you to display only your specific space at your custom URL and even customize colors to match your brand identity.

    Examples

    More than 400 DAOs already use custom domains for their governance, such as:

    vote.balancer.fi vote.morpho.org vote.sushi.com

    Requirements

    To enable this whitelabel experience, you must subscribe to our . Once your subscription is active, follow the setup process below.

    Setup steps

    1. Add a CNAME record

    Go to your DNS provider or registrar and create a new CNAME record.

    • Host: your custom domain (for example, vote.myprotocol.xyz)

    • Points to: cname-2.snapshot.box

    2. Contact us

    After you have created the CNAME record, let us know you’d like to enable whitelabel. Reach out to us through our , , or .

    What is a space?

    You can think of a space as an organization's account on Snapshot which can be viewed by anyone visiting the platform. It serves as a hub for all proposals related to the organization and a source of information for the users.

    Moreover space allows its organization to manage the roles of organization members and customize the proposal and voting settings.

    Please note that any updates will not affect proposals which already exist, only the new ones will be affected. For more details have a look at the section.

    Space handbook

    Find the best solution for your use case!

    This handbook uses terms like Voting and Validation Strategies,

    Register an ENS domain

    To create your space on Snapshot you need to have an ENS domain. This page will take you through the steps to create an ENS domain.

    ENS on Ethereum Mainnet

    To register a space on you need an ENS domain on the Ethereum Mainnet even if you want to use Ethereum Testnet or any other network (Binance Smart Chain, xDAI, etc). However if you want to experiment with the platform you can register a test space on using an ENS on Goerli Testnet.

    If you already have an ENS domain, feel free to skip this and follow the guide.

    POAP - Proof of Attendance

    Take POAPs into account when calculating the individual Voting Power.

    POAPs stands for Proof of Attendance Protocol. In simple terms, POAPs are like digital badges or tokens that you can earn for attending or participating in certain events, both in-person and online.

    Let's say you attend a conference, workshop, or even a virtual event. The organizers of that event can create a unique digital token called a POAP. It serves as proof that someone was present or participated in that specific event. Each token has a unique code or ID that represents their attendance.

    In technical terms, POAP is a type of ab NFT in ERC721 standard. It's minted on the Gnosis chain and can be migrated to ETH Mainnet as an option. Its contract can be found here: .

    Help Center
    Turbo plan
    Help center
    Discord
    Telegram

    Delegate your voting power

    You can delegate your voting power to another address.

    Delegate your voting power on Snapshot

    1. Go to https://snapshot.org/#/delegate

    2. Enter the address you want to delegate to.

    3. To limit the delegation to a specific space, tap the on toggle button and enter the space key (example: balancer.eth) you want your delegation to take effect on. If no space is selected, the effect will take place for all spaces.

    4. Click Confirm to save your delegation.

    Voting

    User guides

    Protocol

    Plugins

    Services

    How to access a space?

    Spaces are listed upon arrival on https://snapshot.org and first and foremost can be filtered by the organization or the ENS name used to create the space. They can be also filtered by other parameters listed next to the search field. You can access the space from there or directly through a link which includes the ENS name (i.e. https://snapshot.org/#/pistachiodao.eth). It is also possible to define a custom domain name for your space.

    Browse spaces

    Proposal search & filtering

    You can browse through all of Space's proposals and apply specific filters to them, including active, pending, and closed proposals, as well as those created by core members, and flagged proposals.

    Flagged (potentially dangerous) proposals are hidden by default.

    If you'd like to display them, you need to apply the Show flagged proposals filter. This is not recommended due to potentially malicious content.

    Joining a space

    If you want to have a quick access to a chosen space you can join it from the directory of all spaces or through the space's individual page by clicking the Join button. It will create a shortcut for the space on the left sidebar and keep you updated with the number of new proposals displayed on top of the space's avatar.

    Space page

    The content of the space overview will depend on the role of the connected account and the space settings. Below you can see the example of our dummy space and what anyone, even when not connected, can see.

    Pistachio DAO is a dummy space for training purposes
    Settings
    Voting types
    . Make sure to learn about those concepts before diving into this chapter! 👉
    👉
    👉
    We also recommend going through the entire
    guide to get acquainted with the process of
    and updating its
    .

    This section will help you combine Voting types, Validation, and Voting strategies based on your organization's needs. We will look into:

    • Most common configurations

    • Voting threshold

    • Anti-whale solutions

    • Sybil resistance mechanisms, scam & spam prevention

    If none of the above are a good fit for your Space, we encourage you to use the Search feature of this documentation with the Lens option selected to ask a question and receive a human-friendly answer 😉

    Feel free to reach out to us on Discord to suggest use cases that we did not cover!

    Voting strategies
    Validation strategies
    Voting t
    ypes
    Spaces
    creating a Space
    settings
    Creating an ENS Domain

    1a. Search for the availability of your name on Snapshot's Create a space page

    1b. Search for the availability of your name on ENS by following this link: https://app.ens.domains/

    2. If the name is available you will be able to see the 3 steps and the registration fee. Please note that the names with 3 or 4 characters cost considerably more than the names with 5+ characters. \

    3. Connect your wallet by tapping on the Connect button on the top left corner of your screen. Make sure your wallet has enough balance to make the transaction successful and that you selected the Ethereum Mainnet (or Goerli Testnet for the demo page).

    4. Confirm all transactions for the three steps from your chosen wallet provider. \

    5. You have now successfully registered on ENS! Additionally, you can also make your Ethereum address point to an ENS name by clicking on ‘Set Reverse Record’ or by going to 'My Account' and then selecting 'Reverse record' and completing the transaction.

    Congratulations! You have created your domain on ENS. You are now ready to create your space on Snapshot.

    If you want to register a custom domain you already own check out this guide: https://docs.ens.domains/dns-registrar-guide​

    https://snapshot.org
    https://testnet.snapshot.org
    Create a space
    Every single POAP has a unique tokenID.

    Now, how can you reward your community for their attendance by giving them Voting Power?

    There are several strategies that you can use for your Space:

    poap

    The poap strategy returns the balance of POAP for a certain event as Voting Power. However, when the eventIds is skipped, it will return the number of all POAP tokens owned by the address.

    Strategy setup:

    🤔 How to find the event ID of the POAP?

    Visit the POAP gallery and search for the name of the POAP you're looking for:

    You can test it out in the Playground on Snapshot:

    poap-with-weight

    It's suggested to use this strategy as an auxiliary scoring method because the score api server may have difficulty handling thousands of requests if huge single Ids are passed into the parameter.

    Since POAP is an ERC721 NFT, it can be distinguished by its tokenID, i.e. each tokenID represents a unique POAP. One can designate multiple tokenIDs and attach different weight to each.

    In the example, if one holds a poap with the tokenID "100001", he will get 100 (1*100) voting power. Playground

    0x22C1f6050E56d2876009903609a2cC3fEf83B415
    https://github.com/snapshot-labs/snapshot.js/blob/master/test/examples/space.json
    https://github.com/snapshot-labs/snapshot.js/blob/a0adc547aa0922aa6abd35708a4a292048bca6a2/test/schema.ts#L4

    Space badges

    Learn about the meaning of space badges

    At Snapshot Labs, we want to help our users safely browse and discover spaces and make informed decisions about them. That’s why we’ve introduced verification and warning badges.

    The goal is to provide users a simple way to navigate voting in web3. To facillitate that Snapshot displays a verification badge or warning label next to the space name and in each proposal belonging to that space.

    The verification badge signifies that the space has been verified as the authentic version of the protocol it claims to be. Please note that the verified badge does not represent an endorsement of the protocol by Snapshot Labs. A verified badge also says nothing about its merits as an investment. A space may carry a verified badge but might still be a bad investment idea. Think of the verified badge only as a tool to help you find the correct version of the space you’re searching for.\

    If you would like to get your space verified, head to and follow the guide.

    The warning badge means that we either identified that space as impersonating a project, IP infringement or potential scam.

    Badge
    Label
    Description

    Voting on a proposal which belongs to a space flagged with the warning badge is not dangerous as the vote does not trigger a transaction - your account as assets are safe.

    Add a skin

    Learn what a custom skin is and how to use one for your space with a custom domain.

    What is a skin?

    A skin is a custom color scheme which you can use for your space if it has a custom domain set up. The customization will be visible only on the custom domain and will not affect the color scheme of your space on https://snapshot.org.

    If you haven't setup a custom domain yet, head to

    Mana

    Mana is a meta-transaction relayer that allows users to interact with Snapshot X without paying gas fee. This service works in conjunction with signature based Snapshot X . 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

    Architecture

    To enhance the user experience of interacting with the protocol, we offer a number of additional off-chain services. All of these products are fully open source in the . These are all completely optional and therefore do not introduce any censorship concerns.

    • : An easy to use interface for interacting with the protocol.

    • : A fast and efficient indexer for all the protocol data.

    • :

    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 and support multiple chains.

    GitHub

    {
      "symbol": "POAP",
      "eventIds": [
        "1213",
        "1293"
      ]
    }
    {
      "symbol": "POAP",
      "tokenIds": [
        {"id":"100001", "weight": 100}, 
        {"id":"100002", "weight": 10}, 
        {"id":"1000", "weight": 1}
      ]
    }
    https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/mana

    Public API

    https://mana.box

    authenticators
    Public APIs

    Mainnets: https://api.snapshot.box Testnets: https://testnet-api.snapshot.box

    Checkpoint
    https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/api
    Delegation setups
    Liquidity / staking pool token voting
    NFT voting
    Custom calculations

    Quorum

    The content of this page is being working on at the moment

    Create a space
    to learn how you can do it.

    Add a skin to your space

    Head to your space settings on Snapshot and find the Custom domain section.

    Select a skin by scrolling through the list of available choices or by typing the name you are looking for. If none of them work for you, you can create a custom skin following the section below: Create a custom skin

    Don't forget that the skin will only be applied to the custom domain URL!

    Snapshot skin selector.

    That's it! You should be able to see the custom color scheme for your space on its custom domain 🎉

    Create a custom skin

    1. Fork the snapshot-spaces repository

    Create a fork of the snapshot-spaces repository:

    2. Add the styling for your space

    Create a new file in the skins directory following the my-space.scss naming convention:

    Add your custom scss to the newly created file. The class name .my-space should match the filename you created. Make sure that you edit only the values (i.e. #384aff on the right-hand side and do not change the properties (i.e. --primary-color).

    Don't forget to change the file name my-space.scss and css selector .my-space to your space name.

    You can have a look at a custom skin example by following this link.

    3. Import the scss file

    Add the import of your newly created scss file to the skins/index.js file in the following format:

    4. Create a pull request

    Create a Pull Request with the above changes on the original snapshot-spaces 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 which can take a couple of days. Once it's deployed you can move on to the next step.

    5. Update your space settings

    Follow the steps from Add a skin to your space to use your own custom skin.

    Add a custom domain
    A Typescript SDK that can be used to build 3rd Party Integrations with the protocol.
  • Mana: A meta-transaction relayer that can provide a way for proposal creation and vote costs to be sponsored.

  • Snapshot Labs Github organization
    SX UI
    SX API
    SX.js

    Turbo space

    Verified space with Turbo plan

    Verified

    Manually curated by Snapshot Labs to identify spaces that passed the verification criteria.

    Warning

    Manually curated by Snapshot Labs to identify spaces that impersonate a project, infringe IP, or spaces that are proven to be scams.

    Space verification

    Most common: ERC-721

    Learn which strategies to use to allow users holding NFTs to get Voting Power.

    ERC-721 was the first standardized interface for creating and trading NFTs and is still considered the gold standard.

    ADD SOME JOYFUL TEXT HERE

    In Snapshot, the balanceOf method is applied to both the erc721 and erc20 strategies. The main difference in their implementation is that the decimal of an NFT should always be set to 0.

    All NFTs strategies can be combined with other Voting Strategies, like for example erc20-balance-of or whitelist. The results of each Strategy are summed up to result in the final Voting Power of each user.

    This strategy returns the balance of the specific ERC721 NFT that the user holds.

    It's the most common strategy for NFT-based voting.

    How to set it up? Just provide the contract address of the NFT and its symbol.

    One who owned the specified NFT at the snapshot of proposal creation will be able to vote based on the number of NFTs they have in their wallet.

    If your project is releasing the NFT collection in batches with a limited amount of NFT minted on the deployment of the contract, then don't worry.

    It won't affect the calculation of the Voting Power. Feel free to implement it in your space and engage your community in the governance process! ⚡️

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    This strategy gives an arbitrary weight to the balance of the voters for a specific ERC721 NFT.

    By adjusting the multiplier to 100, if a user owns 1 LAND in his wallet, he will get 100 votes (1*100) on the proposal.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    Each NFT under the same contract address can be identified by its unique tokenID.

    This strategy returns the balance of the voters for a specific ERC721 NFT with a given tokenID.

    If a user holds the NFT which tokenID is included in the tokenIds parameter, they will get 1 vote regardless of how many they hold in total.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    This strategy is a modification of .

    It allows 1 vote per whitelisted tokenID specified in the parameter.

    So in contrary to the previous strategy, the user who holds three NFTs, each included in the strategy setup, will have 3 Voting Power.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    The strategy returns the weighted balance of the voters for ERC721 NFT of which the Token ID is specified in the tokenIdWeightRanges.

    Why it might be useful for your space?

    • NFTs in the collection have continuous tokenIDs

    • You want to assign different weights to specific batches of the collection (higher VP for first NFT minters, lower VP for latest NFT owners)

    The NFT can be assigned a different weight based on the range its token ID falls within.

    If it doesn't fit into any range, it'll still be counted as 1 by defaultWeight setting. However, If you don't want to include the NFT that's outside of the range into voting power, simply set that defaultWeight to 0.

    For example, a user who owns 3 NFTs with the tokenIds: 1000, 6000 and 8000 respectively will have 3 Voting Power. [(1*2)+(1*1)+(1*0)]

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    Delegation

    Which Voting Strategy to choose to enable users to vote on behalf of their delegators?

    Delegation enables you to delegate your voting power to another wallet. Unlike other actions in Snapshot, the delegation itself is an on-chain behavior, requiring some gas fees to execute a delegation transaction. Within this page we will be using two terms: - delegator - user who delegated their Voting Power to another address - delegate - the user who was delegated the Voting Power and can vote on behalf of others

    How can users delegate their Voting Power?

    Before we jump straight into the Voting Strategies setup, let's first understand how users can delegate their Voting Power to others through Snapshot.

    <UPDATE FOR NEW DELEGATION UI>

    Voting Strategies

    In order to take the delegated Voting Power into account, your space has to use one of the delegation Voting Strategies.

    If the delegation-based Voting Strategies are not set for your space, even if users have delegated their VP or are delegates themselves, their Voting Power will be calculated only on the basis of their own assets. This means 👉 Delegation will not work.

    In Snapshot, based on whether the delegated power can be taken back by the delegators when they vote, there are two main delegation strategies:

    • with-delegation - the delegator cannot vote, only the delegate can participate in voting

    • delegation - allows the delegator to vote despite having delegated their Voting Power

    Let's have a look at both in detail:

    This strategy is a combination of delegation + own Voting Power (if not delegated to anyone), moreover, it prevents the delegators from taking back their delegated VP.

    By using this strategy, one can no longer vote if he delegated to another one, and the delegatee's voting power will be his own VP + delegated VP.

    Delegators cannot vote on proposals if this strategy is used in the Space and if they have delegated their VP to another address (for the current space or all spaces). If delegators wish to participate in voting themselves, they have to revoke their delegation for the current space or all spaces before the creation of the proposal.

    The sub-strategies are used to define the source of own VP and delegation.

    Strategy setup

    Note that you can pass the **delegationNetwork**as the optional parameter so that the strategy can read delegation from one network and Voting Power from other networks.

    This is useful when the space has multiple strategies across chains and wants to enable delegation for all the relevant tokens.

    The space can specify the same delegationNetwork in all strategies so users will only need to delegate once in one network instead of delegating respectively on each chain to have all vp delegated

    You can test it out in the Playground on Snapshot:

    This strategy returns the delegated Voting Power only.

    In order to count delegate's full Voting Power including their own holdings and the delegation, you need to set up regular strategies for the space, like for example erc20-balance-of or balance-of-woth-thresholds.

    Now, what's the deal with both delegate and delegator casting a vote?

    In the delegation strategy, if A delegates to B and both of them vote, then the delegated voting power is not calculated. Only the vote of A will be calculated.

    The vote of B will be counted if A does not vote.

    It can include a list of sub-strategies (erc20-..., erc1155-..., whitelist, etc) to calculate the user's Voting Power based on the delegation.

    Strategy setup

    The below example shows that the space allows YFI balance as the delegated voting power in yam.eth space.

    The parameter delegationSpace is optional in space settings. By default it takes delegations of current space.

    However, if you're testing delegation in the playground, you have to specify the space ENS name to let it know from which space you're fetching one's delegation.

    You can test it out in the Playground on Snapshot:

    Multi-token: ERC-1155

    The standard for fungible, non-fungible, and semi-fungible tokens.

    Let's start with a quick overview of the ERC721 and ERC1155 standards:

    Pros
    Cons

    ERC-721

    Most widely recognized, golden NFT standard

    Inability to conduct batch transfers

    ERC-1155

    Supports different token types in a single contract, allows batch transfers

    Stores less robust information in order to save time and transaction costs

    Let's have a look at what Voting Strategies you can use in your space:

    The strategy returns the balance of a specific ERC-1155 token in the user's wallet.

    Since the ERC-1155 standard supports multiple token types, the tokenId here may refer to a particular kind of token or a single item of the collection under the contract address.

    If you want to get the balance of all tokenIds in the contract, you can use the erc1155-balance-of-all strategy. If you have multiple tokenIds, you can use the erc1155-balance-of-ids or erc1155-balance-of-ids-weighted strategy.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    This strategy returns the balance of voters for all tokens in an ERC1155 contract.

    On Polygon, works only with contracts with total tokenIds less than 6000.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    Use this strategy if you want to calculate the Voting Power taking into account multiple tokenIDs.

    Try using this strategy with only a few tokenIDs passed in the parameter for the Score API server is likely to have difficulty handling a large number of requests.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    The equivalent of the Voting Strategy.

    By adjusting the multiplier to 5, if a user owns 1 DAWN in his wallet, they will get 5 Voting Power (1*5).

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    If you'd like to give more or less weight to a list of specific tokenIDs, you can do so by using this strategy. It might serve as a great addition to the for VP differentiation.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    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 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. Voilà! You have just cast a vote 🎉​

    UI

    The UI is the official interface for Snapshot X.

    GitHub

    https://github.com/snapshot-labs/sx-monorepo/tree/master/apps/ui

    UI

    Create a 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.

    1. Navigate to the src\validations.

    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:

    The validation name has to be included in the in the validationClasses variable.

    4. Test your validation

    To make sure your validation passes all tests, run:

    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:

    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.

    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.

    • 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 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.

    • 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 section for more information on the payload contents.

    The core use case for this authenticator is to allow smart contract accounts such as multi-sigs 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 or .

    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.

    And that's it, your proposals can now be executed! 🎉

    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 with this link: 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

    Domino

    For: Discord, Telegram, X, Email, Slack, Webhook, and many more custom automations Status: Ready Install:

    propbot

    For: X Status: Ready Install:

    Boto

    For: Telegram Status: Ready Install:

    If you would like to set up another bot integration, contact us on with the details!

    Create a space

    This page guides you step by step through the process of creating a space for your organization.

    If you already have a space without ENS domain (legacy), you need to .

    If you want to use Snapshot in a demo mode to experiment with the platform head to - our playground connected to the Goerli Testnet. Make sure to provide an ENS name registered on Goerli Testnet in the first step below.

    Webhooks

    Receive event notifications with webhooks.

    Snapshot uses webhooks to notify your application when an event happens. Webhooks are particularly useful for asynchronous events like when a proposal is created, when it starts, or when it ends.

    The webhook server sends a request for any new event, the request is sent to the configured URL with POST method and the event object as body.

    Here is an example of the event object:

    Here are the possible events:

    proposal/created When a new proposal is created

    proposal/start When the voting period for a proposal starts.

    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.

    Audits

    Snapshot X has completed a full audit with and .

    Scope
    Company
    Date
    Report

    Tools overview

    Learn how you can integrate Snapshot into your product, query our APIs or set up webhooks and notifications.

    Vote on a proposal

    Learn how you can submit your vote on a proposal and how your voting power is calculated.

    Who can vote on proposals?

    There are several aspects that define if you are eligible to vote on a specific proposal.

    What is a plugin?

    Learn what plugins are and how to use them.

    Plugin is an extension which enriches Snapshot by additional functionalities without changing the core of Snapshot's logic. The possibilities are many, from plugins which reward users with an NFT when they cast a vote, through adding a comment box for voters to explain their choice, to enabling on-chain execution of Gnosis Safe transactions.

    You can browse through all plugins by heading to and selecting the Plugins filter in the search bar:

    Add a plugin to your space

    You can extend your space's functionality by adding plugin(s) to it. They will be applied to all proposals created after the plugin has been added.

    └── skins
        └── my-space.scss
    .my-space {
      --primary-color: #384aff;
      --bg-color: white;
      --text-color: #586069;
      --link-color: #111111;
      --heading-color: #111111;
      --border-color: #d1d5da;
      --header-bg: white;
      --block-bg: transparent;
    }
    import my-space from "./my-space.scss";
    └── src
        └── validations
            └── basic
    function authenticate(
        uint8 v,
        bytes32 r,
        bytes32 s,
        uint256 salt,
        address target,
        bytes4 functionSelector,
        bytes calldata data
    ) external;
    https://snapshot.box
    Help Center
    https://domino.run/explore/apps/snapshot-tmkg6ni3l3r
    https://x.com/proposalbot
    https://medium.com/boto-corp/telegram-bot-for-snapshot-no-code-353e7f3e2dc8
    Discord
    Voting strategies

    Snapshot.js

    Use Snapshot's javascript library to integrate features like space and proposal creation, voting, and more in your product.

    GraphQL API

    Query the GraphQL API exposing Snapshot's data like information about spaces, proposals, voting, and more.

    Webhooks

    Set up a webhook to receive notifications about specific events to your endpoint.

    Bots

    Use automated bots to get notified about proposals on Snapshot.

    Space actions
    Space actions
    Proposal validations
    Voting strategies
    proposal/end
    When the voting period for a proposal ends.

    proposal/deleted When a proposal is deleted by the author or an admin of the space.

    Test a webhook

    You can use this URL https://webhook.snapshot.org/api/test?url=https://example.com and change example.com with your own endpoint to trigger a test callback.

    Subscribe to events

    If you want to subscribe to Webhooks, please fill out this form, If you don't receive events even after 48 hours after filling the form, please contact our support on Help Center

    {
      id: 'proposal/QmZ21uS8tVucpaNq2LZCbZUmHhYYXunC1ZS2gPDNWwPWD9',
      event: 'proposal/created',
      space: 'yam.eth',
      expire: 1620947058
    }
    function authenticate(address target, bytes4 functionSelector, bytes calldata data) external;
    1. Set or get an ENS domain for your space

    Tap the plus + button in the left sidebar to create a new space.

    If you already own an ENS domain, make sure you are connected to snapshot.org with the Ethereum address that is set as controller of your ENS name (if you are confused about the difference between ENS Registrant and Controller, have a read here).

    If the above condition is met, your ENS name will appear. To confirm it's correct, click on it. \

    If you don't own an ENS domain you will have to register one. Enter a name that suits the needs of your DAO in the register a new domain field and click Register. You can then follow Register an ENS domain and come back here for step 2 once domain has been registered.

    3. Create your profile

    To complete the profile of your space you have to enter a name for your DAO (mandatory) and can add further optional information like a description, avatar, categories describing the field that your DAO is operating in and more.

    You can add or update this information after you have finished creating the space.

    4. Set your very first strategy

    Your space can combine up to 8 voting strategies which will be responsible for calculating the users' voting power. The setting affects all proposals that will be created for that space.

    If you would like to differentiate the calculation of users' voting power between proposals you will have to create a sub-space. Head to Sub-spaces to learn more.

    For the initial setup you have to choose one of the three following strategy types to then specify the strategy details:

    Voting strategies types

    Let's have a look at each of the possible options and see how you can specify the strategy details.

    Each strategy is taking into account the assets that belong to the voting address at the time of proposal creation, not at the time of voting.

    Token weighted voting

    Voting power is weighted by the amount of the token held by the user. The token can be an ERC-20, ERC-721 or ERC-1155 token standard.

    1. Select your token network

    2. Select your token standard

    3. Enter your token contract address

    One person, one vote

    This option can be used in two different ways:

    • Whitelist voting lets you specify a list of addresses that will be able to vote

    • Ticket voting will let any wallet vote (option used mostly for testing purposes)

    Custom setup

    If you feel ready to dive deeper into the custom setup here are a few hints that you may find useful:

    • You can select up to 8 different strategies. Voting power is cumulative.

    • Network can be selected individually for each strategy. This way you can leverage multi-chain voting power calculation.

    • It is possible to set a different symbol for each strategy. They will be displayed on the proposal page.

    • You can write a custom voting strategy if the existing ones are the sufficient for your needs. Have a look at to learn more.

    At the time of writing this article there are around 415 Snapshot voting strategies and this number keeps growing. Learn more about the strategies.

    Moderation

    Admins will be able to edit the space settings and moderate proposals.

    Authors will be able to create proposals without any constraints like proposal validation. Make sure that members specified in authors field are allowed to submit a proposal.

    You can add between 1 and 100 addresses. Each line should be an individual address - do not list multiple addresses in on line with any characters like commas, dots or semicolons.

    Your space is live !

    What else can I customize?

    Migrate your space to ENS
    https://testnet.snapshot.org
    Add a custom domain
    Add a skin
    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

    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, 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.

    1. A user creates a proposal. The space registers it and stores the start timestamp.

    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.

    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 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 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.

    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. Once the transaction is relayed, and by leveraging the modularity of our authenticators system, a special authenticator 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 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!

    Snapshot X EVM

    16-06-25

    Snapshot X EVM

    ChainSecurity

    03-07-23

    Download

    Snapshot X Starknet

    OpenZeppelin

    10-31-23

    ChainSecurity
    OpenZeppelin

    Voting strategies

    Each space specifies their voting strategies in the space 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 snapshot of proposal creation.

    In most cases you will be required to have a sufficient amount of tokens in the connected wallet at the time 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.

    Voting validation

    Another aspect determining whether you are eligible to vote or not is a voting validation defined by the space. It is a mechanism used to define certain conditions like minimum token balance or also to prevent Sybil Attacks. In other words the space owner wants to make sure that you are human and that bot or fake accounts are not used to overrule the outcome of the voting.

    Cast a vote

    1. Click the Connect wallet button in the top right corner.

    2. Connect with the wallet provider where you hold the tokens relevant for the space you want to vote in.

    3. Go to the space page on Snapshot and selected the active proposal you are interested in.

    4. Select the option(s) you want to vote for. The can differ between individual proposals.

    5. Click to Vote button and sign the message via your wallet provider when prompted.

    6. Voilà! You have casted a vote 🎉

    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.

    1. In order to add a plugin head to your space settings by clicking Settings on your space's page:

    2. Scroll down to the Plugins section and click Add plugin. Select the plugin you want to add in the pop-up window:

    3. Configure the plugin according to your needs. Each plugin requires different setup so before you add it to your space make sure which parameters do you need to provide. As an example, the Gnosis SafeSnap plugin requires a CHAIN_ID of the network your safe is using and the realityAddress . Check out the Using Safe multi-sig setup steps if you want to learn more about this specific plugin.

    4. Click Add and don't forget to Save your new settings.

    5. Well done, you have added a new plugin to your space! 🎉

    The newly added plugin will affect only proposals created after the settings have been updated.

    https://snapshot.org
    erc721
    erc721-with-multiplier
    erc721-with-tokenid
    erc721-with-tokenid-weighted
    erc721-with-tokenid
    erc721-with-tokenid-range-weights
    with-delegation
    delegation
    erc1155-balance-of
    erc1155-all-balances-of
    erc1155-balance-of-ids
    erc1155-with-multiplier
    erc1155-balance-of-ids-weighted
    erc721-with-multiplier
    erc1155-with-multiplier
    index.ts
    https://github.com/snapshot-labs/score-api#checklist-for-adding-a-new-strategy

    Settings

    Customize your space settings, sub-spaces, manage access, voting strategies and validation and more!

    Access your settings

    Navigate to your space settings from the space menu by clicking on Settings.

    If you have trouble finding the settings page you can manually navigate to it with the following URL: https://snapshot.org/#/<YOUR-ENS-NAME>/settings

    Edit the controller of your space

    Space controller is the main account that is able to manage the space settings and assign other addresses as admins of the space. By default it's set as the ENS Controller address however you can change it to another address.

    To replace the current space controller by a new controller click the Edit controller button.

    You will need to sign a transaction on the Ethereum Mainnet to set the ENS text-record.

    Edit your profile information

    To complete your profile, you can upload an avatar/logo, enter a name for your DAO, a description, select up to 2 categories and other details. These settings can be changed at any time.

    Social accounts

    You can link your social media accounts to the space on Snapshot by typing in your account handle, i.e.

    Sub-spaces

    If you don't know what a sub-space is head to to learn more about them!

    In order to connect the space with sub-spaces you need to set them up for both main- and the sub-space.

    1. First create the sub-space as you would create a normal space. Once the sub-space is set up, type it's linked ENS domain name in the Sub-spaces input field. If everything went well, you will be able to click the + on the right.

    2. Head to the sub-space settings and type in the linked ENS domain name of the voting in the correct input field.

    If you see a ❌ after typing the sub-space name it means that it cannot be found. Make sure that you have created the sub-space before adding it.

    Voting strategies

    Specify how the voting power should be calculated by adding one or up to 8 strategies.

    To learn more about what they are and how they work head to .

    If you are using a strategy which gives everyone 1 vote regardless of their holdings, you are required to set up a Voting Validation. Head here to learn how to do it:

    Admins and authors

    In order to specify who can manage the space or create proposals, fill in the apropriate field with addresses for:

    • Admins - able to edit the space settings and moderate proposals.

    • Authors - able to create proposals without any constraints Make sure that members specified in authors field are allowed to submit a proposal.

    Each line should be populated with one address only. Do not add multiple addreses in one line and do not use separators like commas, dots or semicolons. You can add up to 100 addresses in each field.

    Head to to learn more about each role.

    Proposals

    You can provide guidelines and a template which will be displayed during the proposal creation:

    Proposal validation

    Due to multiple spam attacks on Snapshot each space is now required to set up a Proposal Validation. Learn how to do it in

    To validate if someone can post a proposal or not you can use the basic validation by default which takes your voting power with space strategies and checks if you pass a defined threshold.

    To learn what a validation is head to .

    Voting

    The voting delay is a value in time between the time of proposal creation and the moment when users are allowed to vote. The voting period is the duration that the proposal is active and votes can be cast. It is counted from the moment

    Quorum is the amount of voting power collectively achieved by voters which is required for a proposal to pass.

    Shielded Voting

    You can enable Shielded Voting within your space if you want to enable partial privacy and reduce voter apathy. Shielded Voting designed by Shutter is enabling that by using threshold encryption. You can learn more about it in the

    In short, Shielded Voting is a voting setting in which the voters choices are private during the voting period and get revealed when the proposal closes.

    To enable it head to Voting tab and select Shutter within the Privacy setting.

    Delegation

    In the Delegation tab you can set a custom delegation contract to enable delegate discovery for your space:

    Once set, the delegates registry will be visible in the Delegates tab in the Space page.

    Custom domain and skin

    You can add a custom domain to your space by following the guide.

    If you want to apply a different design (skin) to your space, you have to set a custom domain first. Head to to learn how to add a custom skin for your custom domain.

    Treasuries

    In order to link your organization's treasury to Snapshot you have to:

    1. Choose the treasury network

    2. Enter its Ethereum address

    3. Fill in the name of that treasury.

    It is possible to add multiple treasuries to one space.

    Plugins

    You can customize your space even further with various plugins which provide extra features. To learn more about the plugins head to section.

    Save your settings

    Make sure to click the Save button on top of the settings page to apply the changes to your space. Once you sign a message (gasless) in your wallet, changes will be applied!

    Voting strategies

    Learn what a voting strategy is and how to set it up.

    What is a voting strategy?

    Voting strategy is a set of conditions used to calculate user's voting power. Strategies enable Snapshot to calculate the final result of voting on a given proposal.

    In technical terms a strategy is a JavaScript function that returns a score for a set of addresses.

    Strategy/-ies are defined in the space settings in Voting strategies section. Each space can select from one up to eight voting strategies. The default strategy is erc20-balance-of - it calculates the balance of a predefined ERC20 token for each user.

    Voting strategies can be used to create a score from on-chain data, the data however does not necessarily need to be monetary. As an example a strategy can calculate how many POAPs or specific NFTs a user owns.

    You can browse through 400+ strategies by selecting the Strategies filter on the main page of . If you can't find a strategy that fulfills your needs you can create a new one. To learn more about creating custom voting strategies head to .

    How to set up a strategy?

    Majority of spaces on Snapshot is using a single strategy however if you need a more complex calculation, you can combine up to 8 strategies. They will be applied to all proposals created for your space (created after the update of the settings) and the voting power will be calculated cumulatively.

    Multiple voting strategies If you combine several voting strategies the voting power will be calculated in the following way: &#xNAN;total voting power = voting power from strategy A + voting power from strategy B + ...

    In order to set up a voting strategy head to your space settings and scroll down to Strategies section. You should see the below pop-up after clicking Add strategy and selecting a strategy from the list:

    You will see that there is information that you need to provide in order to make the strategy work, for example the network where the token is deployed, its symbol and address of the token's contract.

    Each strategy will require a different setup and you can read the full description and see the required parameters in the strategy's page, for example . You can find each strategy's details through using the search bar and Strategies filter.

    Testing a voting strategy

    Before you add the strategy to your space's settings we highly recommend to test it in the Playground in order to avoid any potential issues with the voting process.

    If you made a mistake in your space settings and votes have already been cast it is not possible to revert them. The best solution would be to (1) delete the proposal, (2) update the settings with correct strategies and (3) recreate the proposal from scratch after the settings have been updated.

    You can access it from the strategy's detail page by clicking the Playground on the right-hand side:

    Your browser will load a Playground page where you can test the custom setup for the chosen strategy. As you can see on the below screenshot you can set the required parameters and provide a list of addresses which in this case are or are not holding a the PUNK ERC721 token.

    If everything is set up correctly you should see the calculated voting power for each address after clicking the ▶️ button:

    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 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.

    • 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:

    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):

    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:

    • newImplementation: The address of the new space implementation.

    • data: A encoded function call to initialize the new implementation.

    Refer to for more information.

    Manage ownership

    The owner can be transferred to a new address or renounced entirely.

    Using Safe multi-sig

    Learn how you can use Snapshot with a Gnosis Safe Multi-sig wallet.

    You can use a Gnosis Safe to vote, create a proposal or setup a space on Snapshot.

    Your Safe has to be on the same network as the Space is. Head to Space settings, Strategies tab to check the Space's network.

    Spaces can be created and updated only by Ethereum Mainnet accounts.

    Voting and proposals are accepted from Space's network only.

    Connecting your Safe

    To connect your Safe to Snapshot

    1. Go to your Safe wallet like https://app.safe.global/apps/open?safe=<NETWORK>:<SAFE_ADDRESS>

    2. Go to the Apps tab.

    3. Search for Snapshot and click on it.

    4. You should now see the Snapshot interface connected to your Safe wallet.

    Signing with your Safe

    There are two ways to sign with your Safe multi-sig wallet on Snapshot:

    1. Synchronous signing (offchain) - you keep the confirmation modal open until all Safe signers confirm the transaction.

    2. Asynchronous signing (onchain) - you can close the confirmation modal and the transaction will be created in your Safe transactions queue. The Safe signers can confirm it later.

    For more information, refer to the .

    Synchronous signing

    By default, the first signer who is signing a message will have to keep the confirmation modal open until all other signers confirm the message.

    This happens offchain and does not create a transaction in your Safe transactions queue. but in messages tab of your Safe. Other signers need to confirm the message in their Safe interface.

    Asynchronous signing

    If you'd like to enable asynchronous signing for all Safe's signers and not have to keep the modal open, you have to enable on-chain signatures in the Safe settings.\

    1. Go to Safe Settings -> Safe Apps.

    2. Enable Always use on-chain signatures

    That's all 🎉 Signers can now sign even if you you close the confirmation modal.

    It will create a pending transaction in your Safe transactions queue. This transaction must be confirmed by the Safe signers within 144 hours.

    Overview

    What's in this book?

    What's Snapshot X?

    Snapshot X is an on-chain voting protocol. Technically speaking, it's a set of modular smart-contracts that interact with each other to do all the book-keeping. The difference with the original is that Snapshot X is fully on-chain. 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 on-chain: The voting logic is fully on-chain 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 1ETH to vitalik.eth. If the proposal passes, the 1ETH will automatically get sent to vitalik.eth

    Snapshot can be found on EVM-chains and on Starknet. Both the and the are open source. The Starknet's specific details can be found on .

    If anything in these docs is unclear or you would like more detail, do not hesitate to reach out on .

    [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.

    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 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 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 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.

    And that's it! You have created a new proposal 🎉

    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:

    Email notifications

    Subscribe to a weekly newsletter summing up what has happened in the spaces you follow.

    Snapshot allows you to subscribe to an e-mail notification system which helps you to stay up to date with spaces you follow.

    You can opt in to receive the following notifications:

    • Weekly digest - summary of spaces' activity over the previous week

    • New proposal - new proposals in spaces you follow

    • Closed proposal - list of closed proposals with their results

    How to subscribe to the e-mail notifications?

    1. Connect your account

    Connect your wallet to Snapshot by clicking the Connect wallet button in the top bar.

    2. Open profile menu

    Click on your account displayed in the top bar and select ✉️ Email notifications:

    3. Subscribe with your e-mail address

    Type your e-mail address and click Subscribe. You will be asked to sign a message in your wallet provider extension.

    4. Confirm subscription

    After signing the message and receiving the confirmation of a successful subscription head to your mailbox and verify your e-mail:

    A new tab will open with a successful verification message.

    5. Enjoy!

    That's it! 🎉

    Notifications are sent out every Monday on a weekly basis.

    You can unsubscribe from the notifications at any moment.

    Manage subscriptions

    You can easily opt-in and out of the available notifications.

    Click on your profile name in the top bar and select Email notifications:

    A new pop-up window will open where you can toggle the different options:

    How to unsubscribe?

    You can easily unsubscribe via the link in each weekly digest or proposal-related notification email.

    Head to your inbox and find an email from the Snapshot subscription, apart from the initial verification message. Scroll down and find the Unsubscribe link.

    You'll be redirected to a page informing you that you have successfully unsubscribed from the notifications.

    SafeSnap

    SafeSnap enables trustless execution for your Snapshot governance on a Safe treasury using Reality.eth oracle.

    How does it work?

    SafeSnap plugin is an oracle based solution which works together with Safe and Snapshot in the following way:

    • A Safe module, where anyone can create a new proposal: an array of multisend transaction payloads.

    • Each proposal is a Reality.eth question asking if (1) the linked Snapshot proposal passed, (2) did the proposal include the payload, and (3) does the payload do what the proposal describes.

    • If the proposal passes on Snapshot, then Reality.eth should resolve to the same outcome, and after a 24 hour cooldown period, the proposal’s transactions are executable by anyone.

    • Reality uses an ERC-20 token (a given DAO’s governance token) for the bond. The minimum bond can be set by way of a proposal to the DAO.

    • The UI is a Snapshot plugin, in which users can enter an array of tx-payloads to be executed sequentially by the Safe if the proposal passes. Once the proposal has passed, the Reality.eth question has resolved, and the 24 hour cooldown period is over, there is the option on the Snapshot interface to trigger each of the multisend transactions in the proposal.

    Setup

    Follow the official Zodiac guide on how to setup a SafeSnap for your project. I'm a non-dev operator (space controller or admin):

    I'm a developer:

    Security recommendations

    In order to ensure maximal safety we recommend to define several paremeters for your setup:

    • Set a substantial bond for an answer

    • Select an arbitrator

    • Select a long cooldown

    • Set up a monitoring infrastructure

    Additional resources

    Learn more about SafeSnap here:

    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:

    • 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 , so refer to that section for more information.

    When calling this strategy, params should be:

    userParams should contain the indexed strategies that the author has power with:

    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 .

    Mobile notifications

    You can now subscribe to mobile notifications from the Spaces you follow on Snapshot!

    At the time of writing this, the feature is enabled in Coinbase Wallet and Converse.

    How do I receive notifications?

    It's simpler than you think:

    1. Join (follow) a Space you would like to receive notifications for

    1. Click the Alert bell icon to turn the notifications on

    1. Open and and log in with the account you used on Snapshot.

    2. Start receiving notifications from chat.snapshot.eth!

    Managing notifications

    You can easily toggle, start, or stop notifications by sending a message in the chat app:

    Lighthouse

    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.

    Galxe

    Learn what Galxe plugin is and how to add it to your space.

    Galxe is building a protocol that powers on-chain credentials with plug-and-play NFT modules. The permissionless infrastructure allows everyone to create, distribute, and gamify NFTs with customized on-chain data.

    With the Galxe plugin users can easily create a campaign and integrate it into Snapshot. This solution will incentivize communities to participate in the proposal vote by rewarding them with OATs (On-Chain Achievement Tokens). After users have voted, they can claim an OAT directly from the Project Galaxy Plugin section on the proposal page on Snapshot.

    Setup

    1. Go to your settings

    In order to add a plugin head to your space settings by clicking Settings on your space's page:

    2. Create a new proposal

    Create a new proposal which you would like to link to the Galxe plugin. You don't need to specify anything related to Galxe yet.

    Once the proposal is created, copy the proposal ID from the address bar in your browser as you will need it in the next step.

    3. Add the plugin

    2. Scroll down to the Plugins section and click Add plugin. Select the plugin you want to add in the pop-up window:

    Add the proposal ID and the Galxe Campaign information which you would like to link with the proposal.

    Make sure to replace <Proposal ID> with your proposal ID which you copied in the previous step.

    Then replace the <Space Name/campaign/Campaign ID> with the details of your space and campaign.

    Example:

    If you have multiple proposals that distribute OATs to voters, you can also add multiple proposalID:campaignInfo pairs at once in the following format:

    If you delete a proposalID-campaignInfo pair, users won’t see the OAT information on the page of your proposal even if the proposal has ended or the OATs have already been distributed.

    4. Confirm and save space settings

    Click Add and scroll up to the top of the page to click Save to persist the updated settings.

    That's it, your users will be able to see the Galxe plugin on the proposal's page!

    You can edit the configuration of your plugin in order to add new proposals to the list. Simply go to settings and click the Galxe plugin to edit the JSON with details. Don't forget to save your settings!

    Domino notifications

    Learn what Domino plugin is and how to add it to your space.

    Domino is a crypto notification system which makes it simple for DAOs to monitor and build automated workflows based on governance data. The Domino plugin allows you to alert space members about new and ended proposals on Snapshot.

    Setup & how it works

    1. Go to your space settings

    In order to add a plugin head to your space settings by clicking Settings on your space's page:

    2. Add the Domino plugin

    Scroll down to the Plugins section and click Add plugin. Select the plugin you want to add in the pop-up window:

    3. Save your settings

    Scroll up to the top of the page and click Save to update your space's settings. The plugin will affect all the newly created proposals.

    4. Create a proposal

    Create a new proposal for your space. You don't need to set anything up for the Domino notifications, they will be enabled automatically.

    5. Get notified

    Users can now head to the proposal's page and click any automation to receive notifications about the proposal.

    As a next step users will be redirected to Dominos's in order to set up their custom notification rules:

    That's it! You're all set up and users can receive direct notifications about the Snapshot proposals 🎉

    Create a space

    Learn how to create a new space for your organization.

    Head to the and follow the below steps:

    1. Organization's details

    Provide the profile information like name, description, social links or handles and treasury.

    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.

    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 , 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 :

    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.

    oSnap

    Combine Safe with Snapshot using oSnap.

    oSnap is being deprecated, and support for the product will end on December 15, 2025. To ensure continued functionality, please follow the migration guide here:

    “oSnap” is short for Optimistic Snapshot Execution. oSnap lets DAOs propose transactions, do an off-chain governance vote, and have the transaction data submitted in a trustless fashion.

    Boost

    Boost is a powerful way to incentivize voting on proposals by rewarding active participation or specific actions.

    Be aware that we are currently in the beta testing phase, and the contracts have not yet been audited. This means there's an increased risk of undiscovered bugs. Participate at your own risk and only commit tokens that you can afford to have locked or potentially at risk.

    How does it work?

    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:

    • .

    • .

    • .

    {
      "address": "0xb47e3cd837dDF8e4c57F05d70Ab865de6e193BBB",
      "symbol": "PUNK"
    }
    {
      "address": "0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d",
      "symbol": "LAND",
      "multiplier": 100
    }
    {
      "address": "0x22C1f6050E56d2876009903609a2cC3fEf83B415",
      "symbol": "POAP",
      "tokenIds": ["613607", "613237"]
    }
    {
      "address": "0x30cDAc3871c41a63767247C8D1a2dE59f5714e78",
      "symbol": "Reaper(s)",
      "tokenIds": ["2112", "2871", "3221", "3587"]
    }
    {
      "address": "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d",
      "symbol": "BAYC",
      "defaultWeight": 0,
      "tokenIdWeightRanges": [
        { "start": 0, "end": 1000, "weight": 2 },
        { "start": 1001, "end": 6000, "weight": 1 }
      ]
    }
    {
      "symbol": "POH (delegated)",
      "delegationSpace": "poh.eth",
      "delegationNetwork": "1",
      "strategies": [
        {
          "name": "erc20-balance-of",
          "params": {
            "address": "0x1dAD862095d40d43c2109370121cf087632874dB",
            "decimals": 0
          }
        }
      ]
    }
    {
      "symbol": "YFI (delegated)",
      "delegationSpace": "yam.eth",
      "strategies": [
        {
          "name": "erc20-balance-of",
          "params": {
            "address": "0xBa37B002AbaFDd8E89a1995dA52740bbC013D992",
            "symbol": "YFI",
            "decimals": 18
          }
        }
      ]
    }son
    {
      "symbol": "ABC",
      "address": "0xE18a32192ED95b0FE9D70D19e5025f103475d7BA",
      "tokenId": "0x8000000000000000000000000000000200000000000000000000000000000000",
      "decimals": 0
    }
    {
      "address": "0x61fcE80D72363B731425c3A2A46a1A5fed9814B2",
      "symbol": "CYBORG"
    }
    {
      "symbol": "ABC",
      "address": "0x2939b94BDc377e66A377cfc15028DF3Bd6aC6C28",
      "ids": [
        "59",
        "352"
      ]
    }
    {
      "address": "0x2C56b43983Ca77cc29b27B4a731F0f0d54ae7e52",
      "tokenId": 1,
      "symbol": "DAWN",
      "decimals": 0,
      "multiplier": 5
    }
    {
      "symbol": "ABC",
      "address": "0x28472a58A490c5e09A238847F66A68a47cC76f0f",
      "ids": [
        "0",
        "1"
      ],
      "weight": 10
    }
    npm run test --validation=<VALIDATION NAME> // replace <VALIDATION NAME>
    function cancel(uint256 proposalId) external;
    function validate(address author, bytes calldata params, bytes calldata userParams) external returns (bool);
    Creating a boost is a straightforward process, here's what happens when you set up a boost:
    1. You decide on the number of tokens you wish to commit as a reward and define the distribution criteria.

    2. Upon creation, your boost becomes visible to all voters on the proposal page, complete with its criteria, so voters know what incentives are in play.

    3. As the creator of the boost, you will receive an NFT from the Boost contract after setting up the boost. This NFT represents your ownership and is essentially your claim ticket for retrieving any unclaimed tokens once the claiming period ends. The retrieval of remaining tokens can be done directly from the proposal page.

    4. After the voting on the proposal voting period ends, the claiming period is initiated. This period lasts for two weeks, during which voters can claim their rewards. The length of this period is enforced by us to give voters enough time to claim their rewards.

    Distribution types

    When setting up a boost, you have the flexibility to choose how rewards are distributed to voters. Two of the primary distribution types are "Lottery" and "Weighted".

    Lottery

    The lottery system randomly selects winners, where the chances to win are based on voting power. You can set the number of winners who will share the tokens equally, and to level the playing field, implement a cap on voting power. This cap means that beyond a certain point, additional voting power doesn't increase one's chances in the lottery, giving everyone a more fair shot at the reward.

    The randomness generator we use is the `ChaCha20Rng` randomness generator, fed with a specific seed. This seed is the sha256 hash of the RANDAO reveal of the finalized epoch corresponding to the end timestamp of the proposal.

    Weighted

    This method allows you to reward voters proportionally to their voting power. You can also set a maximum reward limit per voter to ensure a more equitable distribution and prevent any single voter from receiving a disproportionately large share of the rewards. This can help maintain balance and fairness in the incentive system, especially in spaces where voting power varies significantly between members.

    Benefits of Boost

    Boosting has a twofold benefit: increasing participation and allowing for strategic incentivization.

    Increasing participation

    By boosting a proposal, you directly contribute to the governance process, encouraging others to take part in important decisions. Your influence can lead to higher voter turnout and a more robust decision-making process.

    Strategic incentivization

    Boosting allows you to incentivize voting in a way that aligns with your interests. This strategic layer adds depth to the voting process, as participants can receive additional rewards by voting a certain way.

    If strategic incentivization is not be in line with your space's goals it can be disabled by an admin at space level.

    Fee structure

    Our platform incorporates a dual-fee system designed to sustain the ecosystem and prevent misuse. While both fees are currently set to zero during our closed beta, they are structured as follows:

    Boost creation fee

    A fixed fee in ETH can be added for Boost creation. Currently, this fee is set to 0 ETH.

    Token fee

    The protocol includes a fee mechanism that can be configured as a percentage of the reward tokens. This fee is paid using the same ERC-20 token committed as the reward and is designed to support the platform’s sustainability. Currently, the token fee is set to 0%. If the fee is enabled in the future, it will be clearly displayed in the interface during the Boost creation process.

    FAQs

    Can I cancel my boost and retrieve my tokens early?

    No, once a boost is created, the tokens are locked until the end of the claiming period.

    What happens if the proposal I boosted doesn't pass?

    The boost still fulfills its purpose by incentivizing voting and participation, regardless of the proposal's outcome.

    Are there any additional rewards for creating a boost?

    There are currently no direct rewards for boost creators, but it might help increase your reputation score in the future.

    How is the effectiveness of a boost measured?

    Effectiveness can be gauged by the increase in participation on the proposals you've boosted compared to similar proposals without a boost.

    What safeguards are in place to prevent abuse of the boosting system?

    We're actively monitoring the use of boosts and will iterate on the rules and mechanisms to prevent any form of abuse as we learn from the beta phase. There are some potential risks with Strategic incentivization, so you might want to disable this feature in your space settings.

    voting systems
    , without any further human action needed.

    User guides

    Non-technical documentation for the platform.

    Go here if you want to create a space, proposal or cast a vote.

    Protocol

    Technical documentation for Snapshot X protocol.

    Go here if you want to understand the protocol architecture.

    Services

    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.

    Snapshot
    EVM implementation
    Cairo implementation
    this dedicated page
    Discord
    Download
    Cairo Security Clan
    Download
    https://snapshot.org
    Create a voting strategy
    erc20-balance-of
    Example of setting up an erc20-balance-of strategy.
    space settings
    Coinbase Wallet
    Converse
    Lighthouse
    webpage
    Create a voting strategy
    Herodotus' Facts Registry
    Open Zeppelin's UUPS guide
    Voting strategies
    here
    Proposal status

    Every execution strategy should have a public view function getProposalStatus with the following interface:

    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:

    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

    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

    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 on-chain transactions for 'non-controversial' proposals.

    Emergency Quorum

    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)

    An execution strategy that allows proposals to execute transactions from a specified target Avatar 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:

    Timelock

    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:

    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.

    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

    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:

    When calling this strategy, params should be:

    userParams is not needed.

    OpenZeppelin Style Checkpoint Token

    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:

    When calling this strategy, params should be:

    userParams is not needed.

    Whitelist

    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:

    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.

    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 on-chain governance. The interface of a strategy can be found here.

    Turing Complete Governance
    interface
    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;
    }
        /// @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));
    function upgradeTo(address newImplementation) external; 
    
    function upgradeToAndCall(address newImplementation, bytes memory data) external;
    function transferOwnership(address newOwner) external;
    
    function renounceOwnership() external; 
    uint256 proposalThreshold = ...
    Strategy[] allowedStrategies ...
    params = abi.encode(proposalThreshold, allowedStrategies);
    IndexedStrategy[] userStrategies = ...
    userParams = abi.encode(userStrategies);
    "oats": {
    	"0x554ca2bd7d979e8b72c6ae6415946a7bb470da9f60a9cf931205f083c03632a3": "jokey/campaign/GCixQUUqfE"
    }
    {
    	"oats": {
    		"<proposal ID 1>": "<Space Name>/campaign/<Campaign ID>",
    		"<proposal ID 2>": "<Space Name>/campaign/<Campaign ID>",
    		"<proposal ID 3>": "<Space Name>/campaign/<Campaign ID>",
    	}
    }
    function getProposalStatus(
        Proposal memory proposal,
        uint256 votesFor,
        uint256 votesAgainst,
        uint256 votesAbstain
    ) external view returns (ProposalStatus);
    enum ProposalStatus {
        VotingDelay,
        VotingPeriod,
        VotingPeriodAccepted,
        Accepted,
        Executed,
        Rejected,
        Cancelled
    }
    struct MetaTransaction {
        address to;
        uint256 value;
        bytes data;
        Enum.Operation operation;
        uint256 salt;
    }
    struct MetaTransaction {
        address to;
        uint256 value;
        bytes data;
        Enum.Operation operation;
        uint256 salt;
    }
    function getVotingPower(
        uint32 blockNumber,
        address voterAddress,
        bytes calldata params,
        bytes calldata userParams
    ) external returns (uint256);
    function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96);
    params = abi.encodePacked(tokenAddress)
    function getPastVotes(address account, uint256 blockNumber) external view returns (uint256);
    params = abi.encodePacked(tokenAddress)
    struct Member {
        address addr;
        uint256 vp;
    }
    Member[] members = ...
    params = abi.encode(members)
    for questions
    https://docs.kleros.io/integrations/types-of-integrations/1.-dispute-resolution-integration-plan/channel-partners/kleros-reality-module
    https://github.com/gnosis/zodiac-module-reality/blob/main/docs/setup_guide.md
    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 https://github.com/snapshot-labs/snapshot-docs/blob/master/snapshot-x/user-guides/broken-reference/README.md 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 https://github.com/snapshot-labs/snapshot-docs/blob/master/snapshot-x/user-guides/broken-reference/README.mdto 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 https://github.com/snapshot-labs/snapshot-docs/blob/master/snapshot-x/user-guides/broken-reference/README.md 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 https://github.com/snapshot-labs/snapshot-docs/blob/master/snapshot-x/user-guides/broken-reference/README.md to learn more.

    When selecting the execution strategy you have to provide a quorum - 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

    -> executes transactions on an Avatar contract

    -> uses simple quorum

    Avatar contract is any contract which implements the IAvatarinterface. 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: https://github.com/snapshot-labs/snapshot-docs/blob/master/snapshot-x/user-guides/broken-reference/README.md

    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

    And that's it! 🎉

    Create space page
    Instructions for using oSnap

    Setting up the oSnap module:

    • Create a Safe and Snapshot Space, or connect to your current accounts.

    • Go to Safe Apps, install the Zodiac app, and install the oSnap module through Zodiac.

    • Set the proposal bond, challenge period, and Snapshot Space.

    • Link the oSnap module to your Snapshot Space with SafeSnap.

    • Your oSnap module address is added to the SafeSnap plugin configuration to enforce the results of proposals on-chain.

    Using oSnap with Snapshot:

    • Create a proposal and Snapshot vote, along with the transactions to execute if the proposal passes.

    • Invite the community to vote on the proposal.

    • Once the Snapshot voting period ends, anyone can propose the transactions by posting a bond.

    • After the challenge period, execute the transactions on-chain through the Snapshot interface.

    Tutorials

    Video tutorial on how to deploy and use an oSnap module:

    Follow the oSnap documentation on how to set up an oSnap module for your project.

    Deployment tutorial using the Zodiac module:

    Snapshot tutorial to configure and execute transactions with Snapshot proposals:

    An overview on verifying and disputing proposed transactions:

    Updating the oSnap using admin functions:

    Security recommendations

    In order to ensure maximal safety we recommend to define several paremeters for your oSnap setup:

    • Set a substantial bond for an answer

    • Select a long challenge period

    • Set up a monitoring infrastructure for proposed transactions

    Additional resources

    Learn more about oSnap here:

    https://help.snapshot.box/en/articles/12886075-osnap-deprecation-migration-guide
  • How a proposal passes and how it's being executed.

  • 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, Proposal validation strategies, Voting strategies, and 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.

    If you are planning to integrate Snapshot X into your own product, head to SX.js section to learn about our SDK.

    Who can create a proposal
    How users are authenticated
    How the voting power is determined
    @SnapshotLabs
    Sub-spaces
    Voting strategies
    ticket
    Space roles
    Validation strategies
    technical overview.
    Add a custom domain
    Add a skin
    Plugins
    Gnosis Safe documentation

    Liquidity / staking pool

    Include tokens added in liquidity pools or staking contracts into Voting Power calculation besides those held in users' wallets.

    At the moment, Snapshot already supports calculating the Voting Power of the underlying token in the LP/staking contract from the most popular AMMs like Uniswap v2 and v3, Balancer, or SushiSwap.

    This page only goes through popular strategies as projects may have a different method to call deposited balance in their contracts, which is often customized for a specific use case.

    By enabling such a strategy in your space, you can allow the proposal to capture more users interested in your governance.

    Some spaces allow only users holding tokens in liquidity/staking pools to have Voting Power.

    The strategy fetches the balance of the input token address in all Uniswap-v2 liquidity pools.

    This allows uni-v2 LP token holders to vote based on the underlying token cumulated in each pool.

    For instance, users who have 1 DAI in the DAI-USDC pool, and 2 DAI in the DAI-ETH pool, will have 3 Voting Power.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    Many platforms offer decent yields on one's Uniswap LP tokens.

    By staking in their farms, users transfer their LP tokens to the contract they are interacting with (staking contract) and then can earn rewards from the farm they have joined.

    The specific type of LP that can be farmed depends on the individual platforms.

    The staked-uniswap strategy allows you to get token balance of an LP in a staking contract.

    Strategy setup:

    • tokenAddress: the underlying token you want to use to calculate Voting Power

    • uniswapAddress: the Uniswap LP address where users can deposit their token

    • stakingAddress: the staking contract address where users stake their LP token

    You can test it out in the Playground on Snapshot:

    In comparison with , Uniswap v3 provides users with the most flexibility and granular control over personal assets by introducing features like concentrated liquidity and multiple LP fee tiers per pair — 0.05%, 0.30%, and 1.00%.

    In this context, the USDC / ETH LPs with 0.3% and 0.05% fees represent different contract addresses (poolAddress) respectively.

    The demo illustrates how to get USDC balance in the USDC / ETH 0.3% liquidity pool. If you want to calculate the other token in the pool, you can change the paramtokenReserve to 1.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    LP tokens provided in SushiSwap are called SLP tokens, and the SLP tokens can be staked into the farm (MasterChef LP Staking Pool) to earn rewards.

    This strategy can return the balance of the underlying token in SushiSwap's LP pools and farms as well.

    Strategy setup:

    • address - the underlying token address

    • useStakedBalances - if true it will also return the token balances from the MasterChef LP Staking Pool

    • masterchefVersion - if v2 it will return the token balances from the MasterChef V2 staking contract instead of MasterChef V1.

    You can test it out in the Playground on Snapshot:

    The MasterChef contract has been developed by many projects based on SushiSwap’s original contract like . With MasterChef, users can stake their tokens and in exchange they will get a token reward.

    The strategy is more generic and it gets the balance of the LP token or the underlying base token in the LP from a MasterChef staking pool.

    Strategy setup:

    • chefAddress: Masterchef contract address

    • pid: masterchef pool id (starting with zero)

    • uniPairAddress: address of a Uniswap pair (or a sushi pair or any other with the same interface)

    You can test it out in the Playground on Snapshot:

    A lot of use cases are beyond regular LP/staking scope, for example calculating the balance of staked NFT or from a custom contract.

    The good news is, if there's a method in your contract that reads the balance of the staked token with a single call (interaction with the Smart Contract), you don't need to create a new strategy.

    Instead, use contract-call strategy, which allows any contract method to be used to calculate voter scores.

    As this is a more advanced strategy, don't hesitate to reach out to our support team on !

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    Delegation

    Discover the delegates of specific spaces and delegate your Voting Power directly through Snapshot,

    How to delegate?

    Snapshot enables a couple of ways to delegate your Voting Power to another address (a delegate).

    You can delegate your Voting Power via:

    👉 Delegates Registry per Space (if the Space has set up their custom delegation contract)

    This is a great solution for those who are not sure who to delegate their VP to.

    👉

    The quickest solution to delegate the Voting Power to a known address.

    👉

    If you prefer to skip the interface and interact with the Contract directly.

    Let's look at each option in detail.

    Delegates registry

    This page only applies to custom delegation contracts. To see the Snapshot native delegation, head to:

    https://snapshot.org/#/delegate/space-name.eth

    It is possible to discover the Delegate registry of Spaces that provided their custom delegation contract in settings.

    Head to the Space page and click Delegates in the left sidebar**:**

    You will then see a list of delegates for the Space with the number of their delegators and their total Voting Power within the Space.

    You can delegate your Voting Power to one of the delegates directly by clicking the Delegate button:

    Delegate page

    1. Go to

    2. Enter the address you want to delegate to.

    3. To limit the delegation to a specific space, tap the on switch button and enter the space key (example: balancer.eth) you want your delegation to take effect on. If no space is selected, the effect will take place for all spaces.

    4. Click confirm to save your delegation.

    The direct delegation to a chosen space has priority over the all spaces delegation. What does it mean? 👍 Address A delegates to B for all spaces, and A delegates to C for a chosen space. ◀️ The chosen space uses overriding delegation strategy. 🤚 B votes first - their VP is taken into account. 🖐️ C votes - their vote has priority (direct single space delegation) and erases B's Voting power (all spaces delegation). ✋A votes - B and C's Voting Power decreased to 0.

    Smart contract interaction

    You need to call the setDelegate method with the space id as the first argument (space id is its ENS domain name, for example fabien.eth), and the address of the delegate as the second argument.

    Here is an example of integration in a Solidity contract:

    Supported networks

    • Mainnet

    • Sepolia

    • Optimism

    • Arbitrum

    Delegation contract

    Snapshot uses the Gnosis "Delegate Registry" contract here:

    The contract is deployed on this address: The contract is also available on "Supported networks" listed above.

    Delegations are stored on this subgraph:

    A delegation Voting Strategy must be added to the Snapshot space before delegated votes will be counted. You can use the strategy.

    POAP

    Learn what the POAP plugin is and how to add it to your space.

    POAP is a badge which can be acquired by attending events or experiences. In technical terms - a Proof Of Attendance Protocol NFT (non-fungible asset).

    With the POAP plugin you can reward voters in your space with a unique POAP for each vote they cast on a Snapshot proposal and as a result grow the overall governance participation of your community.

    Setup

    1. Open your space settings on Snapshot

    Ensure that you have the permissions required to add new plugins, or reach out to the admins of the space you'd like to add the plugin to.

    Scroll down to Plugins and click the Add plugin button.

    2. Select the POAP module

    A new popup window will show up where you can search for and select the POAP Module by simply clicking on it.

    No need for additional settings on this step; leave it as-is and click "Add."

    Don't forget to save your settings! Go to the top of the page and click Save. Confirm the changes by signing the message in your wallet.

    3. Create a new proposal

    Now when you create a new proposal, the POAP plugin will be automatically added. There is no need to manually add it to each new proposal.

    Once the proposal has been created, you can get your Snapshot proposal ID from the URL of the proposal. Copy all characters following the proposal/ in the address box in the browser and save them somewhere - you will need it to link the proposal to your POAP drop.

    4. Create a POAP drop

    Follow the instructions in POAP's guide but be sure to request "0" mint links.

    Take note of your Drop ID, as you will need this for the next steps.

    5. Link your proposal to your POAP drop

    Reach out to the POAP team by filling out the required fields in the . Do not post your request anywhere else.

    Your petition will be reviewed by the POAP Curation Body after the vote is finished. If the petition is approved, every voter will be able to mint their special POAP.

    How to check the status of your request?

    If you want to check on the status of your request, you should do so by using the chat bubble:

    • Click the chat bubble in the lower-right corner of the .

    • Type “check on my petition”

    • Enter your drop ID.

    • Get a response!

    If curation needs to contact you, they will do so via the email [email protected]. If customer support needs to contact you, they will do so via the email [email protected]. Beware of scams and never reply to other email addresses.

    Support

    Join POAP Discord: Snapshot Help Center:

    \

    Sybil resistance, scam & spam prevention

    How to avoid scams, spam and make sure that your proposal authors and voters are genuine humans? How can you take into account users' on-chain reputation? Read on!

    A Sybil attack is a type of attack on a computer in which an attacker subverts the service's reputation system by creating a large number of identities and uses them to gain a disproportionately large influence.\

    source:

    In the crypto world, attacks, hacks, and scams are unfortunately all too common. Phishing links can be found everywhere - on Twitter, in seemingly harmless blog posts, and in personal emails. We’ve not been immune to them either! As a popular tool for decentralized decision-making, is an attractive target for scammers looking to manipulate governance proposals for their own gain.

    Most common

    Learn about the most popular configurations.

    Before we jump into specific use cases, let's have a look at the most common configurations that the majority of spaces use on Snapshot.

    Voting strategy + Proposal validation strategy

    The minimum setup required to run your governance on Snapshot consists of:

    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.

    Create a proposal

    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.

    A specific (single choice, weighted, quadratic, and others) can be selected individually for each proposal.

    Voting power for each user is calculated on the basis of the voting strategies selected in the .

    https://snapshot.org/#/playground/poap?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiUE9BUCIsImV2ZW50SWRzIjpbIjEyMTMiLCIxMjkzIl19LCJuZXR3b3JrIjoiMTAwIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHg4MzdkMjFjZmRhNzFlOTNlNTI1N2Y5NWNlMmM0OTc1MTY3NWViY2IxIiwiMHgwMGFjMzZjNTE1MDBlOTAwYWIwZjRlNjkyZmMxMzM4Y2Y3MDU3MWIyIiwiMHhkZDZmNzAyYzI5MDdjZTQwMTg4OGQ5OTNkN2RjMTg1ZTdhODI0NDY2Il19snapshot.org
    GitHub - snapshot-labs/score-api: Scoring API for Snapshot strategiesGitHub

    Polygon

  • BNB Chain

  • Gnosis Chain

  • Fantom

  • Base

  • Base sepolia

  • Linea

  • Blast

  • Sonic

  • Mantle

  • General Snapshot's delegation page
    Smart Contract
    https://snapshot.org/#/delegate
    https://github.com/convex-eth/platform/blob/d3061c19b5e01a4e562c8121b08c44f1b42f0b85/contracts/contracts/BasicCvxHolder.sol#L49-L53
    https://github.com/gnosis/delegate-registry
    0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446
    https://thegraph.com/explorer/subgraph/snapshot-labs/snapshot
    with-delegation
    Logo
    Validation strategy example - Gitcoin Passport
    How to use validation strategies:

    if uniPairAddress is null, returns staked LP token balance as is

  • if the uniPairAddress option is provided, converts staked LP token balance to base token balance (based on the pair total supply and base token reserve)

  • tokenIndex: index of a token in LP pair, by default 0, can switch to 1 for another base token

  • weight: integer multiplier of the result (for combining strategies with different weights | optional)

  • weightDecimals: integer divisor of the result (1 gives a decimal before the result | optional)

  • uniswap
    staked-uniswap
    uniswap-v3
    v2
    sushiswap
    masterchef-pool-balance
    pancake's syrup pool
    contract-call
    Help Center
    How to make your Space more Sybil resistant?

    There are multiple ways which can help you minimize the risk of scams, bots and spam behaviors within your space. We will look into:

    • Space roles

    • Validation Strategies (use for Proposal Creation and Voting)

    • Voting Strategies (use for Voting Validation)

    1. Space roles

    Let’s first recap what are the different permissions of the roles defined in the Members tab of the Space settings:

    • Controller - in full control of the space, the only role able to change the Controller of the space.

    • Admin - able to modify the space settings (apart from the list of Admins), manage the space’s proposals and create proposals.

    • Moderator - able to manage the space’s proposals and create proposals.

    • Author - able to create proposals without having to go through proposal validation.

    Moderators

    Even though we provide several automated mechanisms to minimise the risk of harmful proposals it is still important to have to ability to review the created proposals by an actual person.

    The Moderator role enables just that - without having the access to space settings moderators can hide proposals. It is a great way to have more organization members involved in keeping the governance safe without requiring Admins to be available at all times.

    Discord Bot

    A great addition to the Moderators is a notification system which will inform you and your community about newly created proposals. If your organization uses Discord, you can easily activate our Discord Bot on your server.

    To do so, invite the bot with this link.

    Then type / to see the commands (require administrator role):

    Voilà! Now you and your Moderators can keep an eye on the notifications and react much quicker when a scam proposal has hit your space.

    Authors

    It is also possible to whitelist accounts which will be allowed to create new proposals regardless of the chosen Validation Strategy (more on the strategies in the next section).

    Once added as Authors in the Members tab in the space settings they will surpass the validation process and will be able to create new proposals in the space.

    Authors only mode

    If you wish to limit proposal creators to Admins, Moderators and Authors only, you can do so by enabling the Authors only setting in the Proposal tab in the space settings. Make sure to give the Author role to the users you trust!

    2. Validation Strategies

    In technical terms a Validation Strategy is a JavaScript function that returns a boolean (true or false) for the connected account to define if someone is eligible to create a new proposal or cast a vote.

    Each space can use one Proposal and one Voting Validation for all of its proposals at a time.

    Validation strategy can check both for monetary and non-financial assets of the user like POAPs, Gitcoin Passport stamps.

    All spaces are now required to use Proposal Validation in order to minimize the risk of malicious proposals.

    Basic Validation

    The Basic Validation Strategy allows you to specify multiple Voting Strategies to determine if a user is eligible to create a proposal.

    Voting Strategy is a set of conditions used to calculate user's voting power. Strategies enable Snapshot to calculate the final result of voting on a given proposal.

    When setting the Validation Strategy up it’s important to keep in mind that it is meant to make it difficult for users outside of your community to post scam proposals.

    Therefore make sure to use a high threshold, for example $100 worth of your organization’s token. A good idea would be to check the holdings of previous proposal creators, both legitimate and scammers, to assess a reasonable value.

    In case the threshold you’ve set is too high for some of your community members, don’t forget that you can always add the trusted addresses as Authors, thanks to which they will surpass the Proposal Validation stage.

    Below you can see an example of the Basic Proposal Validation using Voting Strategies set for the space:

    If you want to set up a more complex validation, you can use custom strategies as shown on the screenshot below:

    Gitcoin Passport Validation

    While Basic Validation focuses on the monetary assets, this validation allows you to set requirements protecting your space against Sybil attacks by checking the Gitcoin Passport stamps which serve as validation for user’s identity and online reputation.

    You can select individual or multiple stamps that matter for your space. You can also decide if they need to meet all of these criteria or only one. The more criteria you select, the more sybil resistant your space is.

    3. Voting Strategies

    membership

    Useful when combined with Quadratic Voting, which provides a certain level of Sybil resistance.

    The strategy combines any arbitrary Voting Strategy defining a membership with any Voting Strategy calculating the Voting Power of a user. In the idea one can only vote if he passes the membership strategy, the membership here can be an identity symbol like possessing a UID or a PUNK (with an erc721 strategy) or any other custom verification strategy based on on-chain behavior.

    The membership portion is binary. If the membership strategy returns any number > 0 for an address, then user is considered a member and their Voting Power will be the result of the votingPowerStrategy.

    Otherwise voting power for that address will be 0.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    whitelist

    Assuming the wallet address has been verified to prove its identity by the Space admins the simplest way to implement is to use whitelist strategy which only allows addresses passed into the param to vote on proposals, which is 1 vote 1 address.

    The wallet address can also be its corresponding ENS domain.

    To assign arbitrary votes to a specific address, see whitelist weighted.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    network service
    pseudonymous
    https://en.wikipedia.org/wiki/Sybil_attack
    Snapshot.org
    Selecting at least one Voting strategy
  • Choosing a Validation strategy for proposal creation

  • To recap what Voting and Validation Strategies are:

    ⚡ Validation strategies are a way to define who is allowed to vote on a proposal or create a new one.

    ⚡ Voting strategies calculate how much Voting power each user has.

    Most common Voting strategies

    Token based

    erc20-balance-of

    #1 Voting strategy on Snapshot used by over 8 thousand Spaces.

    This strategy returns the balance for a specific ERC20 token that user holds in their wallet.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    eth-balance

    This strategy returns the balance of the user's ETH holdings as their Voting power.

    Strategy setup:

    You only need to provide the ETH symbol:

    You can test it out in the Playground on Snapshot:

    erc721

    This strategy is designed for NFT holdings and is similar to erc20-balance-of.

    This strategy returns the balance for a specific ERC721 NFT as the user's Voting Power.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    eth-with-balance

    eth-with-balance in its simplest way to assign 1 vote to wallets holding any balance of ETH. Regardless of how big the balance is, the VP is always the same and equal to 1 VP. If the wallet doesn't hold any ETH, the VP is 0.

    This allows you to poll your community without referencing the number of ETH they hold, each address will have 1VP. If you want to use this approach for another token, have a look at the erc20-with-balance strategy.

    You can also use this strategy to set a voting threshold by adding an optional parameter minBalance and defining the minimum required balance which will give the user the eligibility to vote and 1 Voting Power. The parameter value is set to 0 by default.

    Using a low minimum balance, this strategy can be used as a proxy for "active Ethereum address", based on the assumption that active addresses will always have some ETH to pay the fees.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    Other

    whitelist

    Another very popular strategy that gives you control over who can cast the votes. All you need to do is to provide the addresses which should be eligible to vote, and each will have 1 Voting Power.

    The wallet address can also be its corresponding ENS domain.

    To assign arbitrary votes to a specific address, see whitelist weighted.

    Note that if you add other Strategies to your space and users meet the criteria set by those Strategies, the whitelist approach will not work anymore. To be able to combine multiple Voting strategies for whitelisted users, use the Basic Validation strategy in a custom setup with the whitelist strategy selected, and a minimum score of 1 VP.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    Validation strategies

    ⚡ Validation strategies are a way to define who is allowed to vote on a proposal or create a new one.

    All spaces are now required to use Proposal Validation in order to minimize the risk of malicious proposals. You can read more about this requirement here.

    You can also configure a Validation strategy for voting. The combination of Voting Strategies and Voting validation allows you to achieve high customization of your governance process. How? Let's go through a couple of possible scenarios:

    • You are using several Voting strategies for the Voting Power calculation, and at the same time, you want to require each user to have at least 10 VP to be able to cast a vote. You can then use Basic Validation with a minimum score of 10.

    • Only specific users should be able to vote, and their Voting Power should depend on their holdings. You can use whitelist strategy in the custom Basic Validation setup, and any combination of Voting Strategies for the calculation of the individual Voting power.

    • Your space is attacked by bots and you want only genuine humans to cast votes. You can use the Gitcoin Validation for Sybil resistance, and select a combination of Voting Strategies for VP calculation.

    Let's have a look at the currently available strategies:

    Basic Validation

    The Basic Validation Strategy allows you to specify multiple Voting strategies to determine if a user is eligible to create a proposal.

    Voting Strategy is a set of conditions used to calculate user's voting power. Strategies enable Snapshot to calculate the final result of voting on a given proposal.

    When setting the Validation strategy up it’s important to keep in mind that it is meant to make it difficult for users outside of your community to post scam proposals.

    Therefore make sure to use a high threshold, for example, $100 worth of your organization’s token. A good idea would be to check the holdings of previous proposal creators, both legitimate and scammers, to assess a reasonable value.

    In case the threshold you’ve set is too high for some of your community members, don’t forget that you can always add the trusted addresses as Authors, thanks to which they will surpass the Proposal Validation stage.

    Below you can see an example of the Basic Proposal Validation using Voting Strategies set for the space:

    If you want to set up a more complex validation, you can use custom strategies as shown in the screenshot below:

    Gitcoin Passport Validation

    While Basic Validation focuses on the monetary assets, this validation allows you to set requirements protecting your space against Sybil attacks by checking the Gitcoin Passport stamps which serve as validation for the user’s identity and online reputation.

    You can select individual or multiple stamps that matter for your space. You can also decide if they need to meet all of these criteria or only one. The more criteria you select, the more Sybil-resistant your space is.

    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

    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.

    API Key Request Form

    2. Wait 72 hours

    We will review your submission and whitelist the address you provided in the form.

    After 72 hours have passed you can continue with the next steps. If you get any errors while generating the key, please contact our support on Help Center.

    3. Generate the API Key

    After your address has been whitelisted go to https://app.mycrypto.com/sign-message and connect your wallet using the account you provided in the submission form above.

    a) Sign the message with keyword generateKey:

    Untitled

    b) Copy the signature and run the below curl command.

    Make sure to use the signature hash from step 5 in the sig param, do not paste the entire response after signing the message. As an example:

    You will receive the API Key as a response of the curl request. Make sure to store it securely.

    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:

    Alternatively, you can use the apiKey param in the query string with your key as a value:

    If you are using the GraphQL interface you need to provide your key in the headers tab:

    Who can create a proposal?

    Space controller, admins, authors, and users who are eligible according to the proposal validation strategies defined in the space settings.

    Proposal limit Each space has a limit on the number of proposals that can be created daily and monthly. For more details have a look at Proposals limitations

    Create a proposal

    1. Head to the space which you wish to create your proposal for.

    2. Connect with the wallet provider - make sure the connected wallet is where you hold the tokens relevant to the specific space.

    3. Click New proposal in space sidebar:\

    4. Fill in the following fields: - Title - Description (optional, 10K character limit) - Discussion link (optional)\

    5. Select the desired voting system, specify the possible vote options, and define the duration of your proposal. Make sure you allow enough time for users to vote.\

    6. Click Publish - and that's it! You can see your proposal in the proposals list on the space page.

    Snapshot block number

    When you create a proposal by default the snapshot block number 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 snapshot. It means that if the user acquires required tokens after the proposal has been created, they will not be taken into account for their voting power calculation.

    Proposals limitations

    • There is a character limit of 10,000 for the description of a proposal.

    • One address can have a maximum of 10 active proposals at a time, across multiple spaces.

    • Each space has a limit on the number of proposals created daily and monthly:

    Space status
    Daily proposal limit
    Monthly proposal limit

    40

    200

    20

    100

    Unverified

    3

    15

    Flagged

    1

    Learn more about Space verification and flagging in Space badges.

    • You can combine up to 8 voting strategies. The limit also applies to multi-chain strategies. (Turbo spaces can add up to 10 strategies)

    • All testnet spaces will have the same proposal limits as a Verified space

    • Can add up to 20 choices on a proposal (Turbo spaces can add up to 1000 choices)

    voting system
    space settings
    How Do I Set Up a POAP Drop?
    POAP Snapshot Proposal Request form
    POAP website
    http://poap.xyz/discord
    https://help.snapshot.org/en

    Anti-whale

    Minimize the power of large token holders and encourage your community to take part in the vote!

    What is a whale?

    A cryptocurrency whale, more commonly known as a "crypto whale" or just a "whale," is a cryptocurrency community term that refers to individuals or entities that hold large amounts of cryptocurrency. Whales own enough cryptocurrency to influence currency markets. https://www.investopedia.com/terms/b/bitcoin-whale.asp

    Whales can influence not only currency markets but also the governance process of web3 communities. With the large holdings, their sheer Voting Power can oftentimes swing a proposal for their advantage. This leads to outcomes that are not welcomed by the community, poses a threat to taking the Treasury over, and also to lowering community participation in the vote as its members don't see the point in voting knowing that one wallet can decide on its outcome.

    Let's have a look at different setups that will allow your space to minimize the power of whales.

    Voting types

    You can predefine the Voting type that all proposals in your space will use. In the case of the anti-whale approach, the best solution is the Quadratic Voting type.

    1. 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.

    You can set it up for all proposals in your Space by heading to the Voting tab in the Space settings and selecting the desired Voting System in the Type field:

    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 how John with 100 SMS tokens was not able to push through his choice despite having the majority of Voting Power in the group as Quadratic Funding model emphasizes the number of individual contributors rather than the amount contributed.

    This Voting System may encourage the whales to create multiple wallets and split their holdings among them. Therefore it's important to also implement a mechanism providing Sybil Resistance. Read more !

    Voting strategies

    1.

    You can think of it as creating multiple buckets which will determine the Voting Power of an individual address. To put it in the simplest way, imagine that:

    • users who hold less than 5 tokens will have 0 Voting Power

    • users who hold from 5 to 10 tokens will have 1 Voting Power

    • users who hold from 10 to 20 tokens will have 2 Voting Power

    As you can see, you are able to set various thresholds which will fix user's Voting Power at a specific level. This way whales who may have large holdings, for example 1000 tokens, will not get more than 5 Voting Power.

    This way the voting can be more reflective of the public opinion.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    2.

    This strategy executes a chosen Voting Strategy and applies algorithms to its result to reduce the impact of big wallets on the vote.

    This strategy requires understanding some of the calculations shown below. If you have issues with setting it up contact our support on

    In practice, the strategy sets a soft restriction on the voting threshold by giving limited incentive to the voter below threshold by moderately increasing the voting power of voters and reducing the impact of whales as token amount increases, keeping the gap in voting power within a relatively moderate range.

    As an example, assuming our threshold is 5:

    • if user's Voting Power is below or equal to 5, their final Voting Power will be moderately increased

    • if user's Voting Power is above 5, their final Voting Power will be reduced (the higher the Voting Power, the more increased it will be)

    The following algorithm is applied to the result of the configured Voting Strategy:

    Parameters

    antiWhale.threshold - point at which the antiWhale takes effect.

    Results not greater than the threshold will be treated with a static multiplier. This is to reduce infinite incentive for multiple wallet exploits.

    • Default: 1625

    • Lower limit: > 0 - set to default if ≤ 0

    thresholdMultiplier - the multiplier at which all results below antiWhale.threshold are multiplied.

    antiWhale.inflectionPoint - point at which the output matches the result.

    -> Results less than this will increase output.

    -> Results greater than the inflection point will decrease output.

    • Default: 6500

    • Lower limit: > 0 - set to default if ≤ 0

    • Must be bigger or equal to antiWhale.threshold. Otherwise will be same as antiWhale.threshold

    antiWhale.exponent - the exponent is responsible for the antiWhale effect.

    -> Must be ≤ 1, or else it will have a pro-whale effect.

    -> Must be >0, or else it will cause total voting power to trend to 0. Look at the example below to understand its effect.

    Example

    • random value = 5

    • exponent A = 0.5

    • Default: 0.5.

    • Upper limit: 1.

    • Lower limit: > 0 - set to default if ≤ 0.

    log: Boolean flag to enable or disable logging to the console (used for debugging purposes during development)

    Strategy setup:

    Space actions

    Space 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.

    To reduce deployment costs and to enable upgradability, we use ERC-1967 proxies and follow Open Zeppelin's UUPS Upgradability pattern on the space contract implementation.

    After deploying the proxy you should call the initialize function with the initial set of Space settings.

    • 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 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

    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.

    • 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 section.

    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.

    • 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 section.

    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.

    • 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

    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 theexecute method on the Space contract directly:

    • 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.

    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 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 .

    ERC-4824: Decentralized Autonomous Organizations

    The Space contract implements ERC-4824 which is a standard URI and JSON schema for DAOs, focusing on relating on-chain and off-chain representations of membership and proposals. More information can be found . The standard adds a daoURI string to the space settings which can be queried using the following interface:

    https://snapshot.org/#/playground/erc721-with-multiplier?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4Zjg3ZTMxNDkyZmFmOWE5MWIwMmVlMGRlYWFkNTBkNTFkNTZkNWQ0ZCIsInN5bWJvbCI6IkxBTkQiLCJtdWx0aXBsaWVyIjoyMDAwfSwibmV0d29yayI6IjEiLCJzbmFwc2hvdCI6IiIsImFkZHJlc3NlcyI6WyIweDRlYWM2MzI1ZTFkYmYxYWM5MDQzNGQzOTc2NmUxNjRkY2E3MTEzOWUiLCIweDFiMjA0NDI0NTYzZGNmY2FiZDJhZWU2MzIxNjNiOWU2ZGM4YmQ0ZjMiXX0.snapshot.org
    https://snapshot.org/#/playground/erc721-with-tokenid?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4MjJDMWY2MDUwRTU2ZDI4NzYwMDk5MDM2MDlhMmNDM2ZFZjgzQjQxNSIsInN5bWJvbCI6IlBPQVAiLCJ0b2tlbklkcyI6WyI2MTM2MDciLCI2MTMyMzciXX0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHgyYjg1MDc1NzAyYjViYzQ3MzdkOGUxNTYwYjdlZmU4NTM1MTA1YjQ3IiwiMHgzZTE3ZmFjOTUzZGUyY2Q3MjliMGFjZTdmNmQ0MzUzMzg3NzE3ZTllIiwiMHhFNzZCZTlDMWUxMDkxMGQ2QmM2YjYzRDgwMzE3Mjk3NDc5MTBjMmY2IiwiMHhDNWUxNTY5NzcyYjJkNDI1QWM5NDY5ZDM5RjE3MzQxQzAxZTFDRjRjIl19snapshot.org
    https://snapshot.org/#/playground/erc1155-balance-of-ids-weighted?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQkFMIiwiYWRkcmVzcyI6IjB4YmExMDAwMDA2MjVhMzc1NDQyMzk3OGE2MGM5MzE3YzU4YTQyNGUzRCIsImRlY2ltYWxzIjoxOH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHgzNDUxOGY1NTU5NDI1YTdiYjA2ZjY2MTk2OTIwYWYxMGUxOTM4YjVmIl19snapshot.org
    https://snapshot.org/#/playground/erc1155-balance-of?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQUJDIiwiYWRkcmVzcyI6IjB4RTE4YTMyMTkyRUQ5NWIwRkU5RDcwRDE5ZTUwMjVmMTAzNDc1ZDdCQSIsInRva2VuSWQiOiIweDgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAyMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJkZWNpbWFscyI6MH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHgwQjcwNTZlMkQ5MDY0ZjJlYzg2NDdGMWFlNTU2QkFjYzA2ZGE2RGI0IiwiMHhjYzVEZGM4Q0NENUIxRTkwQmM0MkY5OThlYzg2NEVhZDAwOTBBMTJCIiwiMHgwMTU0ZDI1MTIwRWQyMEE1MTZmRTQzOTkxNzAyZTc0NjNjNUE2RjZlIl19snapshot.org
    https://snapshot.org/#/playground/erc1155-with-multiplier?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4MkM1NmI0Mzk4M0NhNzdjYzI5YjI3QjRhNzMxRjBmMGQ1NGFlN2U1MiIsInRva2VuSWQiOjEsInN5bWJvbCI6IkRBV04iLCJkZWNpbWFscyI6MCwibXVsdGlwbGllciI6NX0sIm5ldHdvcmsiOiI0Iiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHg5Y0E3MEI5M0NhRTU1NzY2NDVGNUYwNjk1MjRBOUI5YzNhZWY1MDA2IiwiMHhiNWFFNTE2OUY0RDc1MGU4MDI4ODRkODFiNGY5ZUM2NmM1MjUzOTZGIl19snapshot.org
    https://snapshot.org/#/playground/erc1155-all-balances-of?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4NzFlYjVjMTc5Y2ViNjQwMTYwODUzMTQ0Y2JiOGRmNWJkMjRhYjVjYyIsInN5bWJvbCI6IkNPUkdJIn0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHgwNjY1OEZkNzAwMjNmNTI3QkZBYzFBNmQ5MTQxQzU2ZDk5YzY1MTI5Il19snapshot.org
    https://snapshot.org/#/playground/delegation?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiUE9IIChkZWxlZ2F0ZWQpIiwic3RyYXRlZ2llcyI6W3sibmFtZSI6ImVyYzIwLWJhbGFuY2Utb2YiLCJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4MWRBRDg2MjA5NWQ0MGQ0M2MyMTA5MzcwMTIxY2YwODc2MzI4NzRkQiIsImRlY2ltYWxzIjowfX1dfSwibmV0d29yayI6IjEiLCJzbmFwc2hvdCI6IiIsImFkZHJlc3NlcyI6WyIweDNjMTNmMkI1NkFGNjE0YUM2MzgxMjY1RWNCM0I2MTliQTI2Q0M2NDEiLCIweDA0OGZlZTdjMzI3OWEyNGFmMDc5MGI2YjAwMmRlZDQyYmUwMjFkMmIiLCIweDEzOWE5MDMyYTQ2YzNhZmUzNDU2ZWI1ZjBhMzUxODNiNWYxODljYWUiXX0.snapshot.org

    Validation strategies

    What is a validation strategy?

    A voting validation is a JavaScript function that returns a boolean (true or false) for the connected account. Voting validations are being used on Snapshot to decide if an account can vote or create a proposal in a specific space. Each space can use one voting validation for all of its proposals at a time. While voting strategies calculate the Voting Power mainly on the basis of the monetary assets, the validation strategy can serve as a protection against Sybil attacks. It can take into consideration how many POAPs an account owns or track the account activity to assess if the account is a bot or a real human.

    Subgraphs

    Query data with Subgraphs on The Graph.

    Snapshot’s onchain data can be easily queried with open APIs known as subgraphs. Subgraphs are decentralized APIs powered by The Graph, a protocol for indexing & querying data from blockchains.

    The Snapshot Subgraph

    You can see an interactive query playground on the , where you can test any query.

    Check out the following examples:

    Example output

    {
      "address": "0x6b175474e89094c44da98b954eedeac495271d0f",
      "symbol": "DAI"
    }
    {
      "tokenAddress": "0x6e36556b3ee5aa28def2a8ec3dae30ec2b208739",
      "uniswapAddress": "0xdf6b861b4fbcfaffb62dd1906fcd3a863955704b",
      "stakingAddress": "0xfd15657341492d1918e3a8b7421e9627d52056e9",
      "symbol": "BUILD",
      "decimals": 18
    }
    {
      "symbol": "USDC",
      "poolAddress": "0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8",
      "tokenReserve": 0
    }
    {
      "address": "0x0Ae055097C6d159879521C384F1D2123D1f195e6",
      "useStakedBalances": "true",
      "masterchefVersion": "v1"
    }
    {
      "symbol": "CHEF",
      "chefAddress": "0xD38abbAeC03a9FF287eFc9a5F0d0580E07335D1D",
      "uniPairAddress": null,
      "tokenIndex": null,
      "pid": "0",
      "weight": 1,
      "weightDecimals": 0
    }
    {
      "symbol": "vBNT",
      "address": "0x892f481BD6E9d7D26aE365211D9B45175d5D00e4",
      "decimals": 18,
      "methodABI": {
        "name": "votesOf",
        "type": "function",
        "inputs": [
          {
            "name": "_voter",
            "type": "address",
            "internalType": "address"
          }
        ],
        "outputs": [
          {
            "name": "",
            "type": "uint256",
            "internalType": "uint256"
          }
        ],
        "stateMutability": "view"
      }
    }
    {
        "membershipStrategy": {
          "name": "erc721",
            "params": {
              "address": "0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb"
            }
        },
        "votingPowerStrategy": {
          "name": "erc20-balance-of",
            "params": {
              "address": "0x6b175474e89094c44da98b954eedeac495271d0f",
              "symbol": "DAI",
              "decimals": 18,
            }
        },
        "symbol": "DAI"
      }
    {
      "symbol": "POINT",
      "addresses": [
        "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11",
        "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7"
      ]
    }
    {
      "symbol": "ETH"
    }
    {
      "symbol": "ETH",
      "minBalance": 0.5
    }
    {
      "symbol": "POINT",
      "addresses": [
        "0xa478c2975ab1ea89e8196811f51a7b7ade33eb11",
        "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7"
      ]
    }
    curl --location 'https://keycard.snapshot.org' \
    --header 'accept: application/json' \
    --header 'content-type: application/json' \
    --data '{
        "jsonrpc": "2.0",
        "method": "generate_key",
        "params": {
            "sig": "<SIGNATURE_HASH>"
        },
        "id": "123456789"
    }'
    ...
        "method": "generate_key",
        "params": {
            "sig": "0x85bcabdeb3b43131364d21b32f8c74124d155009fc9d6d40901b4b725f23e0ac632808ebb00f3569bf875ded07b61ac5163ebe757b0897278ab276cdc982e3001c"
        },
    ...
    curl 'https://hub.snapshot.org/graphql?' \
      -H 'content-type: application/json' \
      -H 'x-api-key: <YOUR-API-KEY>' \
      --data-raw '{"query":"\n{\n space(id:\"snapshot.dcl.eth\"){\n  id\n  name\n  members\n}\n}","variables":null}' \
      --compressed
    curl 'https://hub.snapshot.org/graphql?apiKey=<YOUR_API_KEY>' \
      -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

    5

    Turbo
    Verified
    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 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 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 section.

  • userProposalValidationParams: An array of parameters that will be passed to the Proposal Validation Strategy for the Space.

    metadataURI: The updated metadata URI of the proposal.

    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.

  • 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 section for more information.
    Space controller actions
    Execution strategies
    Execution strategies
    Execution strategies
    here
    here
    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;
    function propose(
        address author,
        string calldata metadataURI,
        Strategy calldata executionStrategy,
        bytes calldata userProposalValidationParams
    ) external;
    function updateProposal(
        address author,
        uint256 proposalId,
        Strategy calldata executionStrategy,
        string calldata metadataURI
    ) external;
    function vote(
        address voterAddress,
        uint256 proposalId,
        Choice choice,
        IndexedStrategy[] calldata userVotingStrategies,
        string calldata metadataURI
    ) external;
    function execute(uint256 proposalId, bytes calldata executionPayload) external;
    function getProposalStatus(uint256 proposalId) external view returns (ProposalStatus proposalStatus);
    interface EIP4824 {
        function daoURI() external view returns (string _daoURI);
    }
    users who hold more than 20 tokens will have 5 Voting Power

    exponent B = 1.5

    Calculation A: 5 ^ 0.5 ~= 2.236

    Calculation B: 5 ^ 1.5 ~= 11.18 As you can see exponent lower than 1 will reduce the original random value while exponent bigger than 1 will increase it.

    here
    balance-of-with-thresholds
    anti-whale
    Help Center

    The default validation is checking if the address has any voting power. If the voting power is higher than 0 the connected account is validated. A validation strategy can send a call to a node or subgraph.

    When setting the Validation Strategy up it’s important to keep in mind that it is meant to make it difficult for users outside of your community to post scam proposals or post spam votes.

    Therefore for Proposal Validation make sure to use a high threshold, for example $100 worth of your organization’s token. A good idea would be to check the holdings of previous proposal creators, both legitimate and scammers, to assess a reasonable value.

    Spaces are required to use Proposal Validation. Learn how to set it up on this page or read our article.

    Spaces using only a ticket strategy are required to set a Voting Validation to secure their spaces and ensure a fair voting process preventing spam. Learn here how to set it up: Voting Validation in Space Settings

    How to use validation strategies:

    Validation strategies can be used for two purposes:

    • proposal validation - determine if the account can create a new proposal,

    • voting validation - determine if the account can take part in the voting process.

    Proposal Validation in Space Settings

    Head to Proposals tab in the sidebar to update the configuration:

    Voting Validation in Space Settings

    Head to Voting tab in the sidebar to update the configuration:

    If you want to allow addresses with any voting power to vote you can use the default voting validation.

    If you are using only a ticket Voting Strategy for your space you are required to use a Gitcoin Passport Validation for Voting to protect your space from spam votes.

    Authors only mode

    If you wish to limit proposal creators to Admins, Moderators and Authors only, you can do so by enabling the Authors only setting in the Proposal tab in the space settings. Make sure to give the Author role to the users you trust!

    Validation strategy example - Basic

    The Basic validation strategy allows you to use existing Voting Strategies configured for your space or define a custom setup to determine if a user is eligible to create a proposal or cast a vote.

    In order to use existing setup of Voting Strategies you can simply chose Basic Validation and define a required threshold as on the screenshot below. 100 corresponds to user's Voting Power calculated on the basis of the Voting Strategies.

    Use current setup and define a strong threshold to avoid spam in your space.

    If you wish to use a different configuration, toggle the Use custom strategies button and define the strategies for your use case:

    Use a custom setup using various Voting Strategies to calculate if a user is eligible to create a proposal or vote.

    Validation strategy example - Gitcoin Passport

    Validation strategy built together with Gitcoin Passport. You can select individual or multiple stamps that matter for your space. You can also decide if they need to meet all of these criteria or only one. The more criteria you select, the more sybil resistant your space is.

    Implementation

    Have a look at the example of the Gitcoin Passport validation strategy.

    Voting validation can be specified in your space settings at https://snapshot.page/#/<SPACE ADDRESS>/settings.

    Create a custom validation

    The possibilities are endless! You can build a custom validation strategy for your space. Please have a look at Create a validation strategyfor more details.

    Find more voting validations here:

    Schema

    The schema for this subgraph is defined here in Snapshot’s GitHub.

    Snapshot Subgraph endpoint

    https://gateway.thegraph.com/api/[api-key]/subgraphs/id/4YgtogVaqoM8CErHWDK8mKQ825BcVdKB8vBYmb4avAQo

    This is visible from the Snapshot subgraph’s page on Graph Explorer.

    How to obtain your own API key

    1. Go to thegraph.com/studio and connect your wallet.

    2. Go to https://thegraph.com/studio/apikeys/ to create an API key.

    3. You can use this API key on any subgraph on Graph Explorer, and it’s not limited to just Snapshot.

    Other Snapshot Subgraphs

    • Snapshot on Arbitrum

    • Snapshot on Fantom

    • Snapshot on Gnosis Chain

    See all other subgraphs published by Snapshot Labs.

    How to Query through the API

    You can pass any GraphQL query to the Snapshot endpoint and receive data in JSON format.

    This following code example will return the exact same output as above.

    Sample code (Node.js):

    How to use the Visual Query Editor

    You can use the GraphiQL Explorer to compose your GraphQL queries by clicking on the fields you want.

    About The Graph

    The Graph is a decentralized protocol that enables seamless querying and indexing of blockchain data. It simplifies the complex process of querying blockchain data through the use of subgraphs (open APIs).

    Benefits of using The Graph

    • Anyone can query subgraphs on The Graph

    • All users get 100,000 free queries per month

    • Unlocking more queries is a seamless experience, you can pay with crypto or a credit card

    Additional resources

    For more information about querying data from your subgraph, read more here.

    To explore all the ways you can optimize & customize your subgraph for a better performance, read more about creating a subgraph here.

    Snapshot subgraph’s page on The Graph Explorer
    Logo
    Logo
    https://snapshot.org/#/playground/erc721-with-tokenid-weighted?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4MzBjREFjMzg3MWM0MWE2Mzc2NzI0N0M4RDFhMmRFNTlmNTcxNGU3OCIsInN5bWJvbCI6IlJlYXBlcihzKSIsInRva2VuSWRzIjpbIjIxMTIiLCIyODcxIiwiMzIyMSIsIjM1ODciXX0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIxNzUzNjA0NCIsImFkZHJlc3NlcyI6WyIweDg2MzM3OUFiNDAxZDQ1NDgzNEUxRkUyZUNlNDhGNTFhMjllRTlkN0EiLCIweDRDNEU2ZjEzZmI1RTNmNzBDMzc2MDI2MmEwM0UzMTc5ODI2OTFkMTAiLCIweGY1ODE5NWRlM2FmOTFhZmYxZjhkZDU1OWFkNDFmODhmMWI2YzRhYWYiXX0.snapshot.org
    https://snapshot.org/#/playground/erc721snapshot.org
    Logo
    Logo
    Logo
    Logo
    Logo
    https://snapshot.org/#/playground/with-delegation?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiUE9IIChkZWxlZ2F0ZWQpIiwiZGVsZWdhdGlvblNwYWNlIjoicG9oLmV0aCIsInN0cmF0ZWdpZXMiOlt7Im5hbWUiOiJlcmMyMC1iYWxhbmNlLW9mIiwicGFyYW1zIjp7ImFkZHJlc3MiOiIweDFkQUQ4NjIwOTVkNDBkNDNjMjEwOTM3MDEyMWNmMDg3NjMyODc0ZEIiLCJkZWNpbWFscyI6MH19XX0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHgzYzEzZjJCNTZBRjYxNGFDNjM4MTI2NUVjQjNCNjE5YkEyNkNDNjQxIiwiMHgwNDhmZWU3YzMyNzlhMjRhZjA3OTBiNmIwMDJkZWQ0MmJlMDIxZDJiIiwiMHgxMzlhOTAzMmE0NmMzYWZlMzQ1NmViNWYwYTM1MTgzYjVmMTg5Y2FlIl19snapshot.org
    https://snapshot.org/#/playground/erc721snapshot.org

    Voting threshold

    Define criteria that users have to meet in order to be eligible to vote.

    Suppose you want to restrict the ability to vote within your community for example to prevent bots from affecting the results or to prioritize members with higher stakes in your organization. In that case, you can do so by setting up Voting Strategies or a combination of Voting and Validation Strategies for your space.

    To recap what Voting and Validation Strategies are:

    Validation Strategies are a way to define who is allowed to vote on a proposal or create a new one.

    Voting Strategies calculate how much Voting Power each user has.

    Voting Validation

    This solution is a combination of any Voting Strategy/-ies and Voting Validation.

    Defining the required threshold of the user's Voting Power can be set up simply and leverage the Voting Strategies used in the Space.

    Go to Space Settings and open the Voting tab. At the bottom you can find the Validation section:

    Basic Voting Validation

    Select the .

    This way you can easily define how much Voting Power is required for the users to cast votes on Proposals in your Space. Voting Power is calculated on the basis of the Voting Strategies set for your Space.

    For example, if your Voting Strategy is and a user holds 20 tokens, their Voting Power will be 20 and they will be eligible to vote with the Basic Validation set at 10.

    If they hold 5 tokens, they won't be able to cast a vote as it's < 10 VP.

    Gitcoin Passport Validation

    Select the .

    This Validation allows you to set requirements by checking the stamps which serve as validation for the user’s identity and online reputation. You can select individual or multiple stamps that matter for your space. You can also decide if they need to meet all of these criteria or only one.

    Voting Strategies

    It is also possible to set a required threshold directly through a Voting Strategy.

    We highly recommend the first approach of using a Validation Strategy as it allows higher flexibility and is easier to maintain if the Voting Strategies are changed in the Space Settings.

    You can use it with ERC-20 and ERC-721 tokens.

    The strategy specifies the minimum balance required for a single token held in the user's wallet.

    Let's have a look at an example below. In order to be eligible to vote users are required to hold at least 20 DAI at the snapshot of proposal creation.

    Strategy setup:

    If you are using this strategy for an NFT, make sure to set the decimals to 0 (zero).

    You can test it out in the Playground on Snapshot:

    You can use it with ERC-20 and ERC-721 tokens.

    This strategy allows you to define multiple thresholds and allocate Voting Power at each level.

    In the example below, whoever holds less than 1 unit of the token will have 0 Voting Power.

    Users holding more than 1 but less than 4 units of the token will have 1 Voting Power.

    The maximum Voting Power per user will be fixed at 4 no matter how much of the token they own.

    Strategy setup:

    To achieve 1 vote per wallet, simply keep the line { "threshold": 1, "votes": 1 } under the thresholds param, and remove other keys and values.

    You can test it out in the Playground on Snapshot:

    You can use it with ERC-20 and ERC-721 tokens.

    erc20-with-balance in its simplest form assigns 1 vote to wallets holding any balance of the token. Regardless of how big the balance is, the VP is always the same and is at 1 VP. If the wallet doesn't hold any token, the VP is 0.

    This allows you to poll your community without referencing the number of tokens or NFTs they hold, each address will have 1VP.

    Now, to use this strategy to set a voting threshold you can add an optional parameter minBalance and define the minimum required balance which will give the user the eligibility to vote and 1 Voting Power. The parameter value is set to 0 by default.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    &

    You can use the math strategy flexibly with various tokens and networks. This example demonstrates how to set a threshold taking into account tokens on different chains.

    Math strategy is powerful in its composability. It allows you to apply common mathematical operations to the outputs of Voting Strategies.

    As an example, you can take a square root of the Voting Power calculated by , or the lower (min) value out of two different Voting Strategies. You can see all possibilities on the .

    How to set it up for a multichain scenario?

    If the Voting Power calculated for your space is based on tokens on different chains and you want to set a minimum threshold defining if a user is eligible to vote, you can use the a-if-lt-b operand. Don't worry if it sounds cryptic, we will go through it step by step.

    Operation
    Operand count
    Description

    Let's look at the formula first: (x, a, b) = x < b ? a : x, where:

    • x - sum of Voting Power calculated by the two Voting Strategies on chains 137 and 1 (as per the example below)

    • a - first constant, in our case it's 0

    • b - first constant, in our case it's 100

    Strategy setup:

    The algorithm will check if x is smaller than b, the second constant:

    • if it's below b, the final Voting Power will be set to a, first constant -> 0

    • if it's equal to and higher than b, the final Voting Power will be set to x, the sum of result from the Voting Strategies

    As you can see we can easily define what is the minimum Voting Power (our example: 100) coming from different chains that is required for the user to vote!

    You can test it out in the Playground on Snapshot:

    5.

    You can use it with ERC-20 and ERC-721 tokens, on multiple networks.

    If you want to require users to hold specific tokens in order to be eligible to vote you can use the holds-tokens strategy.

    The minimum balance for each token can be customized.

    Users who meet the criteria will receive 1 Voting Power regardless of the total value of the tokens they hold.

    Note, that the minBalance parameter is exclusive. It means that when set to 1, the user has to hold more than 1 of the specified token in order to vote.

    Strategy setup:

    You can test it out in the Playground on Snapshot:

    Custom calculations

    If none of the above scenarios work for your use case, you can use the customizable Strategies to leverage your own custom calculation mechanisms. Read on to learn more!

    Snapshot provides several Voting Strategies that allow you to use your own API to return user's Voting Power or for example perform mathematical calculations on the results from existing Strategies.

    You can also create a new custom Voting or Validation Strategy if none of the available solutions meet your needs. Have a look at the https://github.com/snapshot-labs/snapshot-docs/blob/master/user-guides/spaces/space-handbook/broken-reference/README.md section of this documentation to learn more!

    This strategy allows off-chain data to be used as Voting Power through calling a custom API endpoint to request the score for a set of addresses.

    It's useful to use this strategy if your API can return the Voting Power directly. It's network agnostic.

    Strategy setup:

    Name
    Type
    Description
    Default

    If you are passing an IPFS URL use the following format:

    If you are passing a JSON URL use the following format:

    If you are passing an API URL use the following format:

    All voter addresses will be passed in the query string.

    If you are passing a API URL with POST method use the following format:

    You can test it out in the Playground on Snapshot:

    You can use the math strategy flexibly with various tokens and networks. This example demonstrates how to set a threshold taking into account tokens on different chains.

    Math strategy is powerful in its composability. It allows you to apply common mathematical operations to the outputs of Voting Strategies.

    As an example, you can take a square root of the Voting Power calculated by , or the lower (min) value out of two different Voting Strategies. You can see all possibilities on the .

    How to set it up for a multichain scenario?

    If the Voting Power calculated for your space is based on tokens on different chains and you want to set a minimum threshold defining if a user is eligible to vote, you can use the a-if-lt-b operand. Don't worry if it sounds cryptic, we will go through it step by step.

    Operation
    Operand count
    Description

    Let's look at the formula first: (x, a, b) = x < b ? a : x, where:

    • x - sum of Voting Power calculated by the two Voting Strategies on chains 137 and 1 (as per the example below)

    • a - first constant, in our case it's 0

    • b - first constant, in our case it's 100

    Strategy setup:

    The algorithm will check if x is smaller than b, the second constant:

    • if it's below b, the final Voting Power will be set to a, first constant -> 0

    • if it's equal to and higher than b, the final Voting Power will be set to x, the sum of result from the Voting Strategies

    As you can see we can easily define what is the minimum Voting Power (our example: 100) coming from different chains that is required for the user to vote!

    You can test it out in the Playground on Snapshot:

    Strategy used in the example:

    Logo

    Create a 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://blog.gnosis.pm/introducing-safesnap-the-first-in-a-decentralized-governance-tool-suite-for-the-gnosis-safe-ea67eb95c34fblog.gnosis.pm
    Announcing “oSnap:” Gasless Snapshot voting with on-chain execution by UMAMedium
    oSnap Proposal Verification | UMA Documentationdocs.uma.xyz
    oSnap | UMA Documentationdocs.uma.xyz
    https://docs.uma.xyz/developers/osnap/snapshot-tutorialdocs.uma.xyz
    https://docs.uma.xyz/developers/osnap/osnap-deployment-tutorialdocs.uma.xyz
    {
      "address": "0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d",
      "symbol": "LAND",
      "decimals": 0,
      "thresholds": [
        { "threshold": 5, "votes": 1 },
        { "threshold": 10, "votes": 2 },
        { "threshold": 20, "votes": 5 }
      ]
    }
    If result > antiWhale.threshold
      result = antiWhale.inflectionPoint * ( result / antiWhale.inflectionPoint ) ^ antiWhale.exponent
    
    If result <= antiWhale.threshold {
      thresholdMultiplier = ( antiWhale.inflectionPoint * ( antiWhale.threshold / antiWhale.inflectionPoint )^ antiWhale.exponent ) / antiWhale.threshold
    
      result = result * thresholdMultiplier
    }
    {
      "symbol": "ANTI",
      "strategy": {
        "name": "erc20-balance-of",
        "params": {
          "address": "0x579cea1889991f68acc35ff5c3dd0621ff29b0c9",
          "symbol": "IQ",
          "decimals": 18
        }
      },
      "antiWhale": {
        "inflectionPoint": 1000,
        "threshold": 250,
        "exponent": 0.5
      },
      "log": true
    }
    import snapshot from '@snapshot-labs/snapshot.js';
    import Validation from '../validation';
    import {
      getPassport,
      getVerifiedStamps,
      hasValidIssuanceAndExpiration
    } from '../passport-weighted/helper';
    
    export default class extends Validation {
      public id = 'passport-gated';
      public github = 'snapshot-labs';
      public version = '0.1.0';
    
      async validate(): Promise<boolean> {
        const requiredStamps = this.params.stamps;
        const passport: any = await getPassport(this.author);
        if (!passport) return false;
        if (!passport.stamps?.length || !requiredStamps?.length) return false;
    
        const verifiedStamps: any[] = await getVerifiedStamps(
          passport,
          this.author,
          requiredStamps.map((stamp) => ({
            id: stamp
          }))
        );
        if (!verifiedStamps.length) return false;
    
        const provider = snapshot.utils.getProvider(this.network);
        const proposalTs = (await provider.getBlock(this.snapshot)).timestamp;
    
        const operator = this.params.operator;
    
        // check issuance and expiration
        const validStamps = verifiedStamps
          .filter((stamp) =>
            hasValidIssuanceAndExpiration(stamp.credential, proposalTs)
          )
          .map((stamp) => stamp.provider);
    
        if (operator === 'AND') {
          return requiredStamps.every((stamp) => validStamps.includes(stamp));
        } else if (operator === 'OR') {
          return requiredStamps.some((stamp) => validStamps.includes(stamp));
        } else {
          return false;
        }
      }
    }
    Example Query: Get the first 5 delegations from Snapshot
    {
      delegations(first: 5) {
        id
        delegator
        space
        delegate
      }
    }
    {
      "data": {
        "delegations": [
          {
            "delegate": "0xde1e6a7ed0ad3f61d531a8a78e83ccddbd6e0c49",
            "delegator": "0x00000000005ef87f8ca7014309ece7260bbcdaeb",
            "id": "0x00000000005ef87f8ca7014309ece7260bbcdaeb-cvx.eth-0xde1e6a7ed0ad3f61d531a8a78e83ccddbd6e0c49",
            "space": 
          },
      //…
         ]}
    }
    const axios = require('axios');
    
    const graphqlQuery = `{
      delegations(first: 5) {
        id
        delegator
        space
        delegate
      }
    }`;
    
    const queryUrl = 'https://gateway.thegraph.com/api/[api-key]/subgraphs/id/4YgtogVaqoM8CErHWDK8mKQ825BcVdKB8vBYmb4avAQo';
    
    const graphQLRequest = {
      method: 'post',
      url: queryUrl,
      data: {
        query: graphqlQuery,
      },
    };
    
    // Send the GraphQL query
    axios(graphQLRequest)
      .then((response) => {
        // Handle the response here
        const data = response.data.data;
        console.log(data);
      })
      .catch((error) => {
        // Handle any errors
        console.error(error);
      });
    Logo
    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.

    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:

    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:

    d. ./score-api/src/strategies/strategies/index.ts

    Import and declare you new strategy in the index.ts file:

    d. examples.json

    Provide an example for the custom strategy setup which will be displayed on https://snapshot.org on the strategy's details page.

    Make sure to include all the parameters you defined above and a list of addresses to test against:

    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.

    4. Test the strategy locally

    Once you saved all the files run the below command with the name of your new strategy:

    It will trigger the tests which you can find in this file. 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

    6. Create a pull request

    Create a Pull Request with the above changes on the original 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 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 strategyto see if it works as you intended.

    Congrats, you've just added a new custom voting strategy! 🎉

    a-if-lt-b

    3

    (x, a, b) = x < b ? a : x

    Basic Voting Validation
    erc20-balance-of
    Gitcoin Passport Validation
    Gitcoin Passport
    balance-of-with-min
    balance-of-with-thresholds
    erc20-with-balance
    math
    multichain
    erc20-balance-of
    strategy page
    holds-tokens

    url

    string

    URL of the API endpoint

    undefined

    type

    string

    Type of the API endpoint ( api-get or api-post or ipfs or json )

    api-get

    additionalParams

    string

    Additional parameters for the API endpoint (optional)

    a-if-lt-b

    3

    (x, a, b) = x < b ? a : x

    api-v2
    math
    erc20-balance-of
    strategy page
    multichain

    ``

    Logo
    Logo
    https://snapshot.org/#/playground/erc721-with-tokenid-range-weights?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4MzBjREFjMzg3MWM0MWE2Mzc2NzI0N0M4RDFhMmRFNTlmNTcxNGU3OCIsInN5bWJvbCI6IlJlYXBlcihzKSIsImRlZmF1bHRXZWlnaHQiOjEsInRva2VuSWRXZWlnaHRSYW5nZXMiOlt7InN0YXJ0IjowLCJlbmQiOjMwMDAsIndlaWdodCI6MX0seyJzdGFydCI6MzAwMSwiZW5kIjo0NzE1LCJ3ZWlnaHQiOjJ9XX0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHg4NjMzNzlBYjQwMWQ0NTQ4MzRFMUZFMmVDZTQ4RjUxYTI5ZUU5ZDdBIiwiMHg0QzRFNmYxM2ZiNUUzZjcwQzM3NjAyNjJhMDNFMzE3OTgyNjkxZDEwIiwiMHhmNTgxOTVkZTNhZjkxYWZmMWY4ZGQ1NTlhZDQxZjg4ZjFiNmM0YWFmIl19snapshot.org
    Logo
    https://snapshot.org/#/playground/erc1155-balance-of-ids?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQUJDIiwiYWRkcmVzcyI6IjB4Mjg0NzJhNThBNDkwYzVlMDlBMjM4ODQ3RjY2QTY4YTQ3Y0M3NmYwZiIsImlkcyI6WyIwIl19LCJuZXR3b3JrIjoiMSIsInNuYXBzaG90IjoiIiwiYWRkcmVzc2VzIjpbIjB4MzQ1MThmNTU1OTQyNWE3YmIwNmY2NjE5NjkyMGFmMTBlMTkzOGI1ZiJdfQ..snapshot.org
    https://docs.uma.xyz/developers/osnap/osnap-module-admin-functionsdocs.uma.xyz
    Logo
    Logo
    https://snapshot.org/#/playground/staked-uniswap?query=eyJwYXJhbXMiOnsidG9rZW5BZGRyZXNzIjoiMHg2ZTM2NTU2YjNlZTVhYTI4ZGVmMmE4ZWMzZGFlMzBlYzJiMjA4NzM5IiwidW5pc3dhcEFkZHJlc3MiOiIweGRmNmI4NjFiNGZiY2ZhZmZiNjJkZDE5MDZmY2QzYTg2Mzk1NTcwNGIiLCJzdGFraW5nQWRkcmVzcyI6IjB4ZmQxNTY1NzM0MTQ5MmQxOTE4ZTNhOGI3NDIxZTk2MjdkNTIwNTZlOSIsInN5bWJvbCI6IkJVSUxEIiwiZGVjaW1hbHMiOjE4fSwibmV0d29yayI6IjEiLCJzbmFwc2hvdCI6IiIsImFkZHJlc3NlcyI6WyIweDg3NjE2ZkE4NTBjODdhNzhmMzA3ODc4ZjMyRDgwOGRhZDhmNGQ0MDEiXX0.snapshot.org
    https://snapshot.org/#/playground/sushiswap?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4Y2FmZTAwMTA2N2NkZWYyNjZhZmI3ZWI1YTI4NmRjZmQyNzdmM2RlNSIsInN5bWJvbCI6IlBTUCIsInVzZVN0YWtlZEJhbGFuY2VzIjoidHJ1ZSIsIm1hc3RlcmNoZWZWZXJzaW9uIjoidjIifSwibmV0d29yayI6IjEiLCJzbmFwc2hvdCI6IiIsImFkZHJlc3NlcyI6WyIweDA1ODQ3NmVkYWNiMjNlOTUwN2NmZjM3OWU3ZGQ4Y2Y0ZGVlNGQyZGIiLCIweDViYTQ3ZjhjNjRmY2Y1NWU5ODZlMmYzNzg2MGI5MWI1MDFkMWIxZWQiXX0.snapshot.org
    https://snapshot.org/#/playground/masterchef-pool-balance?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQ0hFRiIsImNoZWZBZGRyZXNzIjoiMHhEMzhhYmJBZUMwM2E5RkYyODdlRmM5YTVGMGQwNTgwRTA3MzM1RDFEIiwidW5pUGFpckFkZHJlc3MiOm51bGwsInRva2VuSW5kZXgiOm51bGwsInBpZCI6IjAiLCJ3ZWlnaHQiOjEsIndlaWdodERlY2ltYWxzIjowfSwibmV0d29yayI6IjEiLCJzbmFwc2hvdCI6IiIsImFkZHJlc3NlcyI6WyIweGZDQTVhMjdkNGNmRjEwNEZDMjc2ODk3Q0EzZjMyY0ZlRGM2ZjUwQkEiLCIweDU3N2JmYTA4OTgxODdjMTBiYmJiYjNkMDAxYzk0YWFmYjVjZmMwZTQiLCIweDA2OGU0OGU5ODRiODMzODdhZmU3NzI2MTQ5MTY3ODBhNzUzOTI5ZDkiXX0.snapshot.org
    https://snapshot.org/#/playground/uniswap-v3?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiVU5JIiwicG9vbEFkZHJlc3MiOiIweGZjOWY1NzIxMjRkOGY0Njk5NjBiOTQ1MzdiNDkzZjI2NzY3NzZjMDMiLCJ0b2tlblJlc2VydmUiOjB9LCJuZXR3b3JrIjoiMSIsInNuYXBzaG90IjoiIiwiYWRkcmVzc2VzIjpbIjB4MWE2MmM5ZDE3NDZkZTlmZDlkNDQ5ZTgwZGRiYjhhNjdkMmE3MmE5NCIsIjB4NzkwMzk4NzI2YTY4YTJlODFhZWMzZWQwZjdjYzM3NThiY2NjNTY4MSJdfQ..snapshot.org
    https://snapshot.org/#/playground/uniswap?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4NmIxNzU0NzRlODkwOTRjNDRkYTk4Yjk1NGVlZGVhYzQ5NTI3MWQwZiIsInN5bWJvbCI6IkRBSSJ9LCJuZXR3b3JrIjoiMSIsInNuYXBzaG90IjoiIiwiYWRkcmVzc2VzIjpbIjB4NzkzMTdmYzBmYjE3YmMwY2UyMTNhMmI1MGYzNDNlNGQ0YzI3NzcwNCJdfQ..snapshot.org
    https://snapshot.org/#/playground/contract-call?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4MGUwOWZhYmI3M2JkM2FkZTBhMTdlY2MzMjFmZDEzYTE5ZTgxY2U4MiIsInN5bWJvbCI6IkNha2UiLCJkZWNpbWFscyI6MTgsIm1ldGhvZEFCSSI6eyJpbnB1dHMiOlt7ImludGVybmFsVHlwZSI6ImFkZHJlc3MiLCJuYW1lIjoiYWNjb3VudCIsInR5cGUiOiJhZGRyZXNzIn1dLCJuYW1lIjoiYmFsYW5jZU9mIiwib3V0cHV0cyI6W3siaW50ZXJuYWxUeXBlIjoidWludDI1NiIsIm5hbWUiOiIiLCJ0eXBlIjoidWludDI1NiJ9XSwic3RhdGVNdXRhYmlsaXR5IjoidmlldyIsInR5cGUiOiJmdW5jdGlvbiJ9fSwibmV0d29yayI6IjU2Iiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHgwMDljRjdiQzU3NTg0Yjc5OTgyMzZlZmY1MWI5OEExNjhEY2VBOUIwIl19snapshot.org

    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 off-chain voting system. It was designed to work both in the browser and with Node.js.

    👉

    Create a plugin

    Learn how to create a new plugin for Snapshot.

    If the existing plugins do not fulfill the needs of your space it is possible to create a new one. Keep in mind though that at this moment we have a curated list of plugins that extend the core functionality of Snapshot and we want to make sure that their logic is written in line with Snapshot's values.

    The development of new plugins should be coordinated with the Snapshot team.

    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 , you can include the app parameter in your function calls:

    └── src
        └── strategies
            └── strategies
                └── erc20-balance-of
    
    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<Record<string, number>> {
      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<string, BigNumberish> = await multi.execute();
    
      return Object.fromEntries(
        Object.entries(result).map(([address, balance]) => [
          address,
          parseFloat(formatUnits(balance, options.decimals))
        ])
      );
    }
    {
      "$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
        }
      }
    }
    import * as yourStrategyName from './your-strategy-name';
    [
      {
        "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
      }
    ]
    
    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
    }
    npm run test:strategy --strategy=<STRATEGY NAME> // replace <STRATEGY NAME>
    {
      "address": "0x6b175474e89094c44da98b954eedeac495271d0f",
      "symbol": "DAI",
      "decimals": 18,
      "minBalance": 20
    }
    {
      "address": "0xf87e31492faf9a91b02ee0deaad50d51d56d5d4d",
      "symbol": "LAND",
      "decimals": 0,
      "thresholds": [
        { "threshold": 1, "votes": 1 },
        { "threshold": 4, "votes": 2 },
        { "threshold": 11, "votes": 3 },
        { "threshold": 25, "votes": 4 }
      ]
    }
    {
      "address": "0x84cA8bc7997272c7CfB4D0Cd3D55cd942B3c9419",
      "symbol": "Rome",
      "decimals": 18,
      "minBalance": 10
    }
    {
      "symbol": "GHST",
      "operands": [
        {
          "type": "strategy",
          "strategy": {
            "name": "multichain",
            "params": {
              "strategies": [
                {
                  "name": "erc20-balance-of",
                  "network": "137",
                  "params": {
                    "address": "0x385eeac5cb85a38a9a07a70c73e0a3271cfb54a7",
                    "decimals": 18
                  }
                },
                {
                  "name": "erc20-balance-of",
                  "network": "1",
                  "params": {
                    "address": "0x3f382dbd960e3a9bbceae22651e88158d2791550",
                    "decimals": 18
                  }
                }
              ]
            }
          }
        },
        {
          "type": "constant",
          "value": 0
        },
        {
          "type": "constant",
          "value": 1
        }
      ],
      "operation": "a-if-lt-b"
    }
    {
      "symbol": "XYZ",
      "tokenAddresses": [
        {
          "address": "0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85",
          "network": "1",
          "decimals": 0,
          "minBalance": 0
        },
        {
          "address": "0xaC4255eC6885E50352A1957062ac418c2CC94e27",
          "network": "137",
          "decimals": 0,
          "minBalance": 0
        }
      ]
    }
    {
      "url": "ipfs://...",
      "type": "ipfs"
    }
    {
      "url": "https://...",
      "type": "json"
    }
    {
      "url": "https://...",
      "type": "api-get"
    }
    {
      "url": "https://...",
      "type": "api-post"
    }
    {
      "symbol": "GHST",
      "operands": [
        {
          "type": "strategy",
          "strategy": {
            "name": "multichain",
            "params": {
              "strategies": [
                {
                  "name": "erc20-balance-of",
                  "network": "137",
                  "params": {
                    "address": "0x385eeac5cb85a38a9a07a70c73e0a3271cfb54a7",
                    "decimals": 18
                  }
                },
                {
                  "name": "erc20-balance-of",
                  "network": "1",
                  "params": {
                    "address": "0x3f382dbd960e3a9bbceae22651e88158d2791550",
                    "decimals": 18
                  }
                }
              ]
            }
          }
        },
        {
          "type": "constant",
          "value": 0
        },
        {
          "type": "constant",
          "value": 1
        }
      ],
      "operation": "a-if-lt-b"
    }
    Logo
    Logo
    Logo
    Logo
    Logo
    Logo
    Logo
    Logo
    Logo
    Get started

    Installation

    Node.js

    To install Snapshot.js on Node.js, open your terminal and run:

    Browser

    You can create an index.html file and include Snapshot.js with:

    Development

    Install dependencies

    Build package

    Usage

    Base features

    Init client

    Cast a vote

    Create proposal

    Create or edit a space

    Join a space

    Utils

    The below methods are sending a request to Score API. Same as Hub 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

    getScores

    Calculate voting power for a list of voters.

    getVp

    Retrieve voting power for a specific address using given strategies.

    validate

    Validate an address using a given validation strategy.

    getProvider

    Return a Ethers.js JsonRPCProvider connected to an archive node.

    Snapshot.js Repository
    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:

    Example:

    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.

    Snapshot.js
    https://snapshot.box/#/{network}:{space}/proposal/{proposalId}?app={yourappname}
    npm i @snapshot-labs/snapshot.js
    <script src="https://cdn.jsdelivr.net/npm/@snapshot-labs/snapshot.js"></script>
    yarn
    yarn build
    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);
    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'
    });
    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
    });
    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)
        }`
    });
    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"
    });
    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);
    });
    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
    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);
    });
    
    import snapshot from '@snapshot-labs/snapshot.js';
    
    const network = '1';
    const provider = snapshot.utils.getProvider(network);
    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'
    });
    https://snapshot.box/#/s:ens.eth/proposal/0xfa54ff2b55f0495c96ec2d8645241bcff48ca6afe1f4925fb51f29c4667252df?app=boardroom
    1. Fork the snapshot repository

    Create a fork of the snapshot repository:

    2. Create a new directory and the plugin.json

    Position yourself at the root of the project repository and run the below command to create a new directory for your plugin and provide basic details about it.

    Make sure to update myPlugin with the name of your plugin (using camelCase naming convention), name and description to briefly describe what it does:

    Thanks to the plugin.json file the plugin will be visible in the space settings.

    You can check that by running the local server and heading to any space's settings.

    Now it's time to add the logic to it!

    3. Create plugin structure and logic

    In order to display the plugin on Snapshot, you need to create its structure by using components. The below table is listing the available components with their render location:

    Plugin component
    will be rendered here:

    myPlugin/Proposal.vue

    below proposal content

    myPlugin/ProposalSidebar.vue

    proposal sidebar

    myPlugin/Create.vue

    proposal creation, plugins step

    Within those components you can do everything you can do in any other Vue 3 component. You can split the code across multiple components and import them in one of the above, as well as create your own composables or other helper files to structure your code as you like.

    It's technically not required but recommended to use Vue 3's composition API and the <script setup> syntax.

    Let's have a look at the Proposal component example.

    Proposal

    In order to display your plugin below the proposal content you have to create a Proposal component. Navigate to the plugin directory you have just created in the previous step and create a Proposal.vue file. You can start with a basic Vue.js single file component.

    Properties

    To do something meaningful, a plugin will probably need some awareness of the current context (space, proposal, etc). This information is passed down to the plugin components as properties. A component on the proposal page, that needs the proposal's id can receive it in the following way:

    Here are all properties, that will be passed down to the plugin's main components:

    Create form
    Proposal page

    proposal

    form content

    current proposal

    space

    space settings

    space settings

    preview

    preview enabled

    -

    id

    -

    Only the main components (Create.vue, Proposal.vue, ProposalSidebar.vue) in your plugin's root directory will receive those properties automatically. You can of course pass those properties further down to other components as needed.

    Existing components/composables

    Any of the existing UI components in src/components, composables in src/composables or installed packages (like snapshot.js) can be used normally.

    Config defaults

    Most plugins will require some configuration options so that a space admin can enter information like their token address, API endpoints and others. Defaults can be defined in the plugin.json as follows:

    Under the "space" key you can define global config options. They can then be set in the plugin section on a space's settings like so:\

    The "proposal" key let's you define options specific to a single proposal. This key must be set in order for the Create.vue component to be shown in the proposal creation process.

    Localization

    The snapshot.org interface supports multiple languages and new plugins should be built with that in mind. Don't use raw text strings in your plugin's components directly but use the t function instead:

    The actual strings need to be added in src/locales/default.json to be available for translators, in order to update the language specific files, like de-DE.json. You can add your strings on the highest level in default.json, under a unique key, e.g. your plugin's directory name and the translation will be done automatically.

    Learn more about localization in Vue here.

    Numbers and relative time

    Apart from vue-i18n, there are custom number and time formatters available in the useIntl composable.

    4. Create a Pull Request

    Once your plugin is tested and ready to deploy, create a new Pull Request on the original snapshot repository. 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 which can take a couple of days.

    5. Test the plugin in production

    Your plugin is now available on Snapshot! 🎉 Make sure to test it thoroughly in production before communicating to your community.

    https://snapshot.org/#/playground/eth-balance?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQkFMIiwiYWRkcmVzcyI6IjB4YmExMDAwMDA2MjVhMzc1NDQyMzk3OGE2MGM5MzE3YzU4YTQyNGUzRCIsImRlY2ltYWxzIjoxOH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHhhNDc4YzI5NzVhYjFlYTg5ZTgxOTY4MTFmNTFhN2I3YWRlMzNlYjExIiwiMHhlRjgzMDVFMTQwYWM1MjAyMjVEQWYwNTBlMmY3MWQ1ZkJjQzU0M2U3IiwiMHgxRTFBNTFFMjVmMjgxNjMzNWNBNDM2RDY1ZTlBZjc2OTRCRTIzMmFkIiwiMHgxRjcxN0NlOGZmMDc1OTdlZTdjNDA4YjU2MjNkRjQwQWFBZjE3ODdDIiwiMHgxYzdhOTI3NUYyQkQ1YTI2MEE5YzMxMDY5Rjc3ZDUzNDczYjhhZTJlIiwiMHgxZDVFNjVhMDg3ZUJjM2QwM2EyOTQ0MTJFNDZDRTVENjg4Mjk2OWY0IiwiMHgxZjI1NDMzNkU1YzQ2NjM5QTg1MWI5Q2ZDMTY1Njk3MTUwYTZjMzI3IiwiMHgyZWMzRjgwQmVEQTYzRWRlOTZCQTIwMzc1MDMyQ0REM2FBZmIzMDMwIiwiMHg0QWNCY0E2QkUyZjhEMjU0MGJCRjRDQTc3RTQ1ZEEwQTRhMDk1RmEyIiwiMHg0RjNEMzQ4YTZEMDk4MzdBZTc5NjFCMUUwY0VlMmNjMTE4Y2VjNzc3IiwiMHg2RDdmMjNBNTA5RTIxMkJhNzc3M0VDMWIyNTA1ZDFBMTM0ZjU0ZmJlIiwiMHgwN2ExZjZmYzg5MjIzYzVlYkQ0ZTRkZGFFODlBYzk3NjI5ODU2QTBmIiwiMHg4ZDVGMDUyNzBkYTQ3MGUwMTViNjdBYjUwNDJCRGJFMkQyRkVGQjQ4IiwiMHg4ZDA3RDIyNWE3NjliN0FmM0E5MjM0ODFFMUZkRjQ5MTgwZTZBMjY1IiwiMHg4ZjYwNTAxZEU1YjliMDFGOUVBZjEyMTRkYkUxOTI0YUE5N0Y3ZmQwIiwiMHg5QjhlOGREOTE1MTI2MGMyMUNCNkQ3Y2M1OTA2N2NkOERGMzA2RDU4IiwiMHgxN2VhOTJENkZmYkFBMWM3RjZCMTE3YzFFOUQwYzg4QUJkYzhiODRDIiwiMHgzOEMwMDM5MjQ3QTMxRjM5MzliYUU2NWU5NTM2MTIxMjVjQjg4MjY4Il19snapshot.org
    https://snapshot.org/#/playground/eth-with-balance?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQkFMIiwiYWRkcmVzcyI6IjB4YmExMDAwMDA2MjVhMzc1NDQyMzk3OGE2MGM5MzE3YzU4YTQyNGUzRCIsImRlY2ltYWxzIjoxOH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHhhNDc4YzI5NzVhYjFlYTg5ZTgxOTY4MTFmNTFhN2I3YWRlMzNlYjExIiwiMHhlRjgzMDVFMTQwYWM1MjAyMjVEQWYwNTBlMmY3MWQ1ZkJjQzU0M2U3IiwiMHgxRTFBNTFFMjVmMjgxNjMzNWNBNDM2RDY1ZTlBZjc2OTRCRTIzMmFkIiwiMHgxRjcxN0NlOGZmMDc1OTdlZTdjNDA4YjU2MjNkRjQwQWFBZjE3ODdDIiwiMHgxYzdhOTI3NUYyQkQ1YTI2MEE5YzMxMDY5Rjc3ZDUzNDczYjhhZTJlIiwiMHgxZDVFNjVhMDg3ZUJjM2QwM2EyOTQ0MTJFNDZDRTVENjg4Mjk2OWY0IiwiMHgxZjI1NDMzNkU1YzQ2NjM5QTg1MWI5Q2ZDMTY1Njk3MTUwYTZjMzI3IiwiMHgyZWMzRjgwQmVEQTYzRWRlOTZCQTIwMzc1MDMyQ0REM2FBZmIzMDMwIiwiMHg0QWNCY0E2QkUyZjhEMjU0MGJCRjRDQTc3RTQ1ZEEwQTRhMDk1RmEyIiwiMHg0RjNEMzQ4YTZEMDk4MzdBZTc5NjFCMUUwY0VlMmNjMTE4Y2VjNzc3IiwiMHg2RDdmMjNBNTA5RTIxMkJhNzc3M0VDMWIyNTA1ZDFBMTM0ZjU0ZmJlIiwiMHgwN2ExZjZmYzg5MjIzYzVlYkQ0ZTRkZGFFODlBYzk3NjI5ODU2QTBmIiwiMHg4ZDVGMDUyNzBkYTQ3MGUwMTViNjdBYjUwNDJCRGJFMkQyRkVGQjQ4IiwiMHg4ZDA3RDIyNWE3NjliN0FmM0E5MjM0ODFFMUZkRjQ5MTgwZTZBMjY1IiwiMHg4ZjYwNTAxZEU1YjliMDFGOUVBZjEyMTRkYkUxOTI0YUE5N0Y3ZmQwIiwiMHg5QjhlOGREOTE1MTI2MGMyMUNCNkQ3Y2M1OTA2N2NkOERGMzA2RDU4IiwiMHgxN2VhOTJENkZmYkFBMWM3RjZCMTE3YzFFOUQwYzg4QUJkYzhiODRDIiwiMHgzOEMwMDM5MjQ3QTMxRjM5MzliYUU2NWU5NTM2MTIxMjVjQjg4MjY4Il19snapshot.org
    https://snapshot.org/#/playground/erc20-balance-ofsnapshot.org
    https://snapshot.org/#/playground/membership?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQkFMIiwiYWRkcmVzcyI6IjB4YmExMDAwMDA2MjVhMzc1NDQyMzk3OGE2MGM5MzE3YzU4YTQyNGUzRCIsImRlY2ltYWxzIjoxOH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHgzMjk1ZGY0MWEyZjI4OGRhMDM4MThhZTMyNTY1ZTE1OTlmMWIyZWVlIiwiMHgyMDcyMzBFMDZlMTRkMWZhOTRlNzAzZjQ3ODRGODE5NjY3NDcyMjQ3IiwiMHgwMDU1NTk2NjUyMWRmOWVkYmNhOWI4YTQ5N2FkODAzMWNmMzNmNzJhIl19snapshot.org
    https://snapshot.org/#/playground/balance-of-with-thresholds?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4Zjg3ZTMxNDkyZmFmOWE5MWIwMmVlMGRlYWFkNTBkNTFkNTZkNWQ0ZCIsInN5bWJvbCI6IkxBTkQiLCJkZWNpbWFscyI6MCwidGhyZXNob2xkcyI6W3sidGhyZXNob2xkIjo1LCJ2b3RlcyI6MX0seyJ0aHJlc2hvbGQiOjEwLCJ2b3RlcyI6Mn0seyJ0aHJlc2hvbGQiOjIwLCJ2b3RlcyI6NX1dfSwibmV0d29yayI6IjEiLCJzbmFwc2hvdCI6IjE3Mzc4MjU5IiwiYWRkcmVzc2VzIjpbIjB4ZWQ0NzAxNWJiODA4MGI5Mzk5ZjlkMGRkZmM0MjdiOWNlZTJjYWFiMSIsIjB4ZjU2MzQ1MzM4Y2I0Y2RkYWY5MTVlYmVmM2JmZGU2M2U3MGZlMzA1MyIsIjB4N2IxNWU2YzQzOWIyN2E1NTNiNjVhOTkwNGNlNTcxZGE2NjkxYTBmYiIsIjB4OGQyZjNhNzZhNzZmMDU1ZDYyYTkzMTY3OGFiMTZiMDQyZTdiYWRlYiJdfQ..snapshot.org
    mkdir src/plugins/myPlugin && echo '{
      "name": "My Snapshot Plugin",
      "author": "My GH profile",
      "version": "1.0.0",
      "description": "A plugin to show how plugins are built.", # optional
      "defaults": {}, # predefined parameters, optional
      "icon": "", # URL for the icon, optional
      "website": "https://my.website" # optional
    }' > src/plugins/myPlugin/plugin.json
    <script setup>
    const msg = 'Hello world!'
    </script>
    
    <template>
      <h1>My Plugin</h1>
      <div>{{ msg }}</div>
    </template>
    <script setup>
    defineProps({
      id: String // the current proposal's id
    });
    </script>
    
    <template>
      <a :href="'https://...' + id"> ...
    </template>
    <script setup>
    import { useWeb3 } from '@/composables/useWeb3'
    const { web3Account } = useWeb3();
    </script>
    
    <template>
      <Block title='My Plugin'>
        <h2>Your Account: {{ web3Account }}</h1>
      </Block>
    </template>
    {
      "name": "My Snapshot Plugin",
      "description": "A plugin to show how plugins are built.",
      "defaults": {
        "space": {
          "someURL": "https://..."
        },
        "proposal": {
          "someParam": true
        }
      }
    }
    <template>
      <h1>{{ $t('myPlugin.hello') }}</h1>
    </template>
    {
      "myPlugin": {
        "hello": "Hello World!"
      }
    }
    <script setup>
    import { useIntl } from '@/composables/useIntl';
    
    const {
      formatRelativeTime,
      formatDuration,
      formatNumber,
      formatCompactNumber,
      formatPercentNumber
    } = useIntl();
    </script>
    
    <template>
      <div>
        {{ formatRelativeTime(1643350286) }} <!-- "5 minutes ago" -->
        {{ formatDuration(654) }}            <!-- "11 minutes" --> 
        {{ formatNumber(1643350) }}          <!-- "1,643,350" -->
        {{ formatCompactNumber(1643350) }}   <!-- "1.6M" -->
        {{ formatPercentNumber(0.86543) }}   <!-- "86.54%" -->
      </div>
    </template>

    proposal id route parameter

    results

    -

    current voting results

    loadedResults

    -

    whether voting results finished loading

    strategies

    -

    used strategies

    Logo
    Logo
    Logo
    score-api/src/strategies/validations at master · snapshot-labs/score-apiGitHub
    Logo
    Logo

    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

    Quick start

    Installation

    Install the latest version of the beta release:

    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:

    To learn more about StarkNet's Provider object, have a look .

    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

    Create a proposal

    Update a proposal

    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.

    More details coming soon.

    https://snapshot.org/#/playground/balance-of-with-min?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4NmIxNzU0NzRlODkwOTRjNDRkYTk4Yjk1NGVlZGVhYzQ5NTI3MWQwZiIsInN5bWJvbCI6IkRBSSIsImRlY2ltYWxzIjoxOCwibWluQmFsYW5jZSI6MjB9LCJuZXR3b3JrIjoiMSIsInNuYXBzaG90IjoiIiwiYWRkcmVzc2VzIjpbIjB4YTQ3OGMyOTc1YWIxZWE4OWU4MTk2ODExZjUxYTdiN2FkZTMzZWIxMSIsIjB4ZUY4MzA1RTE0MGFjNTIwMjI1REFmMDUwZTJmNzFkNWZCY0M1NDNlNyIsIjB4OUI4ZThkRDkxNTEyNjBjMjFDQjZEN2NjNTkwNjdjZDhERjMwNkQ1OCIsIjB4MzhDMDAzOTI0N0EzMUYzOTM5YmFFNjVlOTUzNjEyMTI1Y0I4ODI2OCJdfQ..snapshot.org
    https://snapshot.org/#/playground/balance-of-with-thresholds?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4QkM0Q0EwRWRBNzY0N0E4YUI3QzIwNjFjMkUxMThBMThhOTM2ZjEzRCIsInN5bWJvbCI6IkJBWUMiLCJkZWNpbWFscyI6MCwidGhyZXNob2xkcyI6W3sidGhyZXNob2xkIjoxLCJ2b3RlcyI6MX0seyJ0aHJlc2hvbGQiOjQsInZvdGVzIjoyfSx7InRocmVzaG9sZCI6MTEsInZvdGVzIjozfSx7InRocmVzaG9sZCI6MjUsInZvdGVzIjo0fV19LCJuZXR3b3JrIjoiMSIsInNuYXBzaG90IjoiIiwiYWRkcmVzc2VzIjpbIjB4ZWQ0NzAxNWJiODA4MGI5Mzk5ZjlkMGRkZmM0MjdiOWNlZTJjYWFiMSIsIjB4ZjU2MzQ1MzM4Y2I0Y2RkYWY5MTVlYmVmM2JmZGU2M2U3MGZlMzA1MyIsIjB4N2IxNWU2YzQzOWIyN2E1NTNiNjVhOTkwNGNlNTcxZGE2NjkxYTBmYiIsIjB4OGQyZjNhNzZhNzZmMDU1ZDYyYTkzMTY3OGFiMTZiMDQyZTdiYWRlYiJdfQ..snapshot.org
    https://snapshot.org/#/playground/erc20-with-balance?query=eyJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4ODRjQThiYzc5OTcyNzJjN0NmQjREMENkM0Q1NWNkOTQyQjNjOTQxOSIsInN5bWJvbCI6IlJvbWUiLCJkZWNpbWFscyI6MTgsIm1pbkJhbGFuY2UiOjEwfSwibmV0d29yayI6IjEiLCJzbmFwc2hvdCI6IjE3MzM1ODIyIiwiYWRkcmVzc2VzIjpbIjB4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMGJhZGRhZCIsIjB4YTQ3OGMyOTc1YWIxZWE4OWU4MTk2ODExZjUxYTdiN2FkZTMzZWIxMSIsIjB4ZUY4MzA1RTE0MGFjNTIwMjI1REFmMDUwZTJmNzFkNWZCY0M1NDNlNyIsIjB4QkEyRTdGZWQ1OTdmZDBFM2U3MGY1MTMwQmNEYmJGRTA2YkI5NGZlMSIsIjB4NEM3OTA5ZDZGMDI5YjNhNTc5ODE0M0M4NDNGNGY4ZTUzNDFhMzQ3MyIsIjB4ODRjQThiYzc5OTcyNzJjN0NmQjREMENkM0Q1NWNkOTQyQjNjOTQxOSIsIjB4NzJhYzE3NjBkYWY1Mjk4NjQyMWIxNTUyYmRjYTA0NzA3ZTc4OTUwZSJdfQ..snapshot.org
    https://snapshot.org/#/playground/holds-tokens?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQkFMIiwiYWRkcmVzcyI6IjB4YmExMDAwMDA2MjVhMzc1NDQyMzk3OGE2MGM5MzE3YzU4YTQyNGUzRCIsImRlY2ltYWxzIjoxOH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHhDNWUzODIzM0NjMEQ3Q0ZmOTM0MGU2MTM5MzY3YUJBNDk4RUM5YjE4IiwiMHgyMDA5YTc1MmE1MEQzQ0RlNDg2ZDdiNTkyMTk0NDM3N0I3MjlFNzQ3IiwiMHg3YjE1ZTZjNDM5YjI3YTU1M2I2NWE5OTA0Y2U1NzFkYTY2OTFhMGZiIiwiMHg4ZDJmM2E3NmE3NmYwNTVkNjJhOTMxNjc4YWIxNmIwNDJlN2JhZGViIiwiMHhFRGU2NGE1NzFDRmU5OEI5MzYyNzFCOTM1YTk1NTYyMGYzODdFMDVBIl19snapshot.org
    https://snapshot.org/#/playground/math?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiR0hTVCIsIm9wZXJhbmRzIjpbeyJ0eXBlIjoic3RyYXRlZ3kiLCJzdHJhdGVneSI6eyJuYW1lIjoibXVsdGljaGFpbiIsInBhcmFtcyI6eyJzdHJhdGVnaWVzIjpbeyJuYW1lIjoiZXJjMjAtYmFsYW5jZS1vZiIsIm5ldHdvcmsiOiIxMzciLCJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4Mzg1ZWVhYzVjYjg1YTM4YTlhMDdhNzBjNzNlMGEzMjcxY2ZiNTRhNyIsImRlY2ltYWxzIjoxOH19LHsibmFtZSI6ImVyYzIwLWJhbGFuY2Utb2YiLCJuZXR3b3JrIjoiMSIsInBhcmFtcyI6eyJhZGRyZXNzIjoiMHgzZjM4MmRiZDk2MGUzYTliYmNlYWUyMjY1MWU4ODE1OGQyNzkxNTUwIiwiZGVjaW1hbHMiOjE4fX1dfX19LHsidHlwZSI6ImNvbnN0YW50IiwidmFsdWUiOjB9LHsidHlwZSI6ImNvbnN0YW50IiwidmFsdWUiOjF9XSwib3BlcmF0aW9uIjoiYS1pZi1sdC1iIn0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIxNzM0MjkzNyIsImFkZHJlc3NlcyI6WyIweDVDNTJjQzdjOTZiREU4NTk0ZTVCNzdENWI3NmQwNDJDQjVGYUU1ZjIiLCIweDREMTlDMGE1MzU3YkM0OGJlMDAxNzA5NWQzQzg3MUQ5YUZDM0YyMWQiLCIweGY1OTg2OTc1M2Y0MURiNzIwMTI3Q2ViOERiQjhhZkFGODkwMzBEZTQiXX0.snapshot.org
    https://snapshot.org/#/playground/whitelist?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQkFMIiwiYWRkcmVzcyI6IjB4YmExMDAwMDA2MjVhMzc1NDQyMzk3OGE2MGM5MzE3YzU4YTQyNGUzRCIsImRlY2ltYWxzIjoxOH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHhhNDc4YzI5NzVhYjFlYTg5ZTgxOTY4MTFmNTFhN2I3YWRlMzNlYjExIiwiMHhlRjgzMDVFMTQwYWM1MjAyMjVEQWYwNTBlMmY3MWQ1ZkJjQzU0M2U3IiwiMHgxRTFBNTFFMjVmMjgxNjMzNWNBNDM2RDY1ZTlBZjc2OTRCRTIzMmFkIl19snapshot.org
    https://snapshot.org/#/playground/whitelist?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQkFMIiwiYWRkcmVzcyI6IjB4YmExMDAwMDA2MjVhMzc1NDQyMzk3OGE2MGM5MzE3YzU4YTQyNGUzRCIsImRlY2ltYWxzIjoxOH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHhhNDc4YzI5NzVhYjFlYTg5ZTgxOTY4MTFmNTFhN2I3YWRlMzNlYjExIiwiMHhlRjgzMDVFMTQwYWM1MjAyMjVEQWYwNTBlMmY3MWQ1ZkJjQzU0M2U3IiwiMHgxRTFBNTFFMjVmMjgxNjMzNWNBNDM2RDY1ZTlBZjc2OTRCRTIzMmFkIl19snapshot.org
    https://snapshot.org/#/playground/math?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiR0hTVCIsIm9wZXJhbmRzIjpbeyJ0eXBlIjoic3RyYXRlZ3kiLCJzdHJhdGVneSI6eyJuYW1lIjoibXVsdGljaGFpbiIsInBhcmFtcyI6eyJzdHJhdGVnaWVzIjpbeyJuYW1lIjoiZXJjMjAtYmFsYW5jZS1vZiIsIm5ldHdvcmsiOiIxMzciLCJwYXJhbXMiOnsiYWRkcmVzcyI6IjB4Mzg1ZWVhYzVjYjg1YTM4YTlhMDdhNzBjNzNlMGEzMjcxY2ZiNTRhNyIsImRlY2ltYWxzIjoxOH19LHsibmFtZSI6ImVyYzIwLWJhbGFuY2Utb2YiLCJuZXR3b3JrIjoiMSIsInBhcmFtcyI6eyJhZGRyZXNzIjoiMHgzZjM4MmRiZDk2MGUzYTliYmNlYWUyMjY1MWU4ODE1OGQyNzkxNTUwIiwiZGVjaW1hbHMiOjE4fX1dfX19LHsidHlwZSI6ImNvbnN0YW50IiwidmFsdWUiOjB9LHsidHlwZSI6ImNvbnN0YW50IiwidmFsdWUiOjF9XSwib3BlcmF0aW9uIjoiYS1pZi1sdC1iIn0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIxNzM0MjkzNyIsImFkZHJlc3NlcyI6WyIweDVDNTJjQzdjOTZiREU4NTk0ZTVCNzdENWI3NmQwNDJDQjVGYUU1ZjIiLCIweDREMTlDMGE1MzU3YkM0OGJlMDAxNzA5NWQzQzg3MUQ5YUZDM0YyMWQiLCIweGY1OTg2OTc1M2Y0MURiNzIwMTI3Q2ViOERiQjhhZkFGODkwMzBEZTQiXX0.snapshot.org
    https://snapshot.org/#/playground/api-v2?query=eyJwYXJhbXMiOnsic3ltYm9sIjoiQkFMIiwiYWRkcmVzcyI6IjB4YmExMDAwMDA2MjVhMzc1NDQyMzk3OGE2MGM5MzE3YzU4YTQyNGUzRCIsImRlY2ltYWxzIjoxOH0sIm5ldHdvcmsiOiIxIiwic25hcHNob3QiOiIiLCJhZGRyZXNzZXMiOlsiMHhlRDJiY0MzMTA0RGE1RjVmOGZBOTg4RDZlOWZBRmQ3NEFlNjJmMzE5IiwiMHgzYzRCOEM1MkVkNGMyOWVFNDAyRDljOTFGZkFlMURiMkJBZGQyMjhEIiwiMHhkNjQ5YkFDZkY2NmYxQzg1NjE4YzUzNzZlZTRGMzhlNDNlRTUzYjYzIiwiMHg3MjYwMjJhOWZlMTMyMmZBOTU5MEZCMjQ0YjgxNjQ5MzZiQjAwNDg5IiwiMHhjNjY2NWViMzlkMjEwNmZiMURCRTU0YmYxOTE5MEY4MkZENTM1YzE5IiwiMHg2ZWYyMzc2ZmE2ZTEyZGFiYjNhM2VkMGZiNDRlNGZmMjk4NDdhZjY4IiwiMHg4OTQ0NmFGMDM2NTJjNTI1N2RCNUM4RTRFODU0OTVFQjc1NDE5NmM1Il19snapshot.org
    GitHub - snapshot-labs-archived/snapshot-spacesGitHub
    Logo
    Logo
    Logo
    Logo
    Logo
    Logo
    Logo
    Logo
    Logo
    here
    npm install @snapshot-labs/sx@beta
    yarn add @snapshot-labs/sx@beta
    bun add @snapshot-labs/sx@beta
    import { clients, evmSepolia } from '@snapshot-labs/sx';
    
    const clientConfig = { networkConfig: evmSepolia }
    
    const client = new clients.EthereumTx(clientConfig);
    const ethSigClient = new clients.EthereumSig(clientConfig);
    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_GOERLI } });
    
    const clientConfig = {
      starkProvider,
      manaUrl,
      ethUrl
    };
    
    const client = new clients.StarkNetTx(clientConfig);
    const starkSigClient = new clients.StarkNetSig(clientConfig);
    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);
    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);
    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_GOERLI } });
    
    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);
    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_GOERLI } });
    
    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);
    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);
    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);
    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_GOERLI } });
    
    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);
    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_GOERLI } });
    
    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);
    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);
    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);
    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_GOERLI } });
    
    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);
    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_GOERLI } });
    
    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);
    import {
      starknetGoerli1,
      getStarknetStrategy,
    } from '@snapshot-labs/sx';
    
    const clientConfig = {
      starkProvider,
      ethUrl,
      networkConfig: starknetGoerli1
    };
    
    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
    );
    Logo
    Logo
    https://github.com/snapshot-labs/score-api/blob/master/src/strategies/validations/validation.ts
    import snapshot from '@snapshot-labs/snapshot.js';
    import { DEFAULT_SUPPORTED_PROTOCOLS } from '../constants';
    import { Protocol, Snapshot } from '../types';
    
    export default class Validation {
      public id = '';
      public github = '';
      public version = '';
      public title = '';
      public description = '';
      public supportedProtocols: Protocol[] = DEFAULT_SUPPORTED_PROTOCOLS;
      public hasInnerStrategies = false;
    
      public author: string;
      public space: string;
      public network: string;
      public snapshot: Snapshot;
      public params: any;
    
      constructor(
        author: string,
        space: string,
        network: string,
        snapshot: Snapshot,
        params: any
      ) {
        this.author = author;
        this.space = space;
        this.network = network;
        this.snapshot = snapshot;
        this.params = params;
      }
    
      // TODO: validate invalid networks
      async validate(customAuthor = this.author): Promise<boolean> {
        try {
          this.validateAddressType(customAuthor);
        } catch (e) {
          return false;
        }
    
        return this.doValidate(customAuthor);
      }
    
      // Abstract method to be implemented by subclasses
      // This contains the actual validation logic without global/commons validation
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      protected async doValidate(_customAuthor: string): Promise<boolean> {
        return true;
      }
    
      private validateAddressType(address: string): boolean {
        try {
          const formattedAddress = snapshot.utils.getFormattedAddress(address);
    
          if (
            (snapshot.utils.isEvmAddress(formattedAddress) &&
              this.supportedProtocols.includes('evm')) ||
            (snapshot.utils.isStarknetAddress(formattedAddress) &&
              this.supportedProtocols.includes('starknet'))
          ) {
            return true;
          }
        } catch (error) {
          // If isStarknetAddress throws an error, fall through to the standard error
        }
    
        throw new Error(
          `Address "${address}" is not a valid ${this.supportedProtocols.join(
            ' or '
          )} address`
        );
      }
    }
    

    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

    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.

    Endpoints

    Production hub

    Demo hub

    Queries

    Get a single space

    Arguments

    id string

    Example

    Try on

    Get multiple spaces

    Arguments

    first number skip number where: - idstring - id_inarray orderBy string orderDirection asc or desc

    Example

    Try on

    Get a single proposal

    Arguments

    id string‌

    Example

    Try on

    Get proposals

    Arguments

    first number skip number where: - idstring - id_inarray - space:string - space_in:array - author:string - author_in:array - network: string - network_in: array - state: array orderBy string orderDirection asc or desc

    Example

    Try on

    Get a single vote

    Choices are indexed 1-based. The first choice has index 1.

    Arguments

    id string‌

    Example

    Try on

    Get votes

    Choices are indexed 1-based. The first choice has index 1.

    Arguments

    first number skip number where: - idstring - id_inarray - space:string - space_in:array - voter:string - voter_in:array - proposal: string - proposal_in: array orderBy string orderDirection asc or desc

    Example

    Try on

    Get voting power

    Arguments

    voter string space string proposal string

    Example

    Try on

    Get follows

    Arguments

    first number skip number where: - idstring - id_inarray - space:string - space_in:array - follower:string - follower_in:array orderBy string orderDirection asc or desc

    Example

    Try on

    Get users

    Arguments

    first number skip number where: - id:string - id_in:array orderBy string orderDirection asc or desc

    Example

    Try on

    Get roles

    Arguments

    first number skip number where: - address:string orderBy string orderDirection asc or desc

    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: - timestampstring - spacearray - space_in:array - type:string - type_in:string orderBy string orderDirection asc or desc

    Example

    Try on

    GraphiQL
    GraphiQL
    GraphiQL
    GraphiQL
    GraphiQL
    GraphiQL
    GraphiQL
    GraphiQL
    GraphiQL
    GraphiQL
    https://hub.snapshot.org/graphql
    https://testnet.hub.snapshot.org/graphql
    query {
      space(id: "yam.eth") {
        id
        name
        about
        network
        symbol
        members
      }
    }
    {
      "data": {
        "space": {
          "id": "yam.eth",
          "name": "Yam Finance",
          "about": "",
          "network": "1",
          "symbol": "YAM",
          "members": [
            "0x683A78bA1f6b25E29fbBC9Cd1BFA29A51520De84",
            "0x9Ebc8AD4011C7f559743Eb25705CCF5A9B58D0bc",
            "0xC3edCBe0F93a6258c3933e86fFaA3bcF12F8D695",
            "0xbdac5657eDd13F47C3DD924eAa36Cf1Ec49672cc",
            "0xEC3281124d4c2FCA8A88e3076C1E7749CfEcb7F2"
          ]
        }
      }
    }
    query {
      spaces(
        first: 20,
        skip: 0,
        orderBy: "created",
        orderDirection: asc
      ) {
        id
        name
        about
        network
        symbol
        strategies {
          name
          params
        }
        admins
        members
        filters {
          minScore
          onlyMembers
        }
        plugins
      }
    }
    {
      "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"
              }
            }
          }
        ]
      }
    }
    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
        }
      }
    }
    {
      "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"
          }
        }
      }
    }
    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
        }
      }
    }
    {
      "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"
            }
          },
          ...
        ]
      }
    }
    query {
      vote (
        id: "QmeU7ct9Y4KLrh6F6mbT1eJNMkeQKMSnSujEfMCfbRLCMp"
      ) {
        id
        voter
        vp
        vp_by_strategy
        vp_state
        created
        proposal {
          id
        }
        choice
        space {
          id
        }
      }
    }
    {
      "data": {
        "vote": {
          "id": "QmeU7ct9Y4KLrh6F6mbT1eJNMkeQKMSnSujEfMCfbRLCMp",
          "voter": "0x96176C25803Ce4cF046aa74895646D8514Ea1611",
          "created": 1621183227,
          "proposal": {
            "id": "QmPvbwguLfcVryzBRrbY4Pb9bCtxURagdv1XjhtFLf3wHj"
          },
          "choice": 1,
          "space": {
            "id": "spookyswap.eth"
          }
        }
      }
    }
    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
        }
      }
    }
    
    {
      "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"
            }
          },
          ...
        ]
      }
    }
    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"
        }
      }
    }
    query {
      follows(
        first: 10,
        where: {
          follower: "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7"
        }
      ) {
        follower
        space {
          id
        }
        created
      }
    }
    {
      "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
          }
        ]
      }
    }
    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"
          }
        ]
      }
    }
    query {
      roles(where:{address:"0xE029Ef62e47E394BC852EFf633eB5aa4A223ECa6"}) {
        space
        permissions
      }
    }
    {
      "data": {
        "roles": [
          {
            "space": "fabien.eth",
            "permissions": [
              "moderator"
            ]
          },
          {
            "space": "zuzazuber.eth",
            "permissions": [
              "moderator"
            ]
          }
        ]
      }
    }
    query {
      messages (
        first: 20
        where: { space: "ens.eth" }
        orderBy: "mci"
        orderDirection: desc
      ) {
        id
        address
        ipfs
        receipt
        type
        mci
      }
    }
    
    {
      "data": {
         "messages": [
          {
            "id": "0x7d99e11ffe3a333229bdcda59866bc5b66b0b7e5c4b5353862a3b8ccbfa26c83",
            "address": "0xdbB1740e424C41E935599634828f5E5c4dF23D43",
            "ipfs": "bafkreiduchkd35btpv3uttrg3f2kx3g52uh44tiruawip6xlgvjbcuo4w4",
            "receipt": "0xaa4d557085872ed82b8bfba5af4247650395eda46ed61672d940eabc74bcde67415db974361f4b24361565d2f9ee6ee636ae935a1dc830c207204ea29ad498741b",
            "type": "follow"
            "mci": 1345000
          },
          ...
        ]
      }
    }
    Not found
    Not found
    https://hub.snapshot.org/graphqlhub.snapshot.org
    https://github.com/snapshot-labs/score-api/blob/master/src/strategies/validations/passport-gated/index.ts
    import snapshot from '@snapshot-labs/snapshot.js';
    import { customFetch } from '../../utils';
    
    import STAMPS from './stampsMetadata.json';
    import Validation from '../validation';
    
    // Create one from https://scorer.gitcoin.co/#/dashboard/api-keys
    const API_KEY = process.env.PASSPORT_API_KEY || '';
    const SCORER_ID = process.env.PASSPORT_SCORER_ID || '';
    
    const headers = API_KEY
      ? {
          'Content-Type': 'application/json',
          'X-API-Key': API_KEY
        }
      : undefined;
    
    // const GET_STAMPS_METADATA_URI = `https://api.scorer.gitcoin.co/registry/stamp-metadata`;
    const GET_PASSPORT_STAMPS_URI = `https://api.scorer.gitcoin.co/registry/stamps/`;
    const GET_PASSPORT_SCORE_URI = `https://api.scorer.gitcoin.co/registry/score/${SCORER_ID}/`;
    const POST_SUBMIT_PASSPORT_URI = `https://api.scorer.gitcoin.co/registry/submit-passport`;
    
    const PASSPORT_SCORER_MAX_ATTEMPTS = 2;
    
    const stampCredentials = STAMPS.map(stamp => {
      return {
        id: stamp.id,
        name: stamp.name,
        description: stamp.description,
        credentials: stamp.groups
          .flatMap(group => group.stamps)
          .map(credential => credential.name)
      };
    });
    
    // Useful to get stamp metadata and update `stampsMetata.json`
    // console.log('stampCredentials', JSON.stringify(stampCredentials.map((s) => ({"const": s.id, title: s.name}))));
    
    function hasValidIssuanceAndExpiration(credential: any, proposalTs: string) {
      const issuanceDate = Number(
        new Date(credential.issuanceDate).getTime() / 1000
      ).toFixed(0);
      const expirationDate = Number(
        new Date(credential.expirationDate).getTime() / 1000
      ).toFixed(0);
      if (issuanceDate <= proposalTs && expirationDate >= proposalTs) {
        return true;
      }
      return false;
    }
    
    function hasStampCredential(stampId: string, credentials: Array<string>) {
      const stamp = stampCredentials.find(stamp => stamp.id === stampId);
      if (!stamp) {
        console.log('[passport] Stamp not supported', stampId);
        throw new Error('Stamp not supported');
      }
      return credentials.some(credential => stamp.credentials.includes(credential));
    }
    
    async function validateStamps(
      currentAddress: string,
      operator: string,
      proposalTs: string,
      requiredStamps: Array<string> = []
    ): Promise<boolean> {
      if (requiredStamps.length === 0) return true;
    
      const stampsResponse = await customFetch(
        GET_PASSPORT_STAMPS_URI + currentAddress,
        {
          headers
        }
      );
      const stampsData = await stampsResponse.json();
    
      if (!stampsData?.items) {
        console.log('[passport] Stamps Unknown error', stampsData);
        throw new Error('Unknown error');
      }
      if (stampsData.items.length === 0) return false;
    
      // check expiration for all stamps
      const validStamps = stampsData.items
        .filter((stamp: any) =>
          hasValidIssuanceAndExpiration(stamp.credential, proposalTs)
        )
        .map((stamp: any) => stamp.credential.credentialSubject.provider);
    
      if (operator === 'AND') {
        return requiredStamps.every(stampId =>
          hasStampCredential(stampId, validStamps)
        );
      } else if (operator === 'OR') {
        return requiredStamps.some(stampId =>
          hasStampCredential(stampId, validStamps)
        );
      }
      return false;
    }
    
    function evalPassportScore(scoreData: any, minimumThreshold = 0): boolean {
      // scoreData.evidence?.type === 'ThresholdScoreCheck' -> Returned if using Boolean Unique Humanity Scorer (should not be used)
      if (scoreData.evidence?.type === 'ThresholdScoreCheck') {
        return (
          Number(scoreData.evidence.rawScore) > Number(scoreData.evidence.threshold)
        );
      }
      // scoreData.score -> Returned if using Unique Humanity Score
      return Number(scoreData.score) >= minimumThreshold;
    }
    
    async function validatePassportScore(
      currentAddress: string,
      scoreThreshold: number
    ): Promise<boolean> {
      // always hit the /submit-passport endpoint to get the latest passport score
      const submittedPassport = await customFetch(POST_SUBMIT_PASSPORT_URI, {
        headers,
        method: 'POST',
        body: JSON.stringify({ address: currentAddress, scorer_id: SCORER_ID })
      });
      const submissionData =
        submittedPassport.ok && (await submittedPassport.json());
    
      if (!submittedPassport.ok) {
        const reason = !SCORER_ID
          ? 'SCORER_ID missing'
          : submittedPassport.statusText;
        console.log('[passport] Scorer error', reason);
        throw new Error(`Scorer error: ${reason}`);
      }
    
      // Scorer done calculating passport score during submission
      if (submittedPassport.ok && submissionData.status === 'DONE') {
        return evalPassportScore(submissionData, scoreThreshold);
      }
    
      // Try to fetch Passport Score if still processing (submittedPassport.status === 'PROCESSING')
      for (let i = 0; i < PASSPORT_SCORER_MAX_ATTEMPTS; i++) {
        const scoreResponse = await customFetch(
          GET_PASSPORT_SCORE_URI + currentAddress,
          {
            headers
          }
        );
        const scoreData = await scoreResponse.json();
    
        if (scoreResponse.ok && scoreData.status === 'DONE') {
          return evalPassportScore(scoreData, scoreThreshold);
        }
        console.log(
          `[passport] Waiting for scorer... (${i}/${PASSPORT_SCORER_MAX_ATTEMPTS})`
        );
        await snapshot.utils.sleep(3e3);
      }
      const reason =
        'Failed to fetch Passport Score. Reached PASSPORT_SCORER_MAX_ATTEMPTS';
      console.log('[passport] Scorer error', reason);
      throw new Error(`Scorer error: ${reason}`);
    }
    
    export default class extends Validation {
      public id = 'passport-gated';
      public github = 'snapshot-labs';
      public version = '1.0.0';
      public title = 'Gitcoin Passport Gated';
      public description =
        'Protect your proposals from spam and vote manipulation by requiring users to have a valid Gitcoin Passport.';
    
      protected async doValidate(customAuthor: string): Promise<boolean> {
        const requiredStamps = this.params.stamps || [];
        const operator = this.params.operator;
        const scoreThreshold = this.params.scoreThreshold || 0;
    
        if (requiredStamps.length > 0 && (!operator || operator === 'NONE'))
          throw new Error('Operator is required when selecting required stamps');
    
        const provider = snapshot.utils.getProvider(this.network);
        const proposalTs = (await provider.getBlock(this.snapshot)).timestamp;
        const validStamps = await validateStamps(
          customAuthor,
          operator,
          proposalTs,
          requiredStamps
        );
    
        if (scoreThreshold === 0) {
          return validStamps;
        }
    
        const validScore = await validatePassportScore(
          customAuthor,
          scoreThreshold
        );
    
        return validStamps && validScore;
      }
    }