The caller signs a voucher
Its node signs a contract voucher with the node's advertised price and maxAmount, the most the call may cost. A call made from a billed run carries that run's id too.
Build / Contracts
A contract is a RequestScript script you deploy to a node. It becomes one of the node's resources, so anyone on the network can call it, and it can call any resource itself. You don't write TypeScript or run your own node, and every call is paid for by whoever made it.
POST /v1/contractsrun01
Most resources on Stellarium are TypeScript objects that a node's owner registers before the node starts. A contract is a resource written in RequestScript instead, and deployed to a node that's already running, by its owner or by another organization.
| Registered resource | Contract | |
|---|---|---|
| Written in | TypeScript | RequestScript |
| Added by | the node's owner, at startup | anyone with a run token, at any time |
| Functions | any you declare | one, run, taking the contract's parameters |
| Price | per call, per token, or free | the tokens it runs, at the node's price |
| Code | stays private on the node | public: anyone can read what a call does |
To a script, a contract looks like any other resource. Peers learn about it as soon as it's deployed.
02
A contract is a RequestScript contract with two requirements:
com.example, so its full name is unique on the network.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"
}This contract calls the paid com.example.Weather resource and turns its answer into words. Like any script, it checks error on a call's result, so a failed call doesn't fail the contract.
03
Send the source to the node's POST /v1/contracts, with a run token from one of your organization's API clients for that node. Create the API client on the Stellarium Platform. The node's owner can also use its API key.
curl -X POST https://node.example/v1/contracts \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $RUN_TOKEN" \
-d '{"source": "contract com.example.Outlook(city: string): string { ... }", "description": "Today in words"}'{ "path": "com.example", "name": "Outlook", "version": "1", "feeMicroUsd": 1000 }If you run a node, these settings control what others can deploy to it:
# What another organization pays to deploy a contract here, in micro-USD.
CONTRACT_DEPLOY_FEE_MICRO_USD=1000
# The largest contract this node accepts. Defaults to 64 KiB.
MAX_CONTRACT_SOURCE_BYTES=6553604
A script binds a contract like any resource and calls its run function:
request Today {
const outlook: com.example.Outlook
return outlook.run(city: "Oslo")
}{ "returnValue": { "value": "12 degrees", "error": null }, "usage": { "tokens": 17 } }Contracts can call other contracts, up to 8 deep, so a loop between them stops. A contract that fails, for example by dividing by zero, comes back as the call's error.
05
A contract runs on a node that didn't write it, and it may make paid calls of its own. Stellarium makes one rule hold for every call: the caller pays for everything the contract does, never the node hosting it.
Its node signs a contract voucher with the node's advertised price and maxAmount, the most the call may cost. A call made from a billed run carries that run's id too.
The voucher must be for this exact call, unexpired, signed by the caller and paying the host's organization. Without one, the call is refused before anything runs.
It opens a run for the call and holds up to maxAmount on the caller's balance, or out of the caller's own run. It never charges more than the price the caller signed.
Every paid call the contract makes draws on that run, so the host never pays for a caller's contract.
Afterwards, the host captures the tokens the contract executed, charged at its price per million tokens. A contract that failed is still charged for what it ran.
Whatever the run didn't spend returns to where it came from: the caller's balance, or the caller's run.
A contract that makes paid calls costs more than one call, so a node that calls contracts can raise its ceiling:
# The most one call to a contract on another node may cost, with the
# paid calls the contract makes, in micro-USD. Defaults to COST_LIMIT_MICRO_USD.
CONTRACT_COST_LIMIT_MICRO_USD=5000006
contract com.example.Outlook@2(...). Deploying a version that exists is refused.GET /v1/contracts lists every version with its source, so you can read exactly what a call will do.DELETE /v1/contracts/com.example.Outlook@2. The node's owner can delete any contract on their node.07
A contract can keep values between calls in state variables, declared with var state at the top of its body. Only the contract's own code can change them, so the published source is also the policy for its data: a counter, a quota, a cache, or an agent's memory.
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 is one copy shared by every caller. var state(scope) name keeps a copy for each value of scope, any expression that evaluates to a string or a number.caller tells a contract who it runs for: caller.org is the organization paying for the call, and caller.node the node that made it. Both are verified, so state(caller.org) gives each organization a copy no one else can reach.08
Contracts appear in GET /v1/resources with kind: "contract" and their price, alongside every other function. Read the full reference in the stellarium-ts README and the documentation, and learn the language at requestscript.org.
Built a contract? Show it off on Discord.