Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
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.
102 changes: 102 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,102 @@
---
title: "Custom Requests"
description: "Author reusable forms that let your treasury file any on-chain call as a proposal, with no developer needed"
---

Trezu creates the everyday treasury proposals (payments, transfers, swaps, staking) for you. But a treasury is a SputnikDAO, and a SputnikDAO can call **any method on any contract**. Trezu could already display those calls when someone else created them. It just had no way to **create** them.

**Custom Requests** close that gap. A treasury authors a reusable form once, and members fill that form to file an arbitrary on-chain call as a normal request, with no hand-written arguments and no developer needed.

<Info>
A custom request is still a regular treasury request. It goes through the same approvals and execution as a payment or a role change, so a template never grants authority by itself.
</Info>

***

## Turning it on

Custom Requests ship disabled. A member with the **[Governance](/governance/members-and-roles#governance) role** enables it from **`Settings`** -> **`Developer`**.

{/* screenshot: Settings -> Developer, the Custom Requests toggle */}

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.

todo?


Once enabled, a **`Request Templates`** section appears in the left sidebar for everyone on the team.

<Warning>
Enabling the feature is gated on the same on-chain permission that gates changing the treasury policy. A regular member can see and use templates, but only a Governance member can turn the feature on or author templates.
</Warning>

***

## How it works

A custom request moves through three stages, each with its own permission.

<Steps>
<Step title="Author a template">
A Governance member opens **`Request Templates`** and builds a template: the contract and method to call, plus the form a member will fill. This is saved to your treasury and reused.
</Step>
<Step title="Fill the form">
Any member opens the template, fills the inputs (an amount, an account, a message), and submits. Trezu turns the filled form into the on-chain call for them.
</Step>
<Step title="Approve and execute">
The submission becomes a `Pending` request in the **`Requests`** section, where the team reviews and votes on it like any other request. On approval, the treasury executes the call.
</Step>
</Steps>

The template author fixes the parts that must not change (the contract, the method, the attached deposit, and the gas). Members only ever supply the **arguments** to the call. They cannot redirect it to a different contract or method.

***

## Two ways to author

A template is defined by a **manifest**, a small JSON document. The editor offers two modes over the same manifest, and you can switch between them at any time.

<CardGroup cols={2}>
<Card title="Visual" icon="table-cells">
A form builder. You describe the call and its inputs without writing JSON. This is the default and covers most templates.
</Card>
<Card title="Code" icon="code">
A JSON editor over the same manifest, with live validation. Useful for pasting a manifest in or making power edits.
</Card>
</CardGroup>

### The Visual editor is arguments-first

The call is the unit you build. Each **argument** to the call is one of two things:

- **Static** — a fixed value the author sets (a piece of text, a number, a flag). It is the same on every request.
- **Member input** — a value the member fills in when they file. The input's name becomes the argument, so naming the input names the argument.

Anything you reference as a member input automatically becomes a field on the form. The author lays out the call, and the form builds itself from it.

***

## Build a template with an AI assistant

You do not have to write the manifest by hand. We publish a portable AI **skill** that walks an assistant through building a template for you. You describe the proposal in plain English, the assistant asks what is fixed and what a member fills, and it returns a valid manifest to paste into the **Code** tab. This is the fastest path for a non-developer.

The skill is self-contained, so it works without any access to the Trezu codebase. Install it into your assistant of choice:

<CardGroup cols={2}>
<Card title="Get the skill" icon="github" href="https://github.com/NEAR-DevHub/trezu/tree/main/docs/trezu-custom-proposal-template">
The `trezu-custom-proposal-template` skill, with a README covering Claude Code, Claude.ai, and Codex.
</Card>
<Card title="Use it in Claude Code" icon="terminal">
Drop the folder into `~/.claude/skills/`, then ask: "Build a Trezu custom proposal template that pays USDC to a recipient each month."
</Card>
</CardGroup>

<Tip>
See the [Manifest DSL reference](/custom-requests/manifest-dsl) for the full shape of a manifest, useful both for hand-authoring and for checking what an assistant generated.
</Tip>

***

## Who can do what

| Action | Required |
|--------|----------|
| Enable the feature | Governance role |
| Author, edit, or delete a template | Governance role |
| See and fill a template | Team membership |
| Approve the resulting request | The normal voting rules for that treasury |
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