> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stellarium.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Contracts: RequestScript resources on any node

> Deploy RequestScript contracts to a Stellarium node as resources the network can call, billed by the tokens they run, with every paid call paid by the caller.

A **contract** is a [RequestScript contract](https://docs.requestscript.org/contracts/overview) deployed to a running Stellarium node. Its latest version becomes one of the node's resources, so any node on the network can call it, and it can call any resource itself. You write it in RequestScript instead of TypeScript, and you can deploy it to another organization's node with a [run token](https://docs.stellarium.sh/concepts/api-clients.md), without running a node of your own.

| | Registered resource | Contract |
| - | - | - |
| Written in | TypeScript | RequestScript |
| Added by | The node's owner, before `start` | The node's owner, or any organization with a run token for the node, at any time |
| Functions | Any you declare | One, `run`, which takes the contract's parameters |
| Price | Per call, per token, or free | The [script tokens](https://docs.stellarium.sh/concepts/api-clients.md#script-tokens) it executes, at the node's listed price |
| Code | Stays on the node | Public: `GET /v1/contracts` returns every version's source |

## Writing a contract

A contract for Stellarium needs two things a plain RequestScript contract doesn't:

* **A path**, such as `com.example`, so its full name is unique on the network.
* **A return type**, declared after the parameters. It needs RequestScript 1.4.0 or later. The node refuses a contract without one.

```kotlin Outlook.req theme={null}
contract com.example.Outlook(city: string): string {
    const weather: com.example.Weather
    const today = weather.temperature(city: city)
    if (today.error != null) {
        return "unknown"
    }
    return "${today.value} degrees"
}
```

The contract's body can bind and call any resource the node knows about, including other contracts.

## Deploying a contract

Send the source to the node's [`POST /v1/contracts`](https://docs.stellarium.sh/node/http-api#contracts), with the node's `API_KEY` or a run token from one of your organization's API clients for that node:

```bash theme={null}
curl -X POST https://node.example.com/v1/contracts \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"source": "contract com.example.Outlook(city: string): string {\n  const weather: com.example.Weather\n  return \"${weather.temperature(city: city).value} degrees\"\n}", "description": "Today in words"}'
```

```json Response theme={null}
{ "path": "com.example", "name": "Outlook", "version": "1", "feeMicroUsd": 1000 }
```

* **Deploying may cost a fee.** A node sets what other organizations pay to deploy to it with `contractDeployFeeMicroUsd`. The fee is paid from your API client's run, like a paid call, and isn't charged if the deploy is refused. Deploys with the node's API key, or by the node's own organization, are free.
* **Peers learn about it right away.** The node announces its resources to its peers again after every deploy and delete, so they can call the contract without restarting.
* **Names are taken once.** A contract can't use the name of a resource registered on the node, or of a resource the node learned from a peer.

### Versions and ownership

Versions are immutable, as in [RequestScript versioning](https://docs.requestscript.org/contracts/versioning). To change a contract, deploy it again with a new version, such as `contract com.example.Outlook@2(...)`. Deploying a version that exists is refused with `409`. Calls always run the latest version.

The first organization to deploy a contract owns every version of it. Only that organization's API clients can add versions or delete it, with [`DELETE /v1/contracts/:ref`](https://docs.stellarium.sh/node/http-api#contracts). The node's API key can do both for any contract on the node.

## Calling a contract

A script binds a contract like any resource and calls `run` with the contract's parameters:

```kotlin Today.req theme={null}
request Today {
    const outlook: com.example.Outlook

    return outlook.run(city: "Oslo")
}
```

```json Response theme={null}
{ "returnValue": { "value": "12 degrees", "error": null }, "usage": { "tokens": 17 } }
```

In `GET /v1/resources`, a contract's function has `kind: "contract"` and a `paymentManifest` with `priceMicroUsdPerMillionTokens` and `recipientOrganizationId`.

* A contract that fails while running, for example by dividing by zero, comes back as the call's `error`. Its tokens are still charged.
* Contracts can call other contracts, at most 8 deep. A deeper call fails, so contracts that call each other in a loop stop.
* A contract called from a script on the same node reads that script's run token as the [built-in variable](https://docs.stellarium.sh/concepts/built-in-variables.md) `jwt`. Called from another node, `jwt` is `null`. Every contract reads [`caller`](https://docs.stellarium.sh/concepts/built-in-variables#caller), whoever called it.

## Keeping state between calls

A contract can keep values between calls in **state variables** (RequestScript 1.5.0 or later), declared with `var state` at the top level of its body. Only the contract's own code can change them, so its published source is also the policy for its data.

```kotlin Visits.req theme={null}
contract com.example.Visits(page: string): int32 {
    var state total: int32 = 0
    var state(caller.org) mine: int32 = 0

    total = total + 1
    mine = mine + 1
    return mine
}
```

* `var state name = value` is one copy shared by every caller. `var state(scope) name = value` keeps a copy for each value of `scope`, any expression that evaluates to a string or an integer. The initializer runs the first time a copy is used. See [State variables](https://docs.requestscript.org/contracts/overview#state-variables) for the language rules.
* **`caller`** is a [built-in variable](https://docs.stellarium.sh/concepts/built-in-variables.md#caller) every contract reads: the organization paying for the call and the node that made it, both verified. `var state(caller.org) ...` gives each paying organization its own copy, which no other caller can reach.
* **State is saved only if the call succeeds.** A call that fails, or would grow a scope past the node's `maxContractStateBytes` (64 KiB by default), saves nothing. Its tokens are still charged.
* **Calls to a stateful contract run one at a time on its node**, so concurrent calls never lose an update. A call waits at most 30 seconds for earlier ones, and fails if it has to wait longer. A contract that calls itself, directly or through other contracts, fails its outer call rather than lose the inner call's update.
* **State belongs to the contract, not a version.** A new version keeps it, and a stored value is converted to the new version's declared type. A value that doesn't fit fails the call. Deleting every version of a contract deletes its state.
* **State is private.** A contract shares it only through what it returns. Its owner and the node's admin can read all of it with [`GET /v1/contracts/:ref/state`](https://docs.stellarium.sh/node/http-api#contracts).

## How a call is paid

A contract runs on a node that may not have written it, and it may make paid calls of its own. So every call follows one rule: **the caller pays for everything the contract does**, never the node hosting it.

```mermaid theme={null}
sequenceDiagram
  participant C as Caller node
  participant H as Hosting node
  participant O as Operator
  C->>H: POST /v1/run/resource + contract voucher
  H->>O: Authorize a run for the call (hold up to maxAmount)
  O-->>H: Held
  H->>H: Run the contract, its paid calls naming the run
  H-->>C: Result
  H->>O: Capture the tokens executed
  Note over O: Unused hold goes back to the caller
```

1. The caller's node signs a **contract voucher** (`kind: "contract"`) for the call, with the node's advertised price and `maxAmount`, the most the call may cost. `maxAmount` is the caller's `contractCostLimitMicroUsd`. A call made from a billed run also carries the run's `runId`.
2. The hosting node refuses a call without a contract voucher with `402`. It checks that the voucher is bound to this exact call, pays its own organization, agrees to at least its current price, hasn't expired, and is signed by the voucher's key. If any check fails it responds `400`.
3. The node asks the operator to **authorize a run** for the call. The operator holds up to `maxAmount` on the caller organization's available balance or, for a voucher with a `runId`, out of that run's hold. It refuses the call if the hold can't cover the contract's most tokens at the node's price. If the caller is in the hosting node's own organization, the call isn't billed.
4. The contract runs. Each paid function it calls is paid with a voucher naming the contract's run, exactly as a [billed script's calls](https://docs.stellarium.sh/concepts/api-clients.md#how-a-billed-run-works) are.
5. The node **captures** the run with the tokens the contract executed, charged at the node's price, and never at more than the price the caller signed. What's left of the hold goes back where it came from: the caller's balance, or the caller's run.

The operator's refusals reach the caller as the call's `error`, with the operator's status, as for any paid call: see [How a paid call works](https://docs.stellarium.sh/concepts/payments.md#how-a-paid-call-works).

> [!TIP]
>
> A contract that makes paid calls costs more than a single call. `contractCostLimitMicroUsd` defaults to `costLimitMicroUsd`, which covers one call, so raise it on nodes that call contracts:
>
> ```bash .env theme={null}
> CONTRACT_COST_LIMIT_MICRO_USD=50000
> ```

A node learns its listed price from the operator when it starts, and checks it again every five minutes. When the price changes, the node announces its contracts again with the new price.

## Hosting contracts

Any node accepts contracts with its API key. To let other organizations deploy, list the node and enroll it in an organization, as for [API clients](https://docs.stellarium.sh/concepts/api-clients.md#listing-a-node). These [configuration](https://docs.stellarium.sh/node/configuration.md) options control what they can deploy:

| Option | Variable | Default | Purpose |
| - | - | - | - |
| `contractDeployFeeMicroUsd` | `CONTRACT_DEPLOY_FEE_MICRO_USD` | `0` | What another organization pays to deploy a contract |
| `maxContractSourceBytes` | `MAX_CONTRACT_SOURCE_BYTES` | `65536` | The largest contract the node accepts. A larger one is refused with `413` |
| `maxContractStateBytes` | `MAX_CONTRACT_STATE_BYTES` | `65536` | The most one contract's state may hold in one scope, in bytes |

Contracts and their state are stored in the node's database, SQLite or [Postgres](https://docs.stellarium.sh/node/configuration.md#local-state), and survive restarts.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.