Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add table tutorial #1273

Merged
merged 36 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
18d7889
WIP
qbzzt Aug 3, 2023
d608f46
WIP
qbzzt Aug 3, 2023
0142f22
WIP
qbzzt Aug 7, 2023
3050d81
WIP
qbzzt Aug 8, 2023
ee0b849
WIP
qbzzt Aug 8, 2023
1f3bf2e
WIP
qbzzt Aug 8, 2023
4af93aa
WIP
qbzzt Aug 8, 2023
12083fa
WIP
qbzzt Aug 8, 2023
c931df3
WIP
qbzzt Aug 9, 2023
11d32eb
docs: Extend schema tutorial
qbzzt Aug 10, 2023
d12f411
WIP
qbzzt Aug 10, 2023
9a4bc29
WIP
qbzzt Aug 10, 2023
13a051d
WIP
qbzzt Aug 10, 2023
b9eee88
Merge branch 'main' into 230802-minimal-extend-schema
qbzzt Aug 10, 2023
659a298
Update tutorials.mdx
qbzzt Aug 10, 2023
a28e827
Update extend_schema.mdx
qbzzt Aug 10, 2023
5dcc291
WIP
qbzzt Aug 10, 2023
8e8218c
Merge branch '230802-minimal-extend-schema' of https://github.com/lat…
qbzzt Aug 10, 2023
4228cfe
docs: Use `<CollapseCode>`
qbzzt Aug 10, 2023
90a20cd
WIP
qbzzt Aug 10, 2023
e3dd651
WIP
qbzzt Aug 10, 2023
3124b6f
Prettier run
qbzzt Aug 10, 2023
c9cefb8
Merge branch '230802-minimal-extend-schema' of https://github.com/lat…
qbzzt Aug 14, 2023
509db86
Apply suggestions from code review
qbzzt Aug 14, 2023
f813f81
Merge branch '230802-minimal-extend-schema' of https://github.com/lat…
qbzzt Aug 14, 2023
30f6e8f
Added most comments
qbzzt Aug 14, 2023
513cbc0
Post @alvarius comments
qbzzt Aug 14, 2023
b0f4156
tiny fix
qbzzt Aug 14, 2023
ce4e3e9
Merge branch 'main' into 230802-minimal-extend-schema
qbzzt Aug 14, 2023
62d047a
Merge branch 'main' into 230802-minimal-extend-schema
qbzzt Aug 15, 2023
ceefffa
Merge branch 'main' into 230802-minimal-extend-schema
qbzzt Aug 15, 2023
c3d9dbe
Merge branch 'main' into 230802-minimal-extend-schema
qbzzt Aug 16, 2023
b6bd9fc
Merge branch 'main' into 230802-minimal-extend-schema
qbzzt Aug 17, 2023
fd29f81
Update with @holic comments
qbzzt Aug 18, 2023
f32c66e
Merge branch 'main' into 230802-minimal-extend-schema
qbzzt Aug 23, 2023
0987732
Merge branch 'main' into 230802-minimal-extend-schema
alvrs Aug 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/pages/tutorials.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ These tutorials teach you how to do various things with MUD.
[These tutorials](tutorials/minimal) teach you how to make various changes to [the getting started example](https://github.com/latticexyz/mud/tree/main/examples/minimal).
This is an easy way to learn how to modify MUD functionality in various ways.

- [Extend the schema](tutorials/minimal/extend_schema):
In this tutorial you extend the schema and learn how to lookup information using a key.
- [Add a system](tutorials/minimal/add_system):
In this tutorial you add a system to decrement the counter and update the application to use it.

Expand Down
1 change: 1 addition & 0 deletions docs/pages/tutorials/minimal.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ To create the basic version to modify, follow these steps:

## The tutorials

- [Extend the schema](minimal/extend_schema)
- [Add a system](minimal/add_system)
3 changes: 2 additions & 1 deletion docs/pages/tutorials/minimal/_meta.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export default {
add_system: "Add a system",
"extend_schema": "Extend the schema",
"add_system": "Add a system",
qbzzt marked this conversation as resolved.
Show resolved Hide resolved
};
329 changes: 329 additions & 0 deletions docs/pages/tutorials/minimal/extend_schema.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,329 @@
import { CollapseCode } from "../../../components/CollapseCode";

# Extend the schema
qbzzt marked this conversation as resolved.
Show resolved Hide resolved

In this tutorial you extend the schema to add a table of historical counter values and the time in which the counter reached those values.
For the sake of simplicity, we will implement this in the `increment` function rather than use a [storage hook](/store/advanced-features#storage-hooks).

## Setup

Right now you need the `main` version of `mud`, not `next`.
So if you followed the directions, you need to run this command in the root directory.
qbzzt marked this conversation as resolved.
Show resolved Hide resolved

```sh copy
pnpm mud set-version --tag main && pnpm install
```
qbzzt marked this conversation as resolved.
Show resolved Hide resolved

## Modify the MUD configuration file

1. In an editor, open `packages/contracts/mud.config.ts` and add a table definition for `History`.

<CollapseCode>

```ts filename="mud.config.ts" showLineNumbers copy {9-17}
import { mudConfig } from "@latticexyz/world/register";

export default mudConfig({
tables: {
Counter: {
keySchema: {},
schema: "uint32",
},
History: {
keySchema: {
counterValue: "uint32",
},
schema: {
blockNumber: "uint256",
time: "uint256",
},
},
},
});
```

</CollapseCode>

<details>
<summary>Explanation</summary>

A MUD table has two schemas:

- `keySchema`, the key used to find entries
- `schema`, the value in the entry (soon to be renamed to `valueSchema`)

Each schema is represented as a structure with field names as keys, and the appropriate [Solidity data types](https://docs.soliditylang.org/en/latest/types.html) as their values.

In this case, the counter value is represented as a 32 bit unsigned integer, because that is what `Counter` uses.
Block numbers and timestamps can be values up to `uint256`, so we'll use this type for these fields.

</details>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we indent this to the same level as the 1.?

Copy link
Contributor Author

@qbzzt qbzzt Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we should. However, when I do it the prettier script breaks it. :-(

When I try to indent and then commit, prettier "fixes" it to this:

    <details>
 <summary>Explanation</summary>

   A MUD table has two schemas:

   - `keySchema`, the key used to find entries
   - `schema`, the value in the entry (soon to be renamed to `valueSchema`)

   Each schema is represented as a structure with field names as keys, and the appropriate [Solidity data types](https://docs.soliditylang.org/en/latest/types.html) as their values.

   In this case, the counter value is represented as a 32 bit unsigned integer, because that is what `Counter` uses.
   Block numbers and timestamps can be values up to `uint256`, so we'll use this type for these fields.

    </details>

And then I get this error:

./pages/tutorials/minimal/add-table.mdx
Expected a closing tag for `<details>` (42:5-42:14) before the end of `listItem`

I spent enough time on it that I decided it's not worth hassling with anymore.

Copy link
Member

@holic holic Aug 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the trick to get it to work is add a blank line between the opening/closing tags and the contents, like we did for the <CollapseCode>.

Maybe there's an update to prettier that fixes these. Will look!


2. Run this command in `packages/contracts` to regenerate the table libraries.

```sh copy
pnpm build:mud
```

## Update `IncrementSystem`

1. In an editor, open `packages/contracts/src/systems/IncrementSystem.sol`.

- Modify the second `import` line to import `History` (line 5)
- Modify the `increment` function to also update `History` (line 12).
To see the exact functions that are available, you can look at `packages/contracts/src/codegen/tables/History.sol` (that is the reason we ran `pnpm build:mud` to recreate it already).

<CollapseCode>

```solidity filename="IncrementSystem.sol.sol" copy showLineNumbers {5, 12}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import { System } from "@latticexyz/world/src/System.sol";
import { Counter, History, HistoryData } from "../codegen/Tables.sol";

contract IncrementSystem is System {
function increment() public returns (uint32) {
uint32 counter = Counter.get();
uint32 newValue = counter + 1;
Counter.set(newValue);
History.set(newValue, block.number, block.timestamp);
return newValue;
}
}
```

</CollapseCode>
qbzzt marked this conversation as resolved.
Show resolved Hide resolved

<details>
<summary>Explanation</summary>

```solidity
import { Counter, History, HistoryData } from "../codegen/Tables.sol";
```

When a table has multiple fields in the value schema, MUD generates a [Struct](https://www.tutorialspoint.com/solidity/solidity_structs.htm) to hold a full value.
Here is `HistoryData`, copied from `packages/contract/src/codegen/History.sol`.

```solidity
struct HistoryData {
uint256 blockNumber;
uint256 time;
}
```

Note that `IncrementSystem` doesn't need to use `HistoryData`, because it only writes to history, it doesn't read from it.
However, this is part of manipulating the schema and therefore included in this tutorial.

```solidity
History.set(newValue, block.number, block.timestamp);
```

Set the value.
All MUD tables have a `<table>.set` function with the parameters being the key schema fields in order and then the value schema fields in order.

</details>

2. Run this command in `packages/contracts` to rebuild everything this package produces.

```sh copy
pnpm build
```

## Update the user interface

You can already run the application and see in the MUD Dev Tools that there is a `:History` table and it gets updates when you click **Increment**.
Click the **Store data** tab and select the table **:History**.

However, you can also add the history to the user interface.
The directions here apply to the `vanilla` client template, if you use anything else you'll need to modify them as appropriate.

1. Edit `packages/client/src/index.ts`.

- Import two more functions we need (lines 3-4).
- In `components.Counter.update$.subscribe`, add code that updates the selection of history values (lines 18-22).
- Add a function that is called when the user selects a different option (lines 31-37).
qbzzt marked this conversation as resolved.
Show resolved Hide resolved

<CollapseCode>

```ts filename="index.ts" copy showLineNumbers {3-4,18-22,31-37}
import { mount as mountDevTools } from "@latticexyz/dev-tools";
import { setup } from "./mud/setup";
import { encodeEntity } from "@latticexyz/store-sync/recs";
import { getComponentValueStrict } from "@latticexyz/recs";

const {
components,
systemCalls: { increment },
} = await setup();

// Components expose a stream that triggers when the component is updated.
components.Counter.update$.subscribe((update) => {
const [nextValue, prevValue] = update.value;
console.log("Counter updated", update, { nextValue, prevValue });
document.getElementById("counter")!.innerHTML = String(nextValue?.value ?? "unset");

let options: String = "";
for (let i = 1; i <= nextValue?.value; i++) {
options += `<option value="${i}">${i}</option>`;
}
document.getElementById("historyValue")!.innerHTML = options;
});

// Just for demonstration purposes: we create a global function that can be
// called to invoke the Increment system contract via the world.
// (See IncrementSystem.sol)
(window as any).increment = async () => {
console.log("new counter value:", await increment());
};

(window as any).readHistory = async (counterValue) => {
const History = components.History;
const entity = encodeEntity(History.metadata.keySchema, { counterValue });
const { blockNumber, time } = getComponentValueStrict(History, entity);
document.getElementById("blockNumber")!.innerHTML = blockNumber;
document.getElementById("timeStamp")!.innerHTML = new Date(parseInt(Number(time) * 1000));
};

mountDevTools();
qbzzt marked this conversation as resolved.
Show resolved Hide resolved
```

</CollapseCode>

<details>
<summary>Explanation</summary>

```typescript
let options: String = "";
```

Create `options` as an empty string.
This is the way you define a variable in TypeScript: `let <variable name>: <type>`.
Here we initialize it to the empty string.

```typescript
for (let i = 1; i <= nextValue?.value; i++) {
options += `<option value="${i}">${i}</option>`;
}
```

Create the list of options.

```typescript
document.getElementById("historyValue")!.innerHTML = options;
```

Set the internal HTML of the `historyValue` HTML tag to `options`.
Notice the exclamation mark (`!`).
`document.getElementById` may return either a tag that can be changed, or an empty value (if the parameter is not an id of any of the HTML tags).
We know that `historyValue` exists in the HTML, but the TypeScript compiler does not.
This exclamation point tells the compiler that it's OK, there will be a real value there.
[See here for additional information](https://blog.logrocket.com/understanding-exclamation-mark-typescript/).

```typescript
(window as any).readHistory = async counterValue => {
const History = components.History
const entity = encodeEntity(History.metadata.keySchema, { counterValue });
```

`encodeEntity` creates a key in the format that MUD uses, which is based on [ABI argument encoding](https://docs.soliditylang.org/en/latest/abi-spec.html#argument-encoding).

```typescript
const { blockNumber, time } = getComponentValueStrict(History, entity);
```

Read the actual data.

```typescript
document.getElementById("blockNumber")!.innerHTML = blockNumber;
```

Update the value in the HTML table.

```typescript
document.getElementById("timeStamp")!.innerHTML =
new Date(parseInt(Number(time) * 1000))
}
```

Solidity uses [Unix time](https://en.wikipedia.org/wiki/Unix_time).
JavaScript uses a similar system, but it measures times in milliseconds.
So to get a readable date, we take the time (which is a [`BigInt`](https://www.w3schools.com/js/js_bigint.asp)), multiply it by a thousand, and then convert it to a [`Date`](https://www.w3schools.com/jsref/jsref_obj_date.asp) object.

</details>

2. Edit `packages/clients/index.html`.

<CollapseCode>

```html filename="index.html" copy showLineNumbers {12-26}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>a minimal MUD client</title>
</head>
<body>
<script type="module" src="/src/index.ts"></script>
<div>Counter: <span id="counter">0</span></div>
<button onclick="window.increment()">Increment</button>
<hr />
<h2>
History for value:
<select id="historyValue" onInput="window.readHistory(value)"></select>
</h2>
<table border>
<tr>
<th>Block number</th>
<th>Time</th>
</tr>
<tr>
<td id="blockNumber"></td>
<td id="timeStamp"></td>
</tr>
</table>
</body>
</html>
```

</CollapseCode>

<details>
<summary>Explanation</summary>

```html
<hr />
<h2>
History for value:
<select id="historyValue" onInput="window.readHistory(value)"></select>
</h2>
```

This is the [input field](https://www.w3schools.com/tags/tag_select.asp) that lets the user select which counter value they'd like to get information about.
The `id` attribute is used by `packages/client/src/index.ts` to set the options.
The `onInput` attribute is the JavaScript code to execute when the value changes.

```html
<table border></table>
```

A standard [HTML table](https://www.w3schools.com/html/html_tables.asp).

```html
<tr>
<th>Block number</th>
<th>Time</th>
</tr>
<tr>
<td id="blockNumber"></td>
<td id="timeStamp"></td>
</tr>
```

A location for the values, which is set by `window.readHistory` in `index.ts`.

</details>

3. Run `pnpm dev` in the application's root directory, browse to the app URL, and click **Increment** a few times.
Then select a counter value and see that the block number and correct time are written to the HTML table.
Loading