Skip to content

Latest commit

 

History

History
254 lines (188 loc) · 10.7 KB

README.md

File metadata and controls

254 lines (188 loc) · 10.7 KB

@tableland/sdk

NOTICE: Development has moved to: https://github.com/tablelandnetwork/tableland-js/

Review Test Publish License Version Release standard-readme compliant

Tableland JavaScript SDK—the essential tool for building web3 apps with Tableland databases.

Table of Contents

Background

The @tableland/sdk library provides an easy-to-use interface for integrating Tableland databases into your web3 applications, unlocking decentralized data-driven development with SQL queries (creates, writes, reads) and access control.

Install

You can install via npm/yarn:

npm i @tableland/sdk
# yarn add @tableland/sdk

Or directly via GitHub:

npm i tablelandnetwork/js-tableland

Usage

Full library documentation is generated on GitHub, and general docs, examples, and more are available on our docs site.

Database

A Database is used to create, write, and read data. To connect to a Database, you first must import and instantiate it. There are two key considerations:

  • If you are only reading data, you do not need a signer, which is an abstraction of an EVM account and used for sending on-chain transactions—database reads are off-chain.
  • If you are creating or writing to tables, you do need a signer since database mutations require an EVM account to send transactions.

If you need to set up a signer, libraries like ethers help provide a way to create one by leveraging a connection with a provider. From there, all database creates, writes, and reads go through a single prepare method.

Connecting

As noted, creating a table instantiates a Database and must connect to it with a signer; this is also true for any table writes. The ethers library is used in the example below to create a signer, which comes as part of the @tableland/sdk but can also be installed separately.

import { Database } from "@tableland/sdk";
import { providers } from "ethers";

// Connect to a chain provider, such as a browser wallet
const provider = new providers.Web3Provider(window.ethereum);
// Request permission to connect to a users accounts
await provider.send("eth_requestAccounts", []);

// Create a signer from the connected browser wallet
const signer = provider.getSigner();
// Create a database connection; the signer passes the connected
// chain and is used for signing create table transactions
const db = new Database({ signer });

For certain applications, it's possible that developers will want to manage connections from a local wallet and not a browser wallet. You'll want to create a provider connection using a custom provider URL or the default ethers provider for the specified chain.

import { Database } from "@tableland/sdk";
import { Wallet, getDefaultProvider } from "ethers";

// Define your private key (but replace with your own)
const privateKey = PRIVATE_KEY; // It's recommended to use `dotenv` and a place this in a `.env` file
// Create the wallet (an extension of a signer)
const wallet = new Wallet(privateKey);
// Connect to a provider (e.g., Alchemy, etc.)—you should pass your own
// provider URL to `getDefaultProvider()` (avoid the throttled default)
// For example: `https://eth-mainnet.alchemyapi.io/v2/${YOUR_API_KEY}`
const provider = getDefaultProvider("homestead"); // Defaults to Ethereum mainnet
// Create a signer by attaching the wallet to the provider
const signer = wallet.connect(provider);
// Connect to the database
const db = new Database({ signer });

For read-only connections, a signer is not required. This Database connection can be used to make multichain queries across any of the chains in which Tableland is deployed to. Note that in the signer-connected Database above, read queries are still possible.

import { Database } from "@tableland/sdk";

// Create a database connection; since there is no signer,
// table reads are possible but creates/writes are not
const db = new Database();

If no signer is provided and you try to create or write to tables, the Database will default to prompting a browser wallet connection.

Create, write, & read

To create a table, you pass a CREATE TABLE statement to the prepare method and then execute it.

// This is the table's `prefix`--a custom table value prefixed as part of the table's name
const prefix = "my_sdk_table";

const { meta: create } = await db
  .prepare(`CREATE TABLE ${prefix} (id integer primary key, val text);`)
  .run();

// The table's `name` is in the format `{prefix}_{chainId}_{tableId}`
const { name } = create.txn; // e.g., my_sdk_table_80001_311

Once the table is created, you can then insert, update, and/or delete data with prepared statements. Parameter binding is a useful feature such that you can pass parameters in bind that replace the ? placeholders in this example.

// Insert a row into the table with an `INSERT INTO` statement
const { meta: insert } = await db
  .prepare(`INSERT INTO ${name} (id, val) VALUES (?, ?);`)
  .bind(0, "Bobby Tables")
  .run();

// Wait for transaction finality
await insert.txn.wait();

Notice the insert.txn.wait() usage above. Table creates and writes are on-chain actions, so a transaction must be "finalized" before the data is made available via a SELECT query. Once the data is settled, it can be read.

// Perform a read query, requesting `all` rows from the table
const { results } = await db.prepare(`SELECT * FROM ${name};`).all();

Validator

Aside from the core Database, developers can also choose to connect directly to a Tableland Validator node. Connecting to a validator is useful when you'd like to directly access other table or validator information as exposed by a Tableland Gateway API.

A validator is instantiated using a Database. The Database does not need a signer, but it does need a baseUrl to be defined, which helps to explicitly define which chain is being used. The exported helpers has a useful getBaseUrl method that forms the baseUrl based on a passed chain ID number.

For example, assuming you've already established a provider, you can connect to a Validator and retrieve node health info.

import { Database, Validator, helpers } from "@tableland/sdk";

// Get the chain ID from the ethers `provider`
const { chainId } = await provider.getNetwork(); // Or, statically define, e.g., `const chainId = 1`
// Passing the `signer` is optional here, but `baseUrl` is required for the `Validator`
const db = new Database({
  baseUrl: helpers.getBaseUrl(chainId),
});
// Instantiate a validator using an existing Database instance
const validator = new Validator(db.config);
console.log(validator);
//  {
//    config: {
//      baseUrl: "https://tableland.network/api/v1"
//    }
//  }
const isHealthy = await validator.health();
console.log(isHealthy);
// true

Registry

The SDK is in-part a JavaScript abstraction of the core Tableland registry smart contract. Developers can choose to interact directly with the Registry API for table creates and writes, if desired. It also offers a couple of additional features, such as listing all tables owned by an address or transferring table ownership altogether.

Since these are on-chain interactions, a signer is needed to interact with the Registry on a specific chain.

import { Database, Registry, helpers } from "@tableland/sdk";

// Creating the signer is required—use one of the examples above
const db = new Database({ signer });
// Instantiate the registry using an existing Database instance
const registry = await new Registry(db.config); // Must have a signer
// Get all tables owned by an address—defaults to the signer's address
const tables = await registry.listTables();
console.log(tables);
//  [
//    {
//      tableId: '2',
//      chainId: 1
//    }
//  ]

// Transfer a table to another address
const to = "0x1234..."; // A `0x` EVM address in which you're transfer table ownership to
const tx = await reg.safeTransferFrom({
  to,
  tableName: "{name}_{prefix}_{chainId}", // Replace with your table name
});
// Wait for the transaction to finalize, transferring table ownership
await tx.wait();

Build Tools

The Tableland SDK uses an optimized WASM build of our SQL parser under the hood. Unfortunately, some build systems such as Vite require an adjustment to their configuration to support this feature. To temporarily work around this issue, simply add @tableland/sqlparser to the excluded list under optimizeDeps in your vite.config.ts file:

// ...
optimizeDeps: {
  exclude: ["@tableland/sqlparser"];
}
// ...

See our own Rigs project for an example of using this in production.

Development

Get started by cloning, installing, building, and testing the project:

git clone [email protected]:tablelandnetwork/js-tableland.git
cd js-tableland
npm install
npm run build
npm test

To run tests in a few of the common browser environments we are using Playwright. Once your code changes are finished you can run the brower tests by doing:

  • cd test/browser/server
  • npm install
  • cd ../../
  • npm run test:browser

Contributing

PRs accepted.

Small note: If editing the README, please conform to the standard-readme specification.

License

MIT AND Apache-2.0, © 2021-2022 Tableland Network Contributors