tablelandnetwork / dimo-vehicle-defs

Tableland-based DIMO Vehicle Definitions demo

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

dimo-vehicle-defs

standard-readme compliant Test

Tableland-based DIMO Vehicle Definitions demo

Table of Contents

Background

This is a demo of hosting the DIMO vehicle definitions table from the DIMO VehicleId ERC721 contract using Tableland. The table is owned by and written to by the VehicleId contract. VehicleId tokens represent user vehicles and are associated with a vehicle definition (e.g. 2011 Toyota Tacoma). The crux of the problem is how to ensure that a given vehicle definition exists when a new VehicleId is minted from the contract.

We show two potential solutions:

  1. VehicleId simply increments a counter whenever a new vehicle definition is added. When minting a VehicleId, the user passes in an integer that represents the Tableland row ID (auto-incrementing primary key) of a vehicle definition. Since table rows cannot be deleted, if this integer is greater than the current counter, we know it’s invalid and the transaction is rejected.

  2. VehicleId2 leverages a (somewhat experimental) on-chain dynamic merkle tree to track a root hash that represents the entire off-chain vehicle definitions table. When minting a VehicleId, the user passes in the Tableland row ID (auto-incrementing primary key) of a vehicle definition, a hash of its values (make, model, year, etc.), and a merkle inclusion proof that it actually exists in the off-chain table. The contract only needs to store a single bytes32 representing the root hash. This approach is more heavy-handed, but ensures a tighter coupling between the VehicleId and vehicle definition.

    Note that the proofs needs to be generated off-chain from the table state. Ideally, validators go-tableland should have an API endpoint that allows users and apps to fetch inclusion proofs for a given row by internally maintaining a merkle tree for the table. As a user, you wouldn't have to trust what the validator gives you because verification happens on-chain against a root hash that is dynamically updated when rows are added/updated (remember, in this case the table can only be written to by the contract).

NFT Metadata

We also show how the ERC721 token metadata could be handled dynamically from the vehicle definitions table. The tokenUri method returns a Tableland query that selects from the table using the internal VehicleId to vehicle definition mapping (see the query here). The result is that we get token metadata that can change based on its associated vehicle definition. Note, this is only implemented for VehicleId.

Here's what the metadata for a VehicleId might look like.

{
  "name": "VehicleId #1",
  "attributes": [
    {
      "trait_type": "device_type_id",
      "value": "vehicle"
    },
    {
      "trait_type": "make",
      "value": "Ford"
    },
    {
      "trait_type": "make_token_id",
      "value": 41,
      "display_type": "number"
    },
    {
      "trait_type": "oem_platform_name",
      "value": "FordPass"
    },
    {
      "trait_type": "model",
      "value": "F-250 Super Duty"
    },
    {
      "trait_type": "year",
      "value": 2018,
      "display_type": "number"
    },
    {
      "trait_type": "model_style",
      "value": "King Ranch 4dr Crew Cab SB (6.2L 8cyl 6A)"
    },
    {
      "trait_type": "model_sub_style",
      "value": "King Ranch"
    }
  ],
  "metadata": {
    "vehicle_info": {
      "base_msrp": "44600",
      "fuel_type": "Diesel",
      "wheelbase": "164 WB",
      "generation": "13",
      "driven_wheels": "4x4",
      "number_of_doors": "4",
      "manufacturer_code": "W2B",
      "fuel_tank_capacity_gal": "34"
    }
  }
}

There's a lot we could do with the metadata structure. We could JOIN from other tables, remove/add attributes, add an image/animation, etc. Note, because most common metadata format doesn't support nested object traits in attributes, we just add the metadata column from the vehicle definitions table as a top level property.

Development

Building the client

You can build the Typescript client locally:

npm install
npm run build

Testing

Run the test suite:

npm test

Deploying

Deployments are handled on a per-network basis:

npx hardhat run scripts/deploy.ts --network polygon

Extracting the ABI and Bytecode

You can grab the assets you need by compiling and then using some jq magic:

ABI

cat artifacts/contracts/VehicleId.sol/VehicleId.json | jq '.abi' > abi.json

Bytecode

cat artifacts/contracts/VehicleId.sol/VehicleId.json | jq -r '.bytecode' > bytecode.bin

Generate the Go client!

You can use the above abi.json to build the Go client:

mkdir gobuild
abigen --abi ./abi.json --bin ./bytecode.bin --pkg contracts --out gobuild/VehicleId.go

Etherscan verification

To perform Etherscan verification, you first need to deploy a contract to an Ethereum network that's supported by Etherscan, such as Polygon:

npx hardhat run scripts/deploy.ts --network polygon

Then, add a POLYSCAN_API_KEY to your .env file and run the verify script:

npx hardhat run scripts/verify.ts --network polygon

Speedier tests

For faster runs of your tests and scripts, consider skipping ts-node's type checking by setting the environment variable TS_NODE_TRANSPILE_ONLY to 1 in hardhat's environment. For more details see the documentation.

License

The Unlicense

About

Tableland-based DIMO Vehicle Definitions demo

License:The Unlicense


Languages

Language:TypeScript 60.7%Language:Solidity 38.9%Language:Shell 0.4%