-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Updated TokenScript documentation WIP
- Loading branch information
1 parent
262b4a3
commit c36d257
Showing
2 changed files
with
304 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,309 @@ | ||
# 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 is essentially a 2-way message bus that allows the TokenScript developer to make requests to the engine and to receive updated data and other events emitted by the engine. | ||
It also allows the user to access various data including context data, environment variables & contract information. | ||
|
||
## Token Context Data | ||
|
||
As explained in the introduction to TokenScript attributes, Token Context data is made available through the card SDK. | ||
Our starter templates already handles the dataChange event which is used to provide initial and updated token context data to your card. | ||
|
||
If you want to register your own listener, you can do so by overwriting the following eventHandler | ||
|
||
```typescript | ||
tokenscript.tokens.dataChanged = async (oldTokens: any, updatedTokens: ITokenData, id: string) => { | ||
|
||
} | ||
``` | ||
|
||
For our Svelte & React templates, the `updatedTokens.currentInstance` is automatically written to a global context variable that your UI can react to. | ||
|
||
## Environment Variables | ||
|
||
To access environment variables within your card, ensure that the variables is added into `ts:meta` as demonstrated [here](./tokenscript-syntax/xml). | ||
|
||
You can then access these variables via the `tokenscript.env` object: | ||
|
||
```typescript | ||
tokenscript.env.MY_ENV_VARIABLE | ||
``` | ||
|
||
## Event handlers | ||
|
||
The engine include event handler infrastructure that allows the registration of multiple event listeners throughout your application. | ||
|
||
Currently, there is only one handler however more will be added in the future: | ||
|
||
- TRANSACTION_EVENT | ||
|
||
```typescript | ||
// Types | ||
interface TokenScriptEvents { | ||
TRANSACTION_EVENT: ITransactionStatus | ||
} | ||
|
||
// Usage | ||
tokenscript.on("TRANSACTION_EVENT", (data: ITransactionStatus) => { | ||
// Handle transaction event | ||
}) | ||
``` | ||
|
||
The TRANSACTION_EVENT can be used to react to transaction status even when the default action button is used to trigger the transaction. | ||
|
||
## Bundled libraries | ||
|
||
To reduce the size of built TokenScripts, some common libraries are bundled with the Card SDK. | ||
This allows the use of these libraries without explicitly installing them via NPM. | ||
|
||
Bundled libraries | ||
|
||
- Ethers.js ^v6.13 - available via the `ethers` global variable | ||
|
||
## Engine & UX actions | ||
|
||
### setProps | ||
|
||
One of the most important engine actions is `setProps`. It allows the user to define values that can be used as inputs to attributes & transactions: | ||
|
||
```typescript | ||
tokenscript.action.setProps({ | ||
myAttribute: "123", | ||
myAttribute2: 456 | ||
}) | ||
``` | ||
|
||
When invoking this method, the engine checks these values to see if they are `ts:user-entry` attributes. | ||
If they are not user entry values the engine checks if any of these values are a dependency for another attribute defined in the current scope. | ||
If so the engine will reload those attributes and return the updated data to the card via the `dataChanged` handler explained above. | ||
In this way, attribute values can be updated automatically based on an input that is set programmatically from within the card. | ||
|
||
Likewise, declarative transactions can reference these values as transactions arguments. | ||
|
||
Both attributes & transactions reference these values using the `local-ref` XML attribute: | ||
|
||
```xml | ||
<ts:data> | ||
<ts:address ref="ownerAddress"/> | ||
<ts:address local-ref="toAddress"/> | ||
<ts:uint256 ref="tokenId"/> | ||
</ts:data> | ||
``` | ||
|
||
### show/hideLoader | ||
|
||
A self-explanatory method, this allows the user to show or hide the default card loaded provided the TokenScript viewer UI: | ||
|
||
`tokenscript.action.showLoader()` OR `tokenscript.action.hideLoader()`. | ||
|
||
### setActionButton | ||
|
||
The setActionButton method allows the developer to control the state of the default action button: | ||
|
||
```typescript | ||
// Type | ||
type setActionButton = (options: { show?: boolean, disable?: boolean, text?: string }) => void | ||
|
||
// Usage | ||
tokenscript.action.setActionButton({ | ||
show: true, // Whether to show the button | ||
disable: false, // Whether to make the button disabled | ||
text: "Transfer Now" // The text of the button | ||
}) | ||
``` | ||
|
||
This allows the user to hide, disable or update the button text based on some conditions defined in Javascript. | ||
|
||
### executeTransaction | ||
|
||
`executeTransaction` is used to invoke declarative transactions programmatically as discussed in [TokenScript Transactions](./tokenscript-syntax/transactions). | ||
|
||
```typescript | ||
// Types | ||
interface TXOptions { | ||
txName?: string | ||
} | ||
|
||
interface ITransactionStatus { | ||
status: 'started'|'aborted'|'submitted'|'confirmed'|'completed', | ||
txNumber?: string, | ||
txLink?: string, | ||
txRecord?: any | ||
} | ||
|
||
interface ITransactionListener { | ||
(data: ITransactionStatus): void|Promise<void> | ||
} | ||
|
||
type ExecuteTransaction = (optionsOrTxName?: string|TXOptions, listener?: ITransactionListener) => Promise<ITransactionStatus|false> | ||
|
||
// Usage | ||
|
||
// 1. Execute the default transaction (specified in `ts:card`) | ||
if (!await tokenscript.action.executeTransaction()){ | ||
return; // User aborted transaction | ||
} | ||
// Do some post-transaction processing | ||
|
||
// 2. Execute a top-level named transaction, with some input values from Javascript | ||
tokenscript.action.setProps({ | ||
secretCode: "1234" | ||
}); | ||
if (!await tokenscript.action.executeTransaction("levelUp")){ | ||
return; // User aborted transaction | ||
} | ||
|
||
// 3. Use a transaction progress listener | ||
await tokenscript.action.executeTransaction("levelUp", (txInfo) => { | ||
// Update UI based on transaction process | ||
}); | ||
|
||
``` | ||
|
||
Since the executeTransaction method is async, it can be used to chain together transactions and other off-chain processes. | ||
|
||
### showMessageToast | ||
|
||
This allows the developer to show message notifications using the toast notification system provided by TokenScript viewer. | ||
Along with the other UI specific methods, this helps keep the user-experience consistent across all TokenScript applications. | ||
|
||
```typescript | ||
// Type | ||
type ShowMessageToast = (type: 'success'|'info'|'warning'|'error', title: string, description: string) => void; | ||
|
||
// Usage | ||
tokenscript.action.showMessageToast("error", "An error occurred", "Detailed information about the error"); | ||
``` | ||
|
||
### showTransactionToast | ||
|
||
Like the previous method, this allows the user to show transaction specific notifications: | ||
|
||
```typescript | ||
// Type | ||
type ShowTransactionToast = (status: "submitted"|"confirmed", chain: number, txHash: string) => void; | ||
|
||
// Usage | ||
tokenscript.action.showTransactionToast('confirmed', 137, "0x05b9d22ce9ac87e399c18c488c3e02327945ffb4633890030789ea1f2f783e0b"); | ||
``` | ||
|
||
Note: This method only needs to be used when using the injected ethereum provider. If you are using declarative transactions, notifications are already taken care of by the engine. | ||
|
||
### closeCard | ||
|
||
This method simply closes the current card: | ||
|
||
```typescript | ||
tokenscript.action.closeCard(); | ||
``` | ||
|
||
### openCard | ||
|
||
You can also switch between cards using the `openCard` method. | ||
|
||
```typescript | ||
// Type | ||
type OpenCard = (name: string, originId?: string, tokenId?: string) => void; | ||
|
||
// Usage | ||
tokenscript.action.openCard({ | ||
name: "myCardName", | ||
originId: "ReallyCoolToken", // This is the name of the origin contract as defined in the tokenscript.xml. | ||
tokenId: "1" // The tokenId we want to open the card for | ||
}); | ||
``` | ||
|
||
If originId or tokenId are not provided, the current token context is used to open the new card. | ||
|
||
## Ethereum methods | ||
|
||
### Signing | ||
|
||
You can request the user to sign a personal message like this: | ||
|
||
```typescript | ||
// Type | ||
type SignPersonal = (msgParams: {data: string|Uint8Array}, callback?: (error: any, data: string) => void) => void|Promise<string>; | ||
|
||
// Usage | ||
const signature = await tokenscript.personal.sign({data: "My message"}); | ||
``` | ||
|
||
This method is async but can also take a callback as well. | ||
|
||
**Note: If you need typed data signing, use the Ethereum Provider** | ||
|
||
### getRpcUrls | ||
|
||
The `getRpcUrls` method provides an array of RPC URLs for the specified chain ID: | ||
|
||
```typescript | ||
// Type | ||
type GetRpcUrls = (chain: number) => string[]; | ||
|
||
// Usage | ||
const rpcUrls = tokenscript.eth.getRpcUrls(137); | ||
```` | ||
|
||
These URLs are configured and provided by the TokenScript Engine. | ||
|
||
### getRpcProvider | ||
|
||
Instead of requesting URLs and configuring the ethereum provider yourself, you can get an ethers.js provider directly: | ||
|
||
```typescript | ||
// Type | ||
type GetRpcProvider = (chain: number) => AbstractProvider; // ethers.AbstractProvider | ||
// Usage | ||
const provider = tokenscript.eth.getRpcProvider(137); | ||
``` | ||
|
||
If more than one RPC URL is defined for the specified chain ethers.FallbackProvider is returned, otherwise ethers.JsonRpcProvider is returned. | ||
|
||
### getContractInfo | ||
|
||
It's often helpful to get contract information based on the name specified in the TokenScript XML. | ||
In this way there is no need to hard-code contract addresses and ABI JSON within your Javascript code. | ||
Instead, this config information can be specified within the `tokenscript-project.json` environment variables and made available via this API method. | ||
|
||
```typescript | ||
// Type | ||
type GetContractInfo = (name: string, chain?: number) => {chain: number, address: string, abi: any}; | ||
// Usage | ||
const contractInfo = tokenscript.eth.getContractInfo("MyReallyCoolToken"); | ||
``` | ||
|
||
If the chain parameter isn't provided, the chain of the current token is used or the first chain specified within `ts:contract`. | ||
|
||
### getContractInstance | ||
|
||
Just like getContractInfo, we can get an ethers.Contract instance based on the name & chain provided. | ||
These contracts must have an ABI specified within `ts:contract` for the methods you want to execute. | ||
|
||
```typescript | ||
// Type | ||
type GetContractInstance = (name: string, chain?: number) => Contract; // ethers.Contract | ||
// Usage | ||
const contract = tokenscript.eth.getContractInstance("MyReallyCoolToken"); // ABI with getTokenPoints is defined in `ts:contract` | ||
const points = await contract.getTokenPoints(token.tokenId); | ||
``` | ||
|
||
This is most helpful when we want to read contract data within a card, without the use of TokenScript attributes that are loaded when the card is open. | ||
|
||
## Ethereum provider | ||
|
||
To allow scenarios that are not supported by TokenScript's declarative transactions, the Card SDK provides an EIP-1193 proxy. | ||
This is the standard injected provider (`window.ethereum`) that you would be familiar with from integrating with Metamask and other browser extension wallets. | ||
|
||
To use this provider, it's recommended that you wrap it with Ethers.js BrowserProvider like so: | ||
```typescript | ||
const provider = new ethers.BrowserProvider(window.ethereum); | ||
// You can then get an ethers contract instance to interact with a contract | ||
const contract = new ethers.Contract(contractAddress, abi, provider); | ||
``` | ||
|
||
**Note: When using the injected provider, you must take care of chain switching, transaction notifications & error handling yourself.** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters