Engineering Updates | August 2022

Engineering Updates | August 2022

Launching the Tableland engineering blog v4.1.2 with some updates on mainnet support, new features, and those released over the past month or so.

Engineering Updates | August 2022

Launching the Tableland engineering blog with some updates on mainnet support, new features, and those released over the past month or so.


  • Mainnet support! Contracts are now live on Ethereum, Polygon, and Optimism.
  • Optimism Kovan deprecation — and new testnet support was added for Optimism Goerli & Arbitrum Goerli.
  • New package versions are out, so be sure to update to the latest release.
  • New endpoints for schema & structure discoverability.
  • Changes to parameters in some SDK methods, including create and write.
  • New methods in the smart contracts for ACLs & controllers — also reflected in the SDK and CLI.
  • Some new smart contract utilities for working with SQL.
  • Validator updates, including multi-transaction support, increased testnet row limit, and moving to SQLite.
  • Released a SQL parser as a WASM-based JavaScript library, using a custom SQL parser under the hood (written in Go).
  • New SVGs for the TABLE token.
  • New writeups that describe some of these features in more detail, including details on the Tableland Rigs.
  • A bunch of new docs that walk through new and existing features / concepts.

Release Notes


  • Contracts were deployed and are now live the following mainnets: Ethereum, Optimism, and Polygon!
    • Note that while these are obviously mainnets; the Tableland testnet is still being used.
    • Developers should ensure any contracts using Tableland are upgradeable if directly setting the baseURI as the testnet gateway (e.g., an NFT’s metadata).
  • New package versions released:
  • Support was added for the Arbitrum Goerli and Optimism Goerli testnets.
    • This is in part because Optimism is deprecating Optimism Kovan; as such, Tableland will also deprecate Optimism Kovan. Be sure to migrate any relevant tables ASAP!
    • Additionally, support for Arbitrum Nitro Goerli has been added.
  • Validator SQLite migration happened a few weeks ago, and a SQL spec parser has now been released.
    • The transition was from PostgresQL to SQLite, which opened to door for a number of future protocol features. More details are included in the writeup at the bottom.
    • The SQL parser uses the protocol’s custom SQL spec, which is tailored toward web3.
  • Validator client was open sourced () a few weeks ago.
    • As the network prepares to onboard validators over the coming months, the code has been made open to the public after 6+ months of development and years of prior work leading up to it.
  • Images for TABLE ERC721 have been updated (previously, a static Tableland logo).


  • Increased Tableland testnet maximum number of rows to 500k (previously, set to 100k).
  • Added mutli-transaction support.
    • Prior to this release, table creates and writes for the same table was not possible in the same transaction. Developers had to use clunky post-deploy logic / helper functions to create a table and then write to it to avoid any errors.
    • Now, multi-transactions are possible, removing the need for post-deploy smart contract logic. For example, an address can create a table and write to it in the same tx. See the writeup below for more details.
  • For running a validator locally during development, check out the repo — under the hood, it uses the validator source code.

Smart Contracts

  • Released @tableland/[email protected].
  • Added getController — a helper to get the controller of a table, e.g., to get the controller after previously setting it with setController.
  • Also added lockController in addition to a new internal state variable that stores these locks. Use this method to permanently lock a table’s controller.
  • To use these methods, it’d look something like the following:
  • /* 
     * Registry (TablelandTables) proxy contract addresses can always be found
     * here:
    // A private storage variable
    ITablelandTables private _tableland;
    // An instance of the registry contract where `registry` is some `address`
    _tableland = ITablelandTables(registry);
    // And to leverage these new methods, it'd look something like the following
    _tableland.lockController(0x4D5286d81317E284Cd377cB98b478552Bbe641ae, 1);
  • Added new a SQL helpers library — such as generating table names, CREATE schemas, and INSERT/UPDATE/DELETE statements
  • import './SQLHelpers.sol';
    // Somewhere in your contract's code
    SQLHelpers.toCreateFromSchema("my_table", "id int");
    // CREATE TABLE my_table_1 (id int)
    SQLHelpers.toInsert("my_table", 123, "id", "1");
    // INSERT INTO my_table_1_123 (id) VALUES (1)
    SQLHelpers.toUpdate("my_table", 123, "set id = 2", "id = 1");
    // UPDATE my_table_1_123 SET set id = 2 WHERE id = 1
    SQLHelpers.toDelete("my_table", 123, "id = 1");
    // DELETE FROM my_table_1_123 WHERE id = 1
  • Updated with new Tableland deployment proxy contracts and their associated addresses.


A couple of examples using these new endpoints
// Get the matching tables from structure hash
// [
//   {
//     "controller": "0x4D5286d81317E284Cd377cB98b478552Bbe641ae",
//     "name": "rest_api_5_85",
//     "structure": "7d5cd636b3cb913155fe97a54af38383c2a7a94c98369616bdfe6409558cc57a"
//   },
//   {
//     "controller": "0x4D5286d81317E284Cd377cB98b478552Bbe641ae",
//     "name": "matching_structure_5_296",
//     "structure": "7d5cd636b3cb913155fe97a54af38383c2a7a94c98369616bdfe6409558cc57a"
//   }
// ]

// Get the schema of a table
// {
//     "columns": [
//       {
//         "name": "holder",
//         "type": "text",
//         "constraints": [
//           "PRIMARY KEY"
//         ]
//       },
//       {
//         "name": "quantity",
//         "type": "integer",
//         "constraints": []
//       },
//     ],
//     "table_constraints": []
//   }


  • Released @tableland/[email protected].
  • Added schema and structure methods to support using the endpoints described above.
  • Added new controller-related methods: getController, setController, and lockController.
    • setController method helps SDK users provision access control by specifying an address to control the table ACLs, which may be an EOA or contract address.
    • getController and lockController methods are described in the smart contracts section.
    • Examples of these controller methods:
    • // Check the controller, and then lock it if it's "good to go" for an irreversible lock
      let controller = await tableland.getController("my_sdk_table_5_311")
      // 0x0000000000000000000000000000000000000000
      // This likely means `setController` was never and defaults to 0x0
      // Lock the controller....FOR-E-VER...FOR-E-VER...
      let tx = await tableland.lockController("my_sdk_table_5_311")
      // {
      //    hash: "0x084a15643927555e87d3889eaa145bbe382a3687b738b6a7c176d3e5e8fc1cce"
      // }
  • Changes to the parameters in create.
    • The method no longer takes parameters schema and prefix but now offers additional options, where prefix is a key instead of a standalone param. Thus, the parameters for create are now schema and an optional object with a key called prefix, among others.
  • write and setController now allow for rpcRelay as a method-level option to be passed, which is also set in the connection.
    • rpcRelay is a boolean identifier for whether or not a smart contract call should be relayed by the Tableland validator or not. It defaults to true for testnet chains, and it cannot be true for mainnet chains.
    • write now takes the parameters statement and an optional object (instance of WriteOptions) with keys called rpcRelay and skipConfirm.
    • An example:
    • // Runs a mutating query statement at the specified table
      const insertRes = await tableland.write(
        `INSERT INTO ${name} (id, name) VALUES (0, 'Bobby Tables');`,
      		rpcRelay: false,
      		skipConfirm: false
      // {
      //   "hash": "0xbe8c14686af7c2c71c5a996923816d20e31d59a1a976962b443f93a96a031f2d"
      // }


  • Released @tableland/[email protected].
  • Added a controller command, which enables calls to the aforementioned getController, setController, and lockController methods.
    • Each method has a dedicated subcommand for get, set, and lock.
    • > tableland controller get cli_demo_table_5_237
      # "0x0000000000000000000000000000000000000000"
      > tableland controller set 0x4D5286d81317E284Cd377cB98b478552Bbe641ae cli_demo_table_5_237
      # {
      #  "hash": "0xca5efde91c461c1e272d9e7ac65bc50d31493dc9c4a530c678081defd3a6492f"
      # }
      > tableland controller lock cli_demo_table_5_237
      # {
      #  "hash": "0x9b3979e9355a5ae32a2d34cae295e186a1aa60ae9522c3b0c9f1a5bbae8653e4"
      # }
  • Added a new global flag rpcRelay (boolean) for setting whether or not to relay a testnet write queries (write and controller commands).
  • Miscellaneous optimizations and error message improvements were also added.

SQL Parser

  • Released @tableland/[email protected] — a WASM-based JavaScript library that wraps Tableland's Go-based parser.
  • Simply import and use it in testing workflows:
  • import init from "@tableland/sqlparser";
    // Initialize module (adds sqlparser object to global namespace)
    await init();
    // Parse sql statement
    const [parsed] = await sqlparser.parse(
      "select * FrOM fake_table_1 WHere something='nothing';"
  • Read more in the writeup SQLite Migration & Parser.

Release Notes on GitHub

Check out the following links for release notes on GitHub:


Exploring Rigs Tables

  • Take a deep dive into the Tableland Rigs NFT and the technical details for metadata design and composing queries.

SQLite Migration & Parser

  • An overview of the SQLite migration and SQL parser.

Affecting Tableland State

  • An explainer on how the validator client processes transactions and design for the new multi-transaction support.

Value Layering across L1 & L2

  • A brief on layering value across different chains, such as adopting the Tableland Rigs model where L1 NFTs use metadata tables deployed on L2.

Deploying an NFT on Polygon

  • A tutorial that walks through how two set up a two table metadata schema and deploy the tables & NFT smart contract on Polygon.


  • Split Limits & Deployed contracts into separate pages — Limits and a separate Deployed Contracts — to help with readability.
  • Added more information to Limits, including limitations to be aware of with smart contracts, chains, tables, tx times, and table reads.
  • Added information about Response Times, including introductory information on Tableland<>chain interactions.
  • Created a page about Considerations & Tradeoffs. It reviews where people host data in web3 and what data belongs on Tableland.
  • Made a page about Choosing a Chain, with details on L1 vs. L1 as well as chain-specific resources.
  • Some docs on getting started with Polygon.
  • Added a brief on incrementing values with an INTEGER type automatically, if it has the PRIMARY KEY constraint.
  • Updated SDK, CLI, and REST API docs to reflect the changes mentioned throughout.