snapshot
Blog
  • Welcome to Snapshot docs
  • User guides
    • Spaces
      • What is a space?
      • Create a space
        • Register an ENS domain
        • Alternative way to create a space
      • Settings
      • Sub-spaces
      • Space verification
      • Space hibernation
      • Add a custom domain
      • Add a skin
      • Space roles
      • Space badges
      • Snapshot Pro
      • Space handbook
        • Most common
        • Voting threshold
        • Anti-whale
        • Sybil resistance, scam & spam prevention
        • Liquidity / staking pool
        • Delegation
        • NFT voting
          • Most common: ERC-721
          • Multi-token: ERC-1155
          • POAP - Proof of Attendance
        • Custom calculations
    • Create a proposal
    • Voting
      • Vote on a proposal
      • Delegate your voting power
    • Voting strategies
    • Validation strategies
    • Using Safe multi-sig
    • Delegation
  • Developer Guides
    • Create a voting strategy
    • Create a validation strategy
    • Identify integrator activity
    • Add a network
  • Tools
    • Tools overview
    • Snapshot.js
    • API
      • API Keys
    • Webhooks
    • Subgraphs
    • Mobile notifications
    • Bots
  • Snapshot X
    • Overview
    • User guides
      • Create a space
      • Proposals
      • Voting
      • Safe execution setup
    • Protocol
      • Overview
      • Space actions
      • Space controller actions
      • Authenticators
      • Proposal validations
      • Voting strategies
      • Starknet specifics
      • Execution strategies
      • Audits
    • Services
      • Architecture
      • API
      • SX.js
      • UI
      • Mana
  • V1 interface
    • Email notifications
    • Plugins
      • What is a plugin?
      • Create a plugin
      • oSnap
      • SafeSnap
      • POAP
      • Quorum
      • Domino notifications
      • Galxe
    • Boost
  • Community
    • Help center
    • Discord
    • X (Twitter)
    • GitHub
Powered by GitBook
On this page
  • 1. Fork the snapshot-strategies repository
  • 2. Duplicate the erc20-balance-of strategy folder
  • 3. Write the logic for your strategy
  • 4. Test the strategy locally
  • 5. Review the checklist
  • 6. Create a pull request
  • 7. Try it out!

Was this helpful?

Edit on GitHub
Export as PDF
  1. Developer Guides

Create a voting strategy

Learn how to create a new custom voting strategy.

PreviousDelegationNextCreate a validation strategy

Last updated 1 year ago

Was this helpful?

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 snapshot-strategies repository

Create a fork of the snapshot-strategies repository:

2. Duplicate the erc20-balance-of strategy folder

Navigate to strategies directory, duplicate the erc20-balance-of directory and rename it to the chosen name for your new strategy.

└── src
    └── strategies
        └── erc20-balance-of

3. Write the logic for your strategy

There are several files you need to edit:

a. index.ts

This file defines the logic for calculation of the voting power. As an example, the erc20-balance-of is taking as parameters space, network, provider, addresses, options and snapshot in order to be able to retrieve the balances of the token specified in the options parameter for the provided addresses:


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))
    ])
  );
}

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:

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$ref": "#/definitions/Strategy",
  "definitions": {
    "Strategy": {
      "title": "Strategy",
      "type": "object",
      "properties": {
        "symbol": {
          "type": "string",
          "title": "Symbol",
          "examples": ["e.g. UNI"],
          "maxLength": 16
        },
        "address": {
          "type": "string",
          "title": "Contract address",
          "examples": ["e.g. 0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984"],
          "pattern": "^0x[a-fA-F0-9]{40}$",
          "minLength": 42,
          "maxLength": 42
        },
        "decimals": {
          "type": "number",
          "title": "Decimals",
          "examples": ["e.g. 18"]
        }
      },
      "required": ["address", "decimals"],
      "additionalProperties": false
    }
  }
}

d. ./snapshot-strategies/src/strategies/index.ts

import * as youStrategyName from './your-strategy-name';

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:

[
  {
    "name": "Example query",
    "strategy": {
      "name": "erc20-balance-of",
      "params": {
        "address": "0x6b175474e89094c44da98b954eedeac495271d0f",
        "symbol": "DAI",
        "decimals": 18
      }
    },
    "network": "1",
    "addresses": [
      "0xA478c2975Ab1Ea89e8196811F51A7B7Ade33eB11",
      "0xeF8305E140ac520225DAf050e2f71d5fBcC543e7",
      "0x1E1A51E25f2816335cA436D65e9Af7694BE232ad",
      "0x1F717Ce8ff07597ee7c408b5623dF40AaAf1787C",
      "0x1c7a9275F2BD5a260A9c31069F77d53473b8ae2e",
      "0x1d5E65a087eBc3d03a294412E46CE5D6882969f4",
      "0x1f254336E5c46639A851b9CfC165697150a6c327",
      "0x2ec3F80BeDA63Ede96BA20375032CDD3aAfb3030",
      "0x4AcBcA6BE2f8D2540bBF4CA77E45dA0A4a095Fa2",
      "0x4F3D348a6D09837Ae7961B1E0cEe2cc118cec777",
      "0x6D7f23A509E212Ba7773EC1b2505d1A134f54fbe",
      "0x07a1f6fc89223c5ebD4e4ddaE89Ac97629856A0f",
      "0x8d5F05270da470e015b67Ab5042BDbE2D2FEFB48",
      "0x8d07D225a769b7Af3A923481E1FdF49180e6A265",
      "0x8f60501dE5b9b01F9EAf1214dbE1924aA97F7fd0",
      "0x9B8e8dD9151260c21CB6D7cc59067cd8DF306D58",
      "0x17ea92D6FfbAA1c7F6B117c1E9D0c88ABdc8b84C",
      "0x38C0039247A31F3939baE65e953612125cB88268"
    ],
    "snapshot": 11437846
  }
]

e. README.md

Write the description of how the strategy works and provide an example of the setup. It will be displayed on the strategy's details page.

This is the most common strategy, it returns the balances of the voters for a specific ERC20 token.

Here is an example of parameters:

{
  "address": "0x6b175474e89094c44da98b954eedeac495271d0f",
  "symbol": "DAI",
  "decimals": 18
}

4. Test the strategy locally

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

npm run test --strategy=<STRATEGY NAME> // replace <STRATEGY NAME>

5. Review the checklist

6. Create a pull request

The review can take the team up to 72 hours, so please be patient 🙏

7. Try it out!

Import and declare you new strategy in the file:

It will trigger the tests which you can find in . If you get any errors read them carefully as they should point directly to the problem.

Ensure you meet the requirements for adding a new strategy by reviewing the checklist for adding a new strategy which can be found at:

Create a Pull Request with the above changes on the original repo.

After the PR has been merged, you will need to wait for the release of a new version of which can take a couple of days. Once it's deployed you can move on to the next step.

Head to the strategy's details page and click Playground button. Follow the instructions from to see if it works as you intended.

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

🎉
index.ts
this file
https://github.com/snapshot-labs/snapshot-strategies#checklist-for-adding-a-new-strategy
snapshot-strategies
Snapshot
https://github.com/snapshot-labs/snapshot-strategies
Testing a voting strategy