tari-manifest
This skill should be used when the user asks to "write a manifest", "create a transaction manifest", "submit a transaction", "call a component method", "call a template function", "interact with a Tari component", "write Ootle manifest", "authenticate with wallet daemon", or needs to construct Tari Ootle transaction manifests for submitting transactions on the Tari network.
Best use case
tari-manifest is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
This skill should be used when the user asks to "write a manifest", "create a transaction manifest", "submit a transaction", "call a component method", "call a template function", "interact with a Tari component", "write Ootle manifest", "authenticate with wallet daemon", or needs to construct Tari Ootle transaction manifests for submitting transactions on the Tari network.
Teams using tari-manifest should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/tari-manifest/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How tari-manifest Compares
| Feature / Agent | tari-manifest | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
This skill should be used when the user asks to "write a manifest", "create a transaction manifest", "submit a transaction", "call a component method", "call a template function", "interact with a Tari component", "write Ootle manifest", "authenticate with wallet daemon", or needs to construct Tari Ootle transaction manifests for submitting transactions on the Tari network.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Tari Ootle Transaction Manifests
Tari Ootle manifests are a Rust-like DSL for defining transaction instructions. They are parsed by `tari_transaction_manifest::parse_manifest()` and compiled into `Vec<Instruction>` for execution on the Tari engine.
## CRITICAL: User Confirmation Required Before Submission
**NEVER submit a transaction without explicit user confirmation.** Before calling `transactions.submit_manifest` (with `dry_run: false`), ALWAYS:
1. **Present a clear summary** of what the transaction will do, including:
- The action in plain language (e.g. "Transfer 100 tTARI from account A to account B")
- The manifest source code
- All variables and their resolved values (component addresses, amounts, etc.)
- The max fee
- Which account will sign/pay fees
2. **Ask the user to confirm** before proceeding. Wait for explicit approval.
3. **Do not batch or auto-submit** multiple transactions without per-transaction confirmation.
Dry-run submissions (`dry_run: true`) may be performed without confirmation as they do not modify state.
## Manifest Structure
Every manifest has a `fn main()` block for transaction instructions and an optional `fn fee_main()` block for fee payment. Helper functions may be defined and are inlined at call sites.
```rust
// Optional: import templates by hash
use template_<64-char-hex-hash> as TemplateName;
// Optional: fee instructions (executed first)
fn fee_main() {
let account = arg!["account"];
account.pay_fee(1000);
}
// Required: main transaction instructions
fn main() {
let account = var!["account"];
let component = var!["component"];
let result = component.some_method(arg1, arg2);
account.deposit(result);
}
```
## Core Concepts
### Variables and Arguments
Access runtime variables passed to the manifest via globals:
```rust
let my_var = var!["variable_name"]; // workspace variable
let my_var = arg!["argument_name"]; // alias for var!
let my_var = global!["global_name"]; // alias for var!
```
### Template Function Calls
Call static functions on imported templates (constructors, etc.):
```rust
use template_<hash> as MyTemplate;
let component = MyTemplate::new(arg1, arg2);
```
The `Account` template is always pre-imported.
### Component Method Calls
Call methods on component variables (from `var!` or prior call results):
```rust
let result = component.method_name(arg1, arg2);
component.method_name_no_return(arg1);
```
### Chaining Results
Method call results are stored in workspace variables and can be passed to subsequent calls:
```rust
let bucket = account.withdraw(TARI, 1000);
let item = shop.buy(bucket);
account.deposit(item);
```
## Supported Argument Types
| Type | Syntax | Example |
|------|--------|---------|
| String | `"value"` | `"hello"` |
| Integer | `123u64`, `42i32` | `1_000u64` (unsuffixed defaults to i128) |
| Boolean | `true` / `false` | `true` |
| Amount | `Amount(value)` | `Amount(1000)` |
| Address | `Address("prefix_hex...")` or `Address(var)` | `Address("component_ab12...")` |
| SubstateId | `SubstateId("prefix_hex...")` | `SubstateId("vault_ab12...")` |
| NonFungibleId | `NonFungibleId("str")`, `NonFungibleId(1u32)`, `NonFungibleId(1u64)` | `NonFungibleId("MyNFT")` |
| Metadata | `Metadata("key=value")` | `Metadata("name=Token")` |
| PublicKey | `PublicKey("hex")` | `PublicKey("ab12...cd34")` |
| HexBytes | `HexBytes("hex")` | `HexBytes("deadbeef")` |
| CBOR | `Cbor("{json}")` or `cbor!({json})` | `cbor!({"key": [1, 2]})` |
| Tari token | `TARI` | `account.withdraw(TARI, 100)` (`XTR` is a deprecated alias that still works but should not be used in new manifests; when describing transactions to users, call the token **tTARI** on testnet or **$TARI** on mainnet) |
| Workspace var | bare identifier | `bucket` (from prior `let bucket = ...`) |
## Built-in Macros
```rust
// Address allocation
let addr = new_component_addr!();
let addr = new_resource_addr!();
// Logging
info!("message");
debug!("message");
warn!("message");
error!("message");
// Proof management
drop_all_proofs!();
```
## Writing Manifests Workflow
1. **Identify the template and component addresses** the transaction will interact with. These are passed as variables.
2. **Write `fee_main()`** if fees are not handled externally. Typically: `account.pay_fee(max_fee)`.
3. **Write `main()`** with the transaction logic: retrieve variables, call methods, deposit results.
4. **Pass variables** when submitting: map variable names to `ManifestValue` instances (component addresses, resource addresses, amounts, etc.).
5. **Present a summary and ask the user to confirm** before submitting (see "User Confirmation Required" above). Only submit after receiving explicit approval.
## Submitting Manifests via Wallet Daemon JSON-RPC
The wallet daemon exposes a JSON-RPC 2.0 API. All requests are POSTed to the `/json_rpc` endpoint.
### Step 1: Check Auth Method
```bash
curl -s -X POST http://localhost:12008/json_rpc \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"auth.method","params":{}}'
```
Response: `{"result":{"method":"none"}}` or `{"result":{"method":"webauthn"}}`.
### Step 2: Authenticate and Get JWT Token
**Available permissions:** `Admin`, `AccountInfo`, `AccountList`, `AccountBalance`, `KeyList`, `TransactionGet`, `TransactionSend`, `SubstatesRead`, `TemplatesRead`, `GetNft`, `NftGetOwnershipProof`. Use `Admin` for full access.
JWT tokens expire after 5 minutes by default. Refresh with `auth.refresh` or request a new one.
#### When `method` is `"none"`
Authenticate directly with `AuthCredentials::None`:
```bash
curl -s -X POST http://localhost:12008/json_rpc \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0","id":2,"method":"auth.request",
"params":{"permissions":["Admin"],"credentials":"None"}
}'
```
Response contains `{"result":{"token":"eyJ..."}}`. Store this JWT token.
#### When `method` is `"webauthn"`
WebAuthn requires a browser-based passkey interaction (biometric, hardware key, etc.) that cannot be performed by an agent directly. Ask the user to provide a JWT token. Instruct them to:
1. Open the wallet daemon web UI in their browser
2. Authenticate with their passkey
3. Copy the JWT token from the browser (e.g. from the `Authorization` header in dev tools, or from the UI if it exposes one)
4. Paste the token back to the agent
Once the user provides the token, use it in the `Authorization: Bearer <token>` header for all subsequent requests. If the token expires, ask the user to provide a fresh one.
### Step 3: Use Token for All Subsequent Requests
Include the JWT in the `Authorization` header:
```bash
curl -s -X POST http://localhost:12008/json_rpc \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ..." \
-d '{"jsonrpc":"2.0","id":3,"method":"accounts.list","params":{"offset":0,"limit":10}}'
```
### Step 4: Summarize and Confirm with User
**Before submitting**, present a human-readable summary to the user and wait for explicit confirmation. Example:
> **Transaction Summary**
> - **Action:** Transfer 100 tTARI from source account to destination account
> - **Source:** `component_e61f...bf68` (account "dsfgdfg")
> - **Destination:** `component_1642...c0c1` (account "sdfdf")
> - **Max fee:** 1000
> - **Signing key:** default
>
> Proceed? (yes/no)
Only after the user confirms, submit the manifest.
### Step 5: Submit the Manifest
```bash
curl -s -X POST http://localhost:12008/json_rpc \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ..." \
-d '{
"jsonrpc":"2.0","id":4,"method":"transactions.submit_manifest",
"params":{
"manifest": "fn fee_main() {\n let account = var![\"account\"];\n account.pay_fee(1000);\n}\n\nfn main() {\n let account = var![\"account\"];\n let dest = var![\"dest\"];\n let bucket = account.withdraw(TARI, 100);\n dest.deposit(bucket);\n}",
"variables": {
"account": "component_e61f...bf68",
"dest": "component_1642...c0c1"
},
"max_fee": 1000,
"dry_run": false
}
}'
```
**Request fields:**
- `manifest` (string): The manifest source code. Newlines as `\n`, inner quotes escaped.
- `variables` (object): Map of variable names to address/value strings (e.g. `component_<hex>`, `resource_<hex>`, `"1000u64"`).
- `signing_key_id` (optional): Override the signing key. Omit to use default account key.
- `max_fee` (u64): Maximum fee in microtari.
- `dry_run` (bool): If `true`, executes without submitting to network. Returns result in response.
**Response:**
```json
{
"result": {
"transaction_id": "tx_<hex>",
"result": null
}
}
```
`result` is `null` for live submissions (use `transactions.wait_result` to poll). For `dry_run: true`, it contains the `ExecuteResult`.
### Step 6: Wait for Result (optional)
```bash
curl -s -X POST http://localhost:12008/json_rpc \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJ..." \
-d '{
"jsonrpc":"2.0","id":5,"method":"transactions.wait_result",
"params":{"transaction_id":"tx_<hex>","timeout_secs":120}
}'
```
### Useful Endpoints
| Method | Description |
|--------|-------------|
| `auth.method` | Check auth type (no auth required) |
| `auth.request` | Get JWT token |
| `auth.refresh` | Refresh expired JWT token |
| `accounts.list` | List accounts (`offset`, `limit`) |
| `accounts.get_default` | Get default account |
| `accounts.get_balances` | Get account balances |
| `transactions.submit_manifest` | Submit a manifest transaction |
| `transactions.wait_result` | Wait for transaction finalization |
| `transactions.get_result` | Poll transaction result (non-blocking) |
| `substates.get` | Fetch a substate by address |
| `templates.get` | Fetch template function definitions |
### Via Wallet CLI
```bash
tari_wallet_cli transaction submit-manifest manifest.tm \
-g account=component_<hex> \
-g resource=resource_<hex> \
--max-fee 1000
```
### Programmatically (Rust)
```rust
use tari_transaction_manifest::{parse_manifest, ManifestValue};
let mut globals = HashMap::new();
globals.insert("account".to_string(), ManifestValue::from(account_address));
let instructions = parse_manifest(&manifest_str, globals, HashMap::new())?;
// instructions.instructions - main transaction instructions
// instructions.fee_instructions - fee payment instructions
```
## Token Naming
The native Tari token is referred to as `TARI` within manifest code. `XTR` is a deprecated alias that still works but should not be used in new manifests. When communicating with users, always use the proper token name:
- **Testnet:** tTARI
- **Mainnet:** $TARI
Never refer to the token as "XTR" in user-facing summaries, descriptions, or new manifest code.
## Common Patterns
### Withdraw and Deposit
```rust
fn main() {
let source = var!["source_account"];
let dest = var!["dest_account"];
let resource = var!["resource"];
let bucket = source.withdraw(resource, 1000);
dest.deposit(bucket);
}
```
### Create Proof and Call Protected Method
```rust
fn main() {
let account = var!["account"];
let admin_badge = var!["admin_badge"];
let component = var!["component"];
let proof = account.create_proof_by_amount(Address(admin_badge), 1);
component.admin_action();
drop_all_proofs!();
}
```
### Instantiate a Component
```rust
use template_<hash> as MyTemplate;
fn main() {
let new_component = MyTemplate::new("config_value", 42u64);
}
```
## Additional Resources
### Reference Files
- **`references/manifest-syntax.md`** - Complete syntax reference with all supported constructs and edge cases
- **`references/wallet-daemon-api.md`** - Full wallet daemon JSON-RPC API reference including all auth, transaction, account, and substate endpoints
### Example Files
- **`examples/token_swap.rs`** - Token swap between accounts via a DEX component
- **`examples/initialize_component.rs`** - Component initialization from a template
- **`examples/nft_mint_and_deposit.rs`** - Mint an NFT and deposit to accountRelated Skills
windsurf
Tari Ootle development instructions for Windsurf
openai-codex
Tari Ootle development instructions for OpenAI Codex
google-gemini
Tari Ootle development instructions for Google Gemini
github-copilot
Tari Ootle development instructions for GitHub Copilot
cursor
Tari Ootle development instructions for Cursor
amp
Tari Ootle development instructions for Amp
aider
Tari Ootle development instructions for Aider
Manifest
Install and configure the Manifest observability plugin for your agents. Use when setting up telemetry, configuring API keys or endpoints, troubleshooting plugin connection issues, or verifying the observability pipeline is running.
scanning-kubernetes-manifests-with-kubesec
Perform security risk analysis on Kubernetes resource manifests using Kubesec to identify misconfigurations, privilege escalation risks, and deviations from security best practices.
pwa-manifest-generator
Pwa Manifest Generator - Auto-activating skill for Frontend Development. Triggers on: pwa manifest generator, pwa manifest generator Part of the Frontend Development skill category.
slack-manifest
Guide for modifying the Slack app manifest — adding/removing bot scopes, event subscriptions, slash commands, shortcuts, or OAuth config. Ensures single-source-of-truth via slack-app-manifest.json. Triggers on: slack scope, bot scope, slack manifest, slack permission, add slack scope, remove slack scope, slack event subscription, slash command, slack OAuth, slack-app-manifest.
morning-manifesto
Daily morning reflection workflow with task sync to Obsidian, Apple Reminders, and Linear