@tableland/local

A lightweight development environment for Tableland databases.
Table of Contents
Background
Local Tableland provides developers with a user-friendly, lightweight development environment for working with Tableland, making it easier to build, test, and deploy decentralized database applications in web3. You can create a local sandbox environment to build or set up automated tests against a Local Tableland network.
A Tableland network is fundamentally made up of two parts:
- An EVM compatible blockchain with the Tableland Registry contract deployed to it.
- A Tableland Validator node that can listen to events emitted from the contract and materialize tables.
This package contains tooling to get a sandboxed single Tableland network node running locally aside a Hardhat blockchain node. It is useful for local-only development, removing the need for testnet faucets and giving you easy access to logs of everything that is happening on the Tableland network during development.
Install
From your project, install the @tableland/local
package as a development dependency:
npm install --save-dev @tableland/local
Usage
To spin up a Local Tableland network, you can run the following:
npx local-tableland
Under the hood, this will run a binary release of the Tableland Validator, using the Tableland Registry smart contract as an npm
dependency and deploying it to the local Hardhat blockchain node. To shut down Local Tableland, simply quit / exit your terminal session.
Silencing logs
You will see logs from both the Registry contract and the Validator, which are prefixed with the origin of the log. If you want to silence the logs, you can use the silent
flag; verbose logs (default) use the verbose
flag:
npx local-tableland --silent
npx local-tableland --verbose
Wallet & Endpoint Configuration
Under the hood, Local Tableland is running an in-memory instance of a Hardhat network. To connect and interact with the Hardhat network (e.g., using a browser wallet), you'll need to use the following:
- RPC URL:
http://127.0.0.1:8545
- Chain ID:
31337
Checkout the Hardhat docs for more details.
Separately, a Local Tableland node is running. You can interact with the node at the following base URL, such as using any of the Gateway APIs or CLI tool:
- Base URL:
http://localhost:8080
Using with the Tableland SDK
Using the JavaScript SDK with a Local Tableland sandboxed network is the same as any network; you simply configure the provider
with the chain you want to use. For example, you can pass the local URL http://127.0.0.1:8545
to getDefaultProvider
from ethers
and connect the signer
to it:
import { Database } from "@tableland/sdk";
import { getDefaultProvider, Wallet } from "ethers";
const localProviderUrl = "http://127.0.0.1:8545";
const privateKey =
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
const wallet = new Wallet(privateKey);
const provider = getDefaultProvider(localProviderUrl);
const signer = wallet.connect(provider);
const db = new Database({ signer });
When you spin up a Hardhat node, it comes with a setup of default accounts preloaded with 10,000 local-only ETH (symbol: GO
); these accounts have totally open private keys. The example above (0x59c6...
) is the second account since the first one is already used by Local Tableland when deploying the Registry contract.
Using in Tests & Programmatically
If you are using Local Tableland to run tests for your project, or want to start a sandbox network programmatically for any reason, there are some helper utilities exported by this package to help reduce boilerplate. For example, the following demonstrates how to start a local node by:
- Instantiating
LocalTableland
. - Starting the network up and making sure it's ready.
- Retrieving connected account (from Hardhat).
- Getting / connecting a
Database
. - Getting a
Registry
or Validator
instance (via the SDK). - Stopping the network.
import {
LocalTableland,
getDatabase,
getRegistry,
getValidator,
getAccounts,
} from "@tableland/local";
const lt = new LocalTableland({
silent: true,
});
const go = async function () {
lt.start();
await lt.isReady();
const accounts = getAccounts();
const db = getDatabase(accounts[1]);
const response = await db
.prepare(`CREATE TABLE my_table (id integer primary key, name text);`)
.all();
console.log(response);
const registry = getRegistry(accounts[1]);
const myTables = await registry.listTables();
console.log(myTables);
const validator = getValidator(db.config.baseUrl);
const tableData = await validator.getTableById({
chainId: 31337,
tableId: "1",
});
console.log(tableData);
await stop();
};
const stop = async function () {
await lt.shutdown();
};
go().catch((err) => console.log(err));
A best practice for testing is to start a single local network and run all of your tests against it, i.e., don't create an instance for each test. Doing this will speed up test execution significantly!
Using with a Hardhat Project
Using Local Tableland to test a Hardhat project is straightforward. The one key point is that instead of letting Hardhat automatically start a node for you, you will be letting Local Tableland start the node. To do this, you simply have to include the network flag with the value localhost
. For example, instead of running npx hardhat test
, you should run:
npx hardhat --network localhost test
Consider the basic example below that assumes you have created MyContract
in your Hardhat project and are deploying it. It imports mocha
for writing the tests along with some of the helper methods outlined in the previous example.
import { after, before, describe, test } from "mocha";
import { LocalTableland, getAccounts } from "@tableland/local";
const lt = new LocalTableland({
silent: true,
});
const accounts = getAccounts();
before(async function () {
this.timeout(25000);
lt.start();
await lt.isReady();
const Factory = await ethers.getContractFactory(
"MyContract"
);
const myContractInstance = (await Factory.deploy()) as MyContract;
await myContractInstance.deployed();
await new Promise(resolve => setTimeout(() => resolve(), 5000));
});
after(async function () {
await lt.shutdown();
});
describe("Test MyContract and other apps", function () {
test("Should work end to end", async function () {
});
})
See here for a full working example of testing a Hardhat project.
Core Protocol Development
For those contributing to the core Tableland protocol, a common pattern is to spin up Local Tableland to interact with any changes you make to the core software during development: evm-tableland and go-tableland. This helps you check if your local changes are working across the entire network.
Set up a workspace that has all three of these repos cloned into it.
git clone https://github.com/tablelandnetwork/evm-tableland
git clone https://github.com/tablelandnetwork/go-tableland
git clone https://github.com/tablelandnetwork/local-tableland
Your workspace should have the following directories:
.
├── evm-tableland
├── go-tableland
└── local-tableland
Then, you will want to cd
into local-tableland
and create a tableland.config.js
file by running:
npx local-tableland --init
This will create tableland.config.js
in the root of the local-tableland
directory; it defines the relative paths to the aforementioned core protocol directories:
module.exports = {
validatorDir: "../go-tableland",
registryDir: "../evm-tableland",
verbose: false,
silent: false,
};
Once this is all set up, you can check if any changes you make to go-tableland
and/or evm-tableland
code cause things to break. Do this by running the following command from within the local-tableland
directory:
npm test
Notes
Keep an eye out for zombie processes. Killing the Local Tableland process should kill all of the sub-processes, and the cleanup of everything (including Docker, if relevant) is done during startup. But, it's still worth keeping an eye out—and if you find any problems or ways for this tooling to better manage cleanups, please open an issue!
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