0.0.3 • Published 4 months ago

@delvtech/council-core v0.0.3

Weekly downloads
-
License
MIT
Repository
-
Last release
4 months ago

Council SDK

A TypeScript SDK built with ethers v5 for interfacing with the Council protocol.

Ethers v6 and/or viem support TBD soon.

Installation

The Council SDK isn't currently available as a stand alone package but can be installed within the council monorepo.

  1. Clone the council-monorepo.
  2. Add "@council/sdk": "*" to the app or package's package.json.
  3. Run install: yarn.

Basic Usage

import { providers } from "ethers";
import { CouncilContext, VotingContract } from "@council/sdk";

const provider = new providers.JsonRpcProvider("http://localhost:8545");

// 1. Create a new context instance
const context = new CouncilContext(provider);

// 2. Create model instances
const coreVoting = new VotingContract("0x0...", [], context);

// 3. Call methods on the models
const aliceVotes = coreVoting.getVotes("0x...");

Primary Concepts

The SDK is made up of 4 main concepts:

Models

Models are the primary interface for the SDK. They're JavaScript classes that represent specific entities within Council and include methods on them to fetch data and submit transactions.

When possible, these data fetching methods will return other models. For example, fetching a proposal from a VotingContract model will return a new Proposal model instance with it's own methods. This makes it easy to work with return values rather than just returning addresses or ids like you might expect from an API.

// initiates a new VotingContract Model instance
const coreVoting = new VotingContract("0x0...", [], context);

// returns a new Proposal Model instance
const proposal0 = await coreVoting.getProposal(0);

// returns a plain string since there is no model for Quorum.
const quorum = await proposal.getCurrentQuorum();

For more info, checkout the Models reference.

Data Sources

Data sources are JavaScript classes that act as an interchangeable data access layer for the models. They handle querying and caching logic so the models can just focus on defining the shape of Council entities and their relationships.

As an SDK user, you'll likely never touch the data sources directly. A basic data source takes arguments from a model, fetches and caches on-chain data, and returns it to the model for any further transformations or model construction.

Data sources return basic values and objects. They'll never return other data sources or models (class instances).

const coreVoting = new CoreVotingContractDataSource("0x...", context);

// returns a plain JavaScript object of data from the contract
const proposal = await coreVoting.getProposal(0);

For more info, checkout the DataSources reference.

Context

The CouncilContext stores common information used in model and data source methods including shared data sources and their cache. It also includes a couple utility methods for getting and registering new shared data sources.

As an SDK user, you likely won't call any methods on the context. You'll just construct the Context and pass it to new Models.

const context = new CouncilContext(provider);

For more info, checkout the CouncilContext reference.

Utils

Utils are functions for common operations performed when building with the Council SDK.

// Turn a block number into a Date object with the getBlockDate util.
const blockDate = getBlockDate(minedBlockNumber, provider);

// Estimate future dates (e.g., expiration dates) with the estimateFutureDates option.
const estimatedDate = getBlockDate(futureBlockNumber, provider, {
  estimateFutureDates: true,
});

For more info, checkout the Utils reference.

Extending

The Council SDK is purpose built to be extended and modified in a number of ways. Some examples of what you could do:

  • Initiate models with a custom data source
  • Add new model variations (e.g., new vault types)
  • Extend existing models with custom properties or methods
  • Modify the way context manages shared data sources.

The Council Kit monorepo is a great starting point to get all the src code including packages that haven't been published to a registry yet. However, making modifications directly to the source code can make it difficult to upgrade to newer versions. Extensions should be made either in a new package such as @myprotocol/council-vaults or inside app code.

Once version 1.0.0 of the sdk has been published to NPM, this package can be removed from the monorepo and the version updated in dependent apps and packages from * to ^1.0.0.

Adding a new Voting Vault

For this guide we'll assume you've already generated an ethers v5 typechain instances for your voting vault. See @council/typechain for an example.

1. Create a new DataSource class

To fetch data from the custom vault, add a new DataSource class for it.

import { FooVault, FooVault__factory } from "@foo/typechain";
import { CouncilContext, VotingVaultContractDataSource } from "@council/sdk";

// Extend the `VotingVaultContractDataSource` class to get data from on chain
// and inherit the `queryVotePower` method.
export class FooVaultDataSource extends VotingVaultContractDataSource<FooVault> {
  constructor(address: string, context: CouncilContext) {
    super(FooVault__factory.connect(address, context.provider), context);
  }

  // Add some custom data fetching methods taking advantage of util methods on
  // the super class like `this.call()` and `this.getEvents()`
  getAmount(address: string) {
    const [amount, delegate] = this.call("amount", [address]);
    return {
      amount: amount.toString(),
      delegate,
    };
  }
}

2. Create the Model class

Create a new Model class to transform some values into other Model instances and make it possible to initiate many model instances in different parts of the code that will all share the same data source instance and its cached data.

import { Voter } from "@council.typechain";
import { FooVaultDataSouce } from "./FooVaultDataSouce";

export class FooVault extends VotingVault<FooVaultDataSource> {
  constructor(address: string, context: CouncilContext) {
    super(address, context, {
      name: "Foo Vault",
      dataSource: context.registerDataSource(
        { address },
        new FooVaultDataSource(address, context),
      ),
    });
  }

  // add some methods to use in an app
  async getAmount() {
    const { amount, delegate } = this.dataSource.getAmount();
    return {
      amount,
      delegate: new Voter(delegate, this.context.provider),
    };
  }
}

Reference

Models

Data Sources

Context

Utils