TypeScript SDK

Overview

The Prop House SDK simplifies interactions with and querying of the Prop House Protocol from any modern browser or Node.js.

Features

Installation

npm i @prophouse/sdk

Usage

Initialization

The PropHouse class acts as the entry point for all Prop House protocol interactions.

To create a PropHouse instance, provide the chain ID and a signer or provider:

import { ChainId, PropHouse } from '@prophouse/sdk';

const propHouse = new PropHouse({
  evmChainId: ChainId.EthereumMainnet,
  evm: signer, // Alternatively, you can pass a provider if you don't need access to write methods.
});

House & Round Creation

A round can be created on new or existing house in a single transaction. The round can be fully funded, partially funded, or not funded at all during creation. The round can always be funded at a later time.

When funding a round, the PropHouse contract must be approved to spend tokens prior to deposit. The address of the PropHouse contract can be found at propHouse.contract.address. If funding with ETH, no approval is necessary.

We currently support the following voting strategies:

  • BALANCE_OF_ERC721 - Voting power is determined by a user's ERC721 token balance. An optional voting power multiplier can be provided.

  • BALANCE_OF_ERC1155 - Voting power is determined by a user's ERC1155 token balance for a specific token ID. An optional voting power multiplier can be provided.

  • BALANCE_OF_ERC20 - Voting power is determined by a user's ERC20 token balance. An optional voting power multiplier can be provided.

  • ALLOWLIST - Voting power is determined by the addresses and voting power values provided. Currently, we support an allowlist of up to 2,000 members.

  • CHECKPOINTABLE_ERC721 - Voting power is determined by a user's checkpointed ERC721 voting power, which allows delegates who don't actually hold an ERC721 balance to vote. Currently, this strategy ONLY supports the mainnet Nouns ERC721 token.

Create a round on a new house

import { AssetType, HouseType, RoundType, VotingStrategyType } from '@prophouse/sdk';
import { ethers } from 'ethers';

const response = await propHouse.createRoundOnNewHouse(
  {
    houseType: HouseType.COMMUNITY,
    config: {
      contractURI: 'ipfs://bafkreignr3s2dplrfey3yiob2es4fvkgnlo2k7vjuigwswibbwpldvr5wi',
    },
  },
  {
    roundType: RoundType.TIMED,
    title: 'Test Round',
    description: 'A round used for testing purposes',
    config: {
      // Offer 5 ETH split between 10 winners, funded later.
      awards: [
        {
          assetType: AssetType.ETH,
          amount: ethers.utils.parseEther('5'),
        },
      ],
      // Voting power for this round is determined by a user's ERC721 balance.
      votingStrategies: [
        {
          strategyType: VotingStrategyType.BALANCE_OF_ERC721,
          assetType: AssetType.ERC721,
          address: MY_ERC721_TOKEN_ADDRESS,
        },
      ],
      proposalPeriodStartUnixTimestamp: now + ONE_DAY_SECONDS,
      proposalPeriodDurationSecs: ONE_DAY_SECONDS * 10,
      votePeriodDurationSecs: ONE_DAY_SECONDS * 2,
      winnerCount: 10,
    },
  },
);

Create a round on an existing house

import { AssetType, HouseType, RoundType, VotingStrategyType } from '@prophouse/sdk';
import { ethers } from 'ethers';

const response = await propHouse.createRoundOnExistingHouse(
  HOUSE_ADDRESS,
  {
    roundType: RoundType.TIMED,
    title: 'Test Round',
    description: 'A round used for testing purposes',
    config: {
      // Offer 5 ETH each to 2 winners, funded later.
      awards: [
        {
          assetType: AssetType.ETH,
          amount: ethers.utils.parseEther('5'),
        },
        {
          assetType: AssetType.ETH,
          amount: ethers.utils.parseEther('5'),
        },
      ],
      // Voting power for this round is determined by a user's ERC721 balance.
      votingStrategies: [
        {
          strategyType: VotingStrategyType.BALANCE_OF_ERC721,
          assetType: AssetType.ERC721,
          address: MY_ERC721_TOKEN_ADDRESS,
        },
      ],
      proposalPeriodStartUnixTimestamp: now + ONE_DAY_SECONDS,
      proposalPeriodDurationSecs: ONE_DAY_SECONDS * 10,
      votePeriodDurationSecs: ONE_DAY_SECONDS * 2,
      winnerCount: 2,
    },
  },
);

Create and fund a round on a new house

import { Asset, AssetType, HouseType, RoundType, VotingStrategyType } from '@prophouse/sdk';

// Offer a Noun to 1 winner.
const assets: Asset[] = [
  {
    assetType: AssetType.ERC721,
    address: NOUNS_TOKEN_ADDRESS,
    tokenId: 60,
  },
];

const response = await propHouse.createAndFundRoundOnNewHouse(
  {
    houseType: HouseType.COMMUNITY,
    config: {
      contractURI: 'ipfs://bafkreignr3s2dplrfey3yiob2es4fvkgnlo2k7vjuigwswibbwpldvr5wi',
    },
  },
  {
    roundType: RoundType.TIMED,
    title: 'Test Round',
    description: 'A round used for testing purposes',
    config: {
      awards: assets,
      // Voting power for this round is determined by a user's Nouns voting power
      // with a multiplier of 10.
      votingStrategies: [
        {
          strategyType: VotingStrategyType.CHECKPOINTABLE_ERC721,
          assetType: AssetType.ERC721,
          address: NOUNS_TOKEN_ADDRESS,
          multiplier: 10,
        },
      ],
      proposalPeriodStartUnixTimestamp: now + ONE_DAY_SECONDS,
      proposalPeriodDurationSecs: ONE_DAY_SECONDS * 10,
      votePeriodDurationSecs: ONE_DAY_SECONDS * 2,
      winnerCount: 10,
    },
  },
  assets,
);

Create and fund a round on an existing house

import { Asset, AssetType, HouseType, RoundType, VotingStrategyType } from '@prophouse/sdk';

// Offer a Noun to 1 winner.
const assets: Asset[] = [
  {
    assetType: AssetType.ERC721,
    address: NOUNS_TOKEN_ADDRESS,
    tokenId: 60,
  },
];

const response = await propHouse.createAndFundRoundOnExistingHouse(
  HOUSE_ADDRESS,
  {
    roundType: RoundType.TIMED,
    title: 'Test Round',
    description: 'A round used for testing purposes',
    config: {
      awards: assets,
      // Voting power for this round is determined by a user's Nouns voting power
      // with a multiplier of 10.
      votingStrategies: [
        {
          strategyType: VotingStrategyType.CHECKPOINTABLE_ERC721,
          assetType: AssetType.ERC721,
          address: NOUNS_TOKEN_ADDRESS,
          multiplier: 10,
        },
      ],
      proposalPeriodStartUnixTimestamp: now + ONE_DAY_SECONDS,
      proposalPeriodDurationSecs: ONE_DAY_SECONDS * 10,
      votePeriodDurationSecs: ONE_DAY_SECONDS * 2,
      winnerCount: 10,
    },
  },
  assets,
);

Round Asset Management

If a round is not fully funded during creation, it can be funded by anyone at any time. The protocol records depositor addresses and amounts, which allows depositors to recoup their assets, and ONLY their assets, if a round is cancelled.

Ensure that the PropHouse contract has been approved to spend tokens prior to deposit.

Deposit a single asset to a round

import { AssetType } from '@prophouse/sdk';

// ETH
await propHouse.depositTo(ROUND_ADDRESS, {
  assetType: AssetType.ETH,
  amount: ethers.utils.parseEther('1'),
});

// ERC20
await propHouse.depositTo(ROUND_ADDRESS, {
  assetType: AssetType.ERC20,
  address: ERC20_TOKEN_ADDRESS,
  amount: ERC20_TOKEN_AMOUNT,
});

// ERC721
await propHouse.depositTo(ROUND_ADDRESS, {
  assetType: AssetType.ERC721,
  address: ERC721_TOKEN_ADDRESS,
  tokenId: ERC721_TOKEN_ID,
});

// ERC1155
await propHouse.depositTo(ROUND_ADDRESS, {
  assetType: AssetType.ERC1155,
  address: ERC1155_TOKEN_ADDRESS,
  amount: ERC1155_TOKEN_AMOUNT,
  tokenId: ERC1155_TOKEN_ID,
});

Deposit many assets to a round

import { AssetType } from '@prophouse/sdk';

await propHouse.batchDepositTo(ROUND_ADDRESS, [
  {
    assetType: AssetType.ETH,
    amount: ethers.utils.parseEther('1'),
  },
  {
    assetType: AssetType.ERC20,
    address: ERC20_TOKEN_ADDRESS,
    amount: ERC20_TOKEN_AMOUNT,
  },
  {
    assetType: AssetType.ERC721,
    address: ERC721_TOKEN_ADDRESS,
    tokenId: ERC721_TOKEN_ID,
  },
  {
    assetType: AssetType.ERC1155,
    address: ERC1155_TOKEN_ADDRESS,
    amount: ERC1155_TOKEN_AMOUNT,
    tokenId: ERC1155_TOKEN_ID,
  },
]);

Proposal & Vote Submission

We leverage Starknet meta-transactions to greatly reduce the cost of proposal and vote submission. By default, the Prop House SDK sends proposals and votes through the relayer that's operated by the Prop House team.

Timed Round

Proposal Submission

This method (proposeViaSignature) signs a proposal message using the provided signer and submits it to the Starknet meta-transaction relayer.

const result = await propHouse.round.timed.proposeViaSignature({
  round, // The round address
  metadataUri: 'ipfs://bafkreicruoxpnmcgd2uveedyqnvxojhgksunix3jusrjw6rlnnt7onipci',
});

Vote Submission

The method (voteViaSignature) signs a vote message using the provided signer and submits it to the Starknet meta-transaction relayer.

const result = await propHouse.round.timed.voteViaSignature({
  round, // The round address
  votes: [
    {
      proposalId: 1,
      votingPower: 10,
    },
    {
      proposalId: 9,
      votingPower: 8,
    },
  ],
});

Typed Subgraph Client

A query property is exposed on the PropHouse instance, which provides a variety of methods for querying house and round state.

Alternatively, the QueryWrapper class can be instantiated on its own:

import { ChainId, QueryWrapper } from '@prophouse/sdk';

const query = new QueryWrapper(ChainId.EthereumMainnet);

This class is called the QueryWrapper because it wraps queries to multiple chains under the hood (Ethereum & Starknet).


General


Get global stats

Get global protocol stats, including the number of total rounds and proposals created, as well as unique proposers and voters.

const globalStats = await propHouse.query.getGlobalStats();

Houses


Get houses

Get paginated houses. Accepts an optional pagination and ordering configuration.

const houses = await propHouse.query.getHouses();

Get houses where account has creator permissions

Get paginated houses where the provided account has creator permissions. Accepts the account address, and an optional pagination and ordering configuration.

const houses = await propHouse.query.getHousesWhereAccountHasCreatorPermissions(
  accountAddress,
);

Get houses where account is owner

Get paginated houses where the provided account is the house owner. Accepts the account address, and an optional pagination and ordering configuration.

const houses = await propHouse.query.getHousesWhereAccountIsOwner(
  accountAddress,
);

Get houses where account is owner or has creator permissions

Get paginated houses where the provided account is the house owner or has creator permissions. Accepts the account address, and an optional pagination and ordering configuration.

const houses = await propHouse.query.getHousesWhereAccountIsOwnerOrHasCreatorPermissions(
  accountAddress,
);

Get house

Get a single house. Accepts the house address.

const house = await propHouse.query.getHouse(houseAddress);

Rounds


Get rounds

Get paginated rounds. Accepts an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRounds();

Get rounds for house

Get paginated rounds on the provided house. Accepts the house address, and an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRoundsForHouse(houseAddress);

Get rounds where title contains

Get paginated rounds where the title contains the provided partial title text. Accepts the partial title text, and an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRoundsWhereTitleContains(
  partialTitleText,
);

Get rounds managed by account

Get paginated rounds currently managed by the provided account address. Accepts the account address, and an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRoundsManagedByAccount(
  accountAddress,
);

Get rounds relevant to account

Get paginated rounds in which the provided account address is a proposer or voter. Accepts the account address, and an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRoundsRelevantToAccount(
  accountAddress,
);

Get rounds with house info relevant to account

Get paginated rounds, including their house information, in which the provided account address is a proposer or voter. Accepts the account address, and an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRoundsWithHouseInfoRelevantToAccount(
  accountAddress,
);

Get rounds with house info

Get paginated rounds, including their house information. Accepts an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRoundsWithHouseInfo();

Get rounds with house info where title contains

Get paginated rounds, including their house information, where the title contains the provided partial title text. Accepts the partial title text, and an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRoundsWithHouseInfoWhereTitleContains(
  partialTitleText,
);

Get rounds with house info managed by account

Get paginated rounds, including their house information, in which the provided account address is the round manager. Accepts the account address, and an optional pagination and ordering configuration.

const rounds = await propHouse.query.getRoundsWithHouseInfoManagedByAccount(
  accountAddress,
);

Get round

Get a single round. Accepts the round address.

const round = await propHouse.query.getRound(roundAddress);

Get round with house info

Get a single round, including its house information. Accepts the round address.

const round = await propHouse.query.getRoundWithHouseInfo(roundAddress);

Get round Starknet address

Get a round's Starknet address. Accepts the round address.

const starknetRoundAddress = await propHouse.query.getStarknetRoundAddress(roundAddress);

Get round merkle root

Get the merkle root containing the winner information for a given round. Returns null if the round hasn't been finalized. Accepts the round address.

const merkleRoot = await propHouse.query.getRoundMerkleRoot(roundAddress);

Proposals


Get proposals

Get paginated proposals. Accepts an optional pagination and ordering configuration.

const proposals = await propHouse.query.getProposals();

Get proposals by account

Get paginated proposals where the proposer is the provided account address. Accepts an account address, and an optional pagination and ordering configuration.

const proposals = await propHouse.query.getProposalsByAccount(accountAddress);

Get proposals for round

Get paginated proposals in the provided round. Accepts the round address, and an optional pagination and ordering configuration.

const proposals = await propHouse.query.getProposalsForRound(roundAddress);

Get proposal

Get a single proposal. Accepts the round address and proposal ID.

const proposal = await propHouse.query.getProposal(roundAddress, proposalId);

Get round proposal count

Get the number of proposals that have been submitted to a given round. Accepts the round address.

const proposalCount = await propHouse.query.getRoundProposalCount(roundAddress);

Votes


Get votes

Get paginated votes. Accepts an optional pagination and ordering configuration.

const votes = await propHouse.query.getVotes();

Get votes for rounds

Get paginated votes in the provided round. Accepts the round address, and an optional pagination and ordering configuration.

const votes = await propHouse.query.getVotesForRound(roundAddress);

Get votes for proposal

Get paginated votes for the provided proposal. Accepts the round address that contains the proposal, the proposal ID, and an optional pagination and ordering configuration.

const votes = await propHouse.query.getVotesForProposal(roundAddress, proposalId);

Get votes by account

Get paginated votes by an account. Accepts the account address, and an optional pagination and ordering configuration.

const votes = await propHouse.query.getVotesByAccount(accountAddress);

Get votes by account for round

Get paginated votes by an account in a given round. Accepts the account address, the round address, and an optional pagination and ordering configuration.

const votes = await propHouse.query.getVotesByAccountForRound(accountAddress, roundAddress);

Get round vote count

Get the number of votes that have been submitted in a given round. Accepts the round address.

const voteCount = await propHouse.query.getRoundVoteCount(roundAddress);

Deposits


Get deposits

Get paginated round deposits. Accepts an optional pagination and ordering configuration.

const deposits = await propHouse.query.getDeposits();

Get round deposits by account

Get paginated round deposits by the provided account. Accepts an account address, and an optional pagination and ordering configuration.

const deposits = await propHouse.query.getRoundDepositsByAccount(accountAddress);

Balances


Get balances

Get paginated asset balances for many rounds, Accepts an optional pagination and ordering configuration.

const balances = await propHouse.query.getBalances();

Get round balances

Get paginated asset balances for a single round. Accepts the round address, and an optional pagination and ordering configuration.

const balances = await propHouse.query.getRoundBalances(roundAddress);

Claims


Get claims

Get paginated asset claims for many rounds, Accepts an optional pagination and ordering configuration.

const claims = await propHouse.query.getClaims();

Get round claims by account

Get paginated round claims by the provided account. Accepts an account address, and an optional pagination and ordering configuration.

const claims = await propHouse.query.getRoundClaimsByAccount(accountAddress);

Governance Power Strategies

Governance power strategies are valid for both proposal gating and voting.


Get gov power strategies

Get paginated governance power strategies. Accepts an optional pagination and ordering configuration.

const govPowerStrategies = await propHouse.query.getGovPowerStrategies();

Get round proposing strategies

Get paginated proposing strategies for a given round. Accepts a round address, and an optional pagination and ordering configuration.

const proposingStrategies = await propHouse.query.getRoundProposingStrategies(roundAddress);

Get round voting strategies

Get paginated voting strategies for a given round. Accepts a round address, and an optional pagination and ordering configuration.

const votingStrategies = await propHouse.query.getRoundVotingStrategies(roundAddress);

Custom


You can also make custom queries using the underlying EVM and Starknet GraphQL clients:

// EVM
const houses = await propHouse.query.gql.evm.request(`
  {
    houses {
      id
    }
  }
`);

// Starknet
const proposals = await propHouse.query.gql.starknet.request(`
  {
    proposals {
      id
    }
  }
`);

Last updated