Skip to content

Commit

Permalink
feat: Updated TokenScript documentation WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
micwallace committed Sep 4, 2024
1 parent c194ce4 commit 262b4a3
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 12 deletions.
2 changes: 1 addition & 1 deletion pages/framework/_meta.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"core-concepts": "Core Concepts",

"tokenscript-syntax": "XML Syntax",
"card-sdk": "Card SDK"
}
13 changes: 13 additions & 0 deletions pages/framework/card-sdk.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Card SDK

The Card SDK provides the glue between the Javascript defined in the TokenScript project and the engine.
It is essentially a 2-way message bus that allows the TokenScript developer to make requests to the engine and to receive updated data.
It also allows the user to access various data including context data, environment variables & contract information.

## Token Context Data

## Environment Variables

## Engine & UX actions

## Ethereum methods
14 changes: 8 additions & 6 deletions pages/framework/core-concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ This allows the TokenScript developer to focus on their token logic from the ver

## Core Components

TokenScript framework consists of several components:
TokenScript consists of several components:

- TokenScript Engine:
- **TokenScript Engine:**
This is the runtime for TokenScript applications. The engine is headless and allows wallets & DApps to integrate TokenScript support in their own UI.
You can find the reference implementation of the runtime [here](https://github.com/SmartTokenLabs/tokenscript-engine/tree/master/javascript/engine-js).
- TokenScript App:
The developer creates & builds a TokenScript app into a .tsml file. This is an XML file that embeds Javascript, CSS & HTML to provide the user-interface for their TApp.
- TokenScript Card SDK:

- **TokenScript Card SDK:**
This SDK is injected into the TokenScript Card, and allows the developer to invoke standard functionality provided by the engine (i.e. signing a message, invoking a transaction, etc).
You can find the CardSDK [here](https://github.com/SmartTokenLabs/tokenscript-engine/tree/master/javascript/card-sdk).

- **TokenScript App:**
The developer creates & builds a TokenScript app into a .tsml file. This is an XML file that embeds Javascript, CSS & HTML to provide the user-interface for their TApp.

The following diagram shows how all these components interact:

Diagram TBC
Expand Down Expand Up @@ -61,7 +63,7 @@ You will notice that the structure is similar to that of any NPM-based project w
Files to note:

- dist - The output of the web build files. The source files are usually converted to a single HTML file with inlined JS/CSS.
This is achieved using [vite-plugin-singlefile](https://www.npmjs.com/package/vite-plugin-singlefile).
This is achieved using [vite-plugin-singlefile](https://www.npmjs.com/package/vite-plugin-singlefile) or similar.
- src - The source HTML/CSS/JS files for TokenScript cards.
- tokenscript.tsml - This is the build artifact that is produced when building the TokenScript project. You can think of it as the TokenScript version of .exe (Windows) or .apk (Android).
- tokenscript.signed.tsml - The signed version of tokenscript.tsml.
Expand Down
5 changes: 3 additions & 2 deletions pages/framework/tokenscript-syntax/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"contracts": "Contracts",
"attributes": "Attributes",
"cards": "Cards",
"transactions": "Transactions"
}
"transactions": "Transactions",
"selections": "Selections"
}
5 changes: 5 additions & 0 deletions pages/framework/tokenscript-syntax/attributes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ Depending on where you place them, the behavior is slightly different.

It's important to consider how top-level attributes may impact performance when choosing where to place them.

### Attribute dependencies

Attributes can reference other attributes as a dependency. For instance, attribute 'A' may reference the output of attribute 'B' in one of its parameters.
These dependencies are resolved by the TokenScript engine, but be careful about introducing circular dependencies in your application.

## Intrinsic attributes

- contractAddress (in the context of a token card)
Expand Down
80 changes: 80 additions & 0 deletions pages/framework/tokenscript-syntax/selections.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Selections

A selection defines conditions that filter origin tokens for the TokenScript and are used to control the availability of cards.
These conditions use attribute values as input, just like transactions and other attributes.

## Defining a selection

As you can see, defining `ts:selection` is pretty simple:
```xml
<ts:selection name="adopted" filter="level>0">
<ts:label>
<ts:string xml:lang="en">Cat already adopted</ts:string>
</ts:label>
</ts:selection>
```

Just like a lot of tokenscript.xml elements, there is a label included within that can be displayed to the user.
To use this filter to disable cards, reference this selection name in the `excude` attribute of `ts:card` as outlined [here](./cards).

## Filter syntax

The filter syntax uses a lexer & parser model that supports complex conditions.

### Simple conditions

You can specify an attribute name on the left hand side of the condition, with a static value on the right:

- `level>0`
- `canLevelUp=FALSE`

We can then use this selection to disable the card for tokens that are greater than level 0

```xml
<ts:card type="action" name="levelUp" origins="ReallyCoolToken" exclude="adopted">
<!-- Other card elements omitted -->
</ts:card>
```

### Available operators

#### Comparison
- **=** Equals
- **<** Less than
- **>** Greater than
- **<=** Less than or equal
- **>=** Greater than or equal

#### Logical
- **&** And
- **|** Or
- **!** Not

### Right-hand-side values

Attribute values can also be specified on the right hand side by enclosing the attributes like so: `${tokenId}`.
In this way we can compare two attribute values to filter tokens:

`wallet=${ownerAddress}`

We can even combine a RHS attribute with some static value:

`label=prefix-${ownerAddress}-suffix`

### Complex conditions

Complex condition can be achieved through the use of brackets and logical operators:

Both conditions must be true (AND):
`&(level>0)(level<10)`

At least one condition must be true (OR):
`|(level<10)(level>20)`

Both conditions must be false (NOT):

`!(level=0)(level=10)`

Multiple conditions can be grouped using brackets:

`&(!(level=0)(level=10))(wallet=${ownerAddress})`
46 changes: 43 additions & 3 deletions pages/framework/tokenscript-syntax/transactions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ This card implements the erc-721 transfer function and does so without any Javas
</ts:card>
```

The syntax for transactions is very similar to attributes that use `ethereum:call`.
You can see that the syntax for transactions is very similar to attributes that use `ethereum:call`.
Note: Attributes defined within the card (either in XML or via Card SDK `setProps`) should use `local-ref` rather than `ref` to reference attributes.

Since we have defined our user-input attribute `toAddress`, we only need to supply an input field in the view with the same ID:
Expand All @@ -65,6 +65,25 @@ Since we have defined our user-input attribute `toAddress`, we only need to supp
The engine will ask the card SDK to collect the input value before invoking the transaction.
The other parameters (ownerAddress & tokenId) are intrinsic attributes that are known by the engine, but can also be user-define attributes.

## Top-level transactions

Each card element can only have a single transaction declared inside which can become a big limitation for more complex actions, such as an erc-20 approval flow.
This is where top-level transactions come in. Top-level transactions are named transactions that can be invoked from any card using the Card SDK.

```xml
<ts:transaction name="safeTransferFrom">
<ethereum:transaction contract="ReallyCoolToken" function="safeTransferFrom" as="uint">
<ts:data>
<ts:address ref="ownerAddress"/>
<ts:address local-ref="toAddress"/>
<ts:uint256 ref="tokenId"/>
</ts:data>
</ethereum:transaction>
</ts:transaction>
```

Top-level transactions must be declared below `ts:attributes` and have a unique name.

## Setting inputs programmatically

Sometimes using direct user input may not be possible, such as when the input requires some
Expand All @@ -73,13 +92,34 @@ processing to get the final value, or when the value is calculated based on some
The Card SDK's `tokenscript.action.setProps()` method can be used to programmatically set parameter values.
You do not have to define `ts:user-entry` attributes when using `setProps`.

Note: When using `setProps`, make sure you don't have any HTML input elements with the same ID as the keys you set may be overwritten.
Note: When using `setProps`, make sure you don't have any HTML input elements with the same ID, as the keys you've already set may be overwritten.
Alternatively you can define `data-ts-prop="false"` on the input element to disable the automatic collection of input fields.

## Invoking transactions

### UI Button
TokenScript provides a few ways to trigger actions depending on your use-case.

### Action Button

The default action button (uiButton) can be used to trigger the card's transaction or an off-chain actions.

If the card does not contain a transaction, the `window.onConfirm` function is triggered to perform the card's intended action.
An example of this would be if an API call or Ethereum signature is needed to get some transaction inputs.
In this case we can perform my transaction prep and execute the transaction programmatically:

```javascript
window.onConfirn = async () => {
const res = await getApiData(); // Get some off-chain data to use in our transaction
tokenscript.action.setProps({myAttribute: res.myAttribute});
await tokenscript.action.executeTransaction();
}
```

In this way, even the default action button can be used to chain transactions together and define other complex logic in your TokenScript card.

### Card SDK

Sometimes a single action button isn't enough, so transactions can be invoked programmatically using the Card SDK.
See the [Card SDK documentation](../card-sdk) for more details.

When the default action button isn't needed, it can be disabled. See [here](./cards#uiButton) for further details.

0 comments on commit 262b4a3

Please sign in to comment.