Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/docs/custom-templates/visual-editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
158 changes: 158 additions & 0 deletions custom-requests/manifest-dsl.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
---
title: "Manifest DSL Reference"
description: "The JSON shape that defines a custom request template: the call, the form, and the arguments"
---

A template is a JSON object called a **manifest**. It defines the on-chain call to make, the form a member fills, and how the filled values map into the call's arguments. This page is the full reference for that shape, useful when you hand-author a template in the **`Code`** tab or check a manifest an AI assistant produced.

For what the feature is and how to enable it, see [Custom Requests](/custom-requests/overview).

<Tip>
You do not have to write this by hand. An AI assistant can produce a manifest from a plain-English description. Install the [`trezu-custom-proposal-template` skill](https://github.com/NEAR-DevHub/trezu/tree/main/docs/trezu-custom-proposal-template) and paste its output into the **`Code`** tab, then use this page to check the result.
</Tip>

***

## The Manifest

A manifest is a JSON object. `version` is `1`, the only shape this schema describes.

| Key | Required | Notes |
|-----|----------|-------|
| `version` | yes | The literal `1`. |
| `id` | yes | A slug using `[A-Za-z0-9_-]`, unique within your treasury. It is both the template's page address and the marker stamped on every request it files. Reserved: `create`, `new`, `about`. |
| `title` | yes | A human title for the template. |
| `description` | no | A longer explanation, shown to the member filling the form. |
| `icon` | no | An optional icon name. |
| `summary` | no | A one-line human summary shown while filing. It may use `{{placeholders}}`. |
| `binding` | yes | The fixed on-chain call. See below. |
| `fields` | yes | The form inputs. See below. |
| `args` | yes | The call's arguments. See below. |

***

## `binding` - The Call

`binding` is the part of the call the author fixes. A member can never change it.

```json
"binding": {
"receiver_id": "guestbook.near",
"method_name": "add_message",
"deposit": "1",
"gas": "30000000000000"
}
```

| Key | Notes |
|-----|-------|
| `receiver_id` | The contract to call. |
| `method_name` | The method to call on it. |
| `deposit` | The attached deposit in yoctoNEAR, as a whole-number string. |
| `gas` | The gas to attach, as a whole-number string. |

<Warning>
`deposit` and `gas` are whole-number strings in base units, never decimals and never JSON numbers. NEAR amounts are larger than a normal number can hold without losing precision, so they stay as strings. For example, 1.1 NEAR is written `"1100000000000000000000000"` yoctoNEAR.
</Warning>

***

## `fields` - The Form Inputs

`fields` is an array. Each field describes one input on the form.

```json
{ "name": "amount", "label": "Tip amount", "type": "amount", "required": true }
```

| Key | Notes |
|-----|-------|
| `name` | The input's key, using `[A-Za-z0-9_]`, unique within the manifest. This is what `{{placeholders}}` reference. |
| `label` | The text shown above the input. |
| `type` | One of the types below. |
| `required` | Optional flag. Not allowed on `bool`, since a toggle always submits a value. |
| `default` | Optional starting value. Must match the field's `type`. |
| `help` | Optional hint shown under the input. |
| `options` | Required for `select`, not allowed otherwise. An array of strings. |
| `validation` | Optional rules. See below. |

### Field Types

| `type` | Renders and validates as |
|--------|--------------------------|
| `account` | A NEAR account id, with its shape checked. |
| `token` | An omni token id such as `base-0x...`, as free text. |
| `uint` | A whole-number string, safe for large values. |
| `amount` | A whole-number string in base units, safe for large values. No decimals. |
| `number` | A plain numeric value, for counts and ratios, not for token amounts. |
| `text` | Free text. |
| `select` | A dropdown of `options`. |
| `bool` | A toggle. |
| `json` | Raw JSON text. |

### `validation`

| Rule | Notes |
|------|-------|
| `min` / `max` | Whole-number strings. Only on the numeric types (`uint`, `amount`, `number`). |
| `pattern` | A regular expression that must compile. Only on `text` and `number`. |

<Note>
Use `amount` or `uint` (not `number`) for anything that becomes a token amount or other large on-chain value. These stay as digit strings the whole way through, so a value larger than a normal number can hold survives without being rounded.
</Note>

***

## `args` - The Call's Arguments

`args` is a JSON object, the literal arguments passed to the method. Any string value can contain `{{name}}` placeholders that reference a field by its `name`. When a member files the request, each placeholder is replaced with the value they entered.

```json
"args": {
"app": "trezu",
"text": "{{message}}",
"amount": "{{tip}}",
"meta": "{\"by\":\"{{author}}\"}"
}
```

In this example `app` is a fixed value, `text` and `amount` are taken straight from member inputs, and `meta` composes a member input into a larger string.

A value does not have to be a string. `args` can hold any JSON, including numbers, booleans, `null`, nested objects, and arrays. Non-string values are sent to the contract exactly as written, because placeholders are only ever resolved inside strings.

Rules:

- Every `{{placeholder}}` must reference a declared field. A reference to a name that does not exist is an error.
- To write a literal `{{message}}` without it being treated as a placeholder, double the braces as `{{{{message}}}}`. It collapses to `{{message}}` in the output.
- Composing values is just putting more than one placeholder in a string. `"{{first}}.{{last}}"` becomes `"alice.near"`.

***

## A Complete Example

A template that sets a greeting on a guestbook contract:

```json
{
"version": 1,
"id": "set-greeting",
"title": "Set Greeting",
"binding": {
"receiver_id": "guestbook.near",
"method_name": "set_greeting",
"deposit": "0",
"gas": "30000000000000"
},
"fields": [
{ "name": "greeting", "label": "Greeting", "type": "text", "required": true }
],
"args": { "greeting": "{{greeting}}" },
"summary": "Set greeting to {{greeting}}"
}
```

***

## What a Filled Template Produces

Filling and submitting the form above creates one treasury request: a `FunctionCall` to `guestbook.near` running `set_greeting`, with the member's greeting as the argument, the fixed `deposit` and `gas` from the binding, and a description carrying the template's title and an automatic marker that records which template filed it. From there it is an ordinary request that the team approves and the treasury executes.
115 changes: 115 additions & 0 deletions custom-requests/overview.mdx

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This page is excruciatingly boring and I cannot imagine any human reading it. Can you condense it?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Didn't know people have fun reading docs :) I've tried to capture the tone and style of other docs you've accepted. Added screenshots. I hope it's more fun and less boring to you now.

Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
---
title: "Custom Requests"
description: "Author reusable forms that let your treasury file any on-chain call as a proposal, with no developer needed"
---

Trezu builds your everyday treasury proposals for you - payments, transfers, swaps, and staking. But your treasury is a SputnikDAO, and a SputnikDAO can call **any method on any contract**. Until now Trezu could show you those calls when someone else created them, it just had no way for you to create them yourself.

**Custom Requests** close that gap. A member authors a reusable form once, and from then on anyone on the team can fill that form to file an arbitrary on-chain call as a normal request - with no hand-written arguments and no developer needed. Because it travels through your treasury's usual approvals and execution, a template never grants any authority on its own.

<Warning>
Enabling Custom Requests and authoring templates are reserved for members with the [Governance](/governance/members-and-roles#governance) role - the same permission that governs changes to your treasury policy. Once a template exists, any team member can fill and file it.
</Warning>

***

## How It Works

A custom request moves through three stages, each gated by its usual permission.

<Steps>
<Step title="Author a template">
A Governance member describes the call - the contract and method to invoke - and lays out the form members will fill. The template is saved to your treasury and reused as often as you like.
</Step>
<Step title="Fill the form">
Any team member opens the template, fills in the inputs such as an amount, an account, or a message, and submits. Trezu assembles the on-chain call for them.
</Step>
<Step title="Approve and execute">
The submission lands in the **`Requests`** section as a `Pending` request, where the team reviews and votes on it just like any other. Once it reaches the voting threshold, the treasury executes the call.
</Step>
</Steps>

The author fixes the parts that must never change - the contract, the method, the attached deposit, and the gas. Members only ever supply the **arguments** to the call, so a filled request can never be redirected to a different contract or method.

***

## Turning It On

Custom Requests ship disabled. A Governance member turns the feature on from **`Settings`** → **`Developer`**, and a **`Request Templates`** section appears in the left sidebar for everyone on the team.

![Enabling Custom Requests from Settings → Developer](/assets/docs/custom-templates/developer-settings.png)

<Check>
The whole team can see and fill templates as soon as the feature is on. Only authoring stays with Governance.
</Check>

***

## Authoring a Template

A template is defined by a **manifest**, a small JSON document that captures the call and the form together. The editor gives you two ways to build one, and you can switch between them at any time.

<CardGroup cols={2}>
<Card title="Visual" icon="table-cells">
A form builder that is arguments-first. Each argument is either **static**, a fixed value the author sets, or a **member input** that is filled in at request time. Reference an input and it becomes a form field automatically. This is the default, and it covers most templates.
</Card>
<Card title="Code" icon="code">
A live-validated JSON editor over the same manifest, handy for pasting one in or making power edits.
</Card>
</CardGroup>

![The arguments-first Visual editor, with a fixed on-chain call and arguments that are each static or a member input](/assets/docs/custom-templates/visual-editor.png)

<Tip>
Would rather not touch JSON? Hand the [Manifest DSL reference](/custom-requests/manifest-dsl) to any AI assistant, describe your proposal in plain English, and paste the manifest it returns into the **`Code`** tab. There is also a self-contained [`trezu-custom-proposal-template` skill](https://github.com/NEAR-DevHub/trezu/tree/main/docs/trezu-custom-proposal-template) for Claude Code, Claude.ai, and Codex.
</Tip>

***

## What Members Fill

Once a template exists, filling it is as easy as any other request. A member opens it, completes the generated form, and clicks **`File Proposal`**. A one-field template is about as simple as it gets.

![Filling a one-field Set Greeting template](/assets/docs/custom-templates/custom-form-simple.png)

The same mechanism scales all the way up to serious operations. Here a recovery-mint template gathers six inputs and files an omni-FT `ft_deposit` call, with every field validated before the request can be submitted.

![Filling the multi-field Recovery Mint template](/assets/docs/custom-templates/custom-form-standard.png)

***

## Who Can Do What

| Action | Required |
|--------|----------|
| Enable the feature, or author, edit, or delete a template | Governance role |
| See and fill a template | Team membership |
| Approve the resulting request | Your treasury's normal voting rules |

***

## Frequently Asked Questions

<Accordion title="Can I edit or delete a template after members have used it?">
Yes. A Governance member can edit or delete any template at any time from the **`Request Templates`** section. Requests that members already filed are unaffected, and continue through their normal review and execution.
</Accordion>

<Accordion title="Can a member change which contract or method gets called?">
No. The author fixes the contract, method, deposit, and gas when they build the template. Members can only fill in the arguments, so a filled request can never be pointed somewhere else.
</Accordion>

<Accordion title="Do I need to write JSON to author a template?">
Not at all. The Visual editor lets you build the whole call and its form without touching JSON. If you would rather describe what you want in plain English, hand the [Manifest DSL reference](/custom-requests/manifest-dsl) to an AI assistant and paste the result into the **`Code`** tab.
</Accordion>

<Accordion title="What does a custom request look like to approvers?">
Exactly like any other request. It appears in **`Requests`** as `Pending`, shows the call it will make, and follows your treasury's normal voting rules before it executes.
</Accordion>

***

## What's Next?

<Check>
Ready to build one? Head to the [Manifest DSL reference](/custom-requests/manifest-dsl) for the full shape of a manifest, useful whether you are hand-authoring a template or checking what an assistant produced.
</Check>
7 changes: 7 additions & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@
"governance/handling-requests"
]
},
{
"group": "Custom Requests",
"pages": [
"custom-requests/overview",
"custom-requests/manifest-dsl"
]
},
{
"group": "Finance",
"pages": [
Expand Down