From 0113f1134dc9700bd67cef9fe9caf573cc5fe9a9 Mon Sep 17 00:00:00 2001 From: Zach Fedor Date: Thu, 1 Oct 2026 18:05:12 -0400 Subject: [PATCH 1/5] docs: update config documentation for scaffold.yml v2 --- CLAUDE.md | 3 +- README.md | 34 +- crates/stellar-scaffold-cli/README.md | 71 +-- crates/stellar-scaffold-ext-types/README.md | 4 +- crates/stellar-scaffold-reporter/README.md | 8 +- docs/site/docs/cli.md | 65 ++- docs/site/docs/environments.md | 408 ++++++++++-------- docs/site/docs/extensions.md | 49 +-- docs/site/docs/quick-start.mdx | 11 +- docs/site/docs/tutorial/01-getting-started.md | 26 +- .../docs/tutorial/02-making-improvements.md | 158 ++++--- 11 files changed, 471 insertions(+), 366 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4f7d070c..27e145b1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -65,7 +65,8 @@ Clippy and warning flags are configured in `.cargo/config.toml` `rustflags` and `init` → `build` → `generate` → `watch` - `init` — scaffolds a new project; fetches the frontend template from `stellar-scaffold/ui` via degit - `upgrade` — converts an existing Soroban workspace into a full scaffold project -- `build` — builds contracts and generates TypeScript clients based on `environments.toml` +- `build` — builds contracts and generates TypeScript clients based on `scaffold.yml` (version 2: networks, accounts, contracts); version 1 projects still fall back to `environments.toml` +- `config check` / `config show` — validate `scaffold.yml` and print the resolved config for one network - `generate contract` — adds a new contract to an existing project - `watch` — rebuilds on changes diff --git a/README.md b/README.md index cbbccf7f..bd6dc1de 100644 --- a/README.md +++ b/README.md @@ -105,21 +105,19 @@ After scaffolding a project, your folder structure will look like this: ``` my-project/ ├── contracts/ # Rust smart contracts (compiled to WASM) -├── packages/ # Auto-generated TypeScript contract clients -├── src/ # React frontend code -│ ├── components/ # Reusable UI pieces -│ ├── contracts/ # Contract interaction logic -│ ├── App.tsx # Main app component -│ └── main.tsx # Entry point -├── environments.toml # Configuration per environment (dev/test/prod) +├── app/ # Your frontend +│ └── src/ # Components, pages, hooks, entry point +├── app-lib/ # Utility code, ready to use from your app +│ └── clients/ # Generated contract clients — regenerated, don't edit +├── scaffold.yml # Networks, accounts, and contracts ├── .env # Local environment variables -├── package.json # Frontend packages +├── package.json # npm workspace root ├── target/ # Build outputs ``` This template provides a ready-to-use frontend application with example smart contracts and their TypeScript clients. You can use these as reference while building your own contracts and UI. The frontend is set up with `Vite`, `React`, and includes basic components for interacting with the contracts. -See the [CLI Documentation](https://scaffoldstellar.com/docs/cli) for detailed command information and the [Environments Guide](https://scaffoldstellar.com/docs/environments) for configuration details. +See the [CLI Documentation](https://scaffoldstellar.com/docs/cli) for detailed command information and the [Configuration Guide](https://scaffoldstellar.com/docs/environments) for configuration details. --- @@ -150,15 +148,13 @@ Scaffold Stellar's build pipeline supports extensions by calling binaries on you The built-in **[Scaffold Reporter](./crates/stellar-scaffold-reporter/)** extension is included in every new project. It logs compile times, WASM sizes, deploy durations, and total build cycle time directly to your console. -Register extensions in `environments.toml`: +Register extensions in `scaffold.yml`: -```toml -[development] -extensions = ["reporter"] - -# Optional per-extension config: -[development.ext.reporter] -warn_size_kb = 128 +```yaml +extensions: + reporter: + # Optional per-extension config + warn_size_kb: 128 ``` See the [Extensions Guide](https://scaffoldstellar.com/docs/extensions) to learn how the hook system works and how to build your own extension. @@ -207,7 +203,7 @@ After installation, you can interact with the contract using `stellar-cli`: stellar contract invoke --id my-contract-instance -- --help ``` -> You can deploy to testnet or mainnet depending on your `.env` and `environments.toml`. +> You can deploy to testnet or mainnet depending on the network you select in `.env` (`STELLAR_NETWORK`) and `scaffold.yml`. --- ## Concept: What Is the Contract Registry? @@ -239,7 +235,7 @@ Your repo contains the following key folders: ## Documentation * [CLI Commands](https://scaffoldstellar.com/docs/cli) -* [Environment Setup](https://scaffoldstellar.com/docs/environments) +* [Configuration](https://scaffoldstellar.com/docs/environments) * [Registry Guide](https://scaffoldstellar.com/docs/registry) --- diff --git a/crates/stellar-scaffold-cli/README.md b/crates/stellar-scaffold-cli/README.md index 7602935a..7f8fe338 100644 --- a/crates/stellar-scaffold-cli/README.md +++ b/crates/stellar-scaffold-cli/README.md @@ -4,7 +4,7 @@ CLI toolkit for Stellar smart contract development, providing project scaffoldin Stellar Scaffold CLI comes with four main commands: -* `stellar scaffold init` - Creates a new Stellar smart contract project with best practices and configurations in place, including an `environments.toml` file for managing network settings, accounts, and contracts across different environments. +* `stellar scaffold init` - Creates a new Stellar smart contract project with best practices and configurations in place, including a `scaffold.yml` file for managing networks, accounts, and contracts. * `stellar scaffold upgrade` - Transforms an existing Soroban workspace into a full scaffold project by adding frontend components, environment configurations, and project structure. Preserves existing contracts while adding the complete development toolkit. @@ -12,9 +12,9 @@ Stellar Scaffold CLI comes with four main commands: * Build smart contracts with metadata and handle dependencies * Generate TypeScript client packages for frontend integration - The build process respects environment configurations from `environments.toml` and handles contract deployment states based on the current environment (controlled via `STELLAR_SCAFFOLD_ENV`). + The build process follows the configuration in `scaffold.yml` and handles contract deployment states for the selected network (chosen with `--network` or `STELLAR_NETWORK`). -* `stellar scaffold watch` - Development mode that monitors contract source files and `environments.toml` for changes, automatically rebuilding as needed. Defaults to using the `development` environment. +* `stellar scaffold watch` - Development mode that monitors contract source files and `scaffold.yml` for changes, automatically rebuilding as needed. Uses the same network selection as `build`, falling back to `local`. ## Getting Started @@ -52,7 +52,7 @@ stellar scaffold upgrade This will: - Add the frontend application and development tools -- Generate `environments.toml` with your existing contracts +- Generate `environments.toml` (the legacy config format) with your existing contracts - Set up environment files and configurations - Preserve all your existing contract code and structure @@ -66,40 +66,49 @@ cp .env.example .env stellar scaffold watch --build-clients ``` -## Environment Configuration - -Projects use `environments.toml` to define network settings, accounts, and contract configurations for different environments. Example: -```toml -[development] -network = { - name = "local", - run_locally = true -} -accounts = ["account1", "account2"] - -[staging] -network = { - name = "testnet" -} - -[production] -network = { - name = "mainnet" -} +## Configuration + +Projects use `scaffold.yml` to define networks, accounts, and the contracts to deploy or connect to on each network. Example: +```yaml +version: 2 + +networks: + local: + accounts: [me] + testnet: + accounts: [testnet-user] + allow-deploy: true + +contracts: + guess-the-number: + type: workspace + source: guess-the-number + args: + admin: ${account.me} + after-deploy: [reset] + networks: + local: + testnet: + args: + admin: ${account.testnet-user} ``` +Validate it with `stellar scaffold config check`, and print the resolved config for one network with `stellar scaffold config show --network `. See the [Configuration Guide](https://scaffoldstellar.com/docs/environments) for every option. + +Projects created before version 2 keep this configuration in `environments.toml`, selected with `STELLAR_SCAFFOLD_ENV`. `build` and `watch` still read it. + ## Build Process Details `stellar scaffold build` and `stellar scaffold watch` manage: -1. Smart contract compilation and deployment based on environment +1. Smart contract compilation and deployment to the selected network 2. TypeScript client package generation for frontend integration 3. Network and account management (create/fund accounts in development) -4. Contract initialization via constructor args and post-deploy scripts +4. Contract initialization via constructor `args` and `after-deploy` calls The build process ensures: - Correct dependency resolution and build order -- Environment-specific contract deployments +- Network-specific contract deployments - TypeScript client generation for frontend integration - Contract state verification and updates @@ -120,14 +129,12 @@ repository = "https://github.com/your-org/your-project" ## Environment Variables -- `STELLAR_SCAFFOLD_ENV`: Sets current environment (development/staging/production) -- `STELLAR_ACCOUNT`: Default account for transactions -- `STELLAR_RPC_URL`: RPC endpoint URL -- `STELLAR_NETWORK_PASSPHRASE`: Network passphrase +- `STELLAR_NETWORK`: Network from `scaffold.yml` to build for, same as `--network` +- `STELLAR_SCAFFOLD_ENV`: Environment to build for in projects still on `environments.toml` (development/testing/staging/production). Ignored with a version 2 `scaffold.yml` ## For More Information See the full documentation: - [CLI Commands Guide](https://scaffoldstellar.com/docs/cli) -- [Environment Configuration](https://scaffoldstellar.com/docs/environments) +- [Configuration](https://scaffoldstellar.com/docs/environments) - [Deployment Guide](https://scaffoldstellar.com/docs/deploy) \ No newline at end of file diff --git a/crates/stellar-scaffold-ext-types/README.md b/crates/stellar-scaffold-ext-types/README.md index 72db52be..43417333 100644 --- a/crates/stellar-scaffold-ext-types/README.md +++ b/crates/stellar-scaffold-ext-types/README.md @@ -72,7 +72,7 @@ ProjectContext ── pre-dev, post-dev (aggregates all contracts) #### `config` field -Every context type carries the extension's own configuration from `environments.toml`. Where it lives depends on the hook: +Every context type carries the extension's own configuration from the `extensions:` section of `scaffold.yml` (or from `environments.toml` in projects that still use it). Where it lives depends on the hook: | Hook group | Access path | |---|---| @@ -81,7 +81,7 @@ Every context type carries the extension's own configuration from `environments. | pre/post-codegen | `ctx.deploy.compile.config` | | pre/post-dev | `ctx.config` | -The field is typed `Option`. When no config is provided it is `None` and is absent from the JSON entirely (`#[serde(skip_serializing_if = "Option::is_none")]`). Scaffold always injects the real value from `environments.toml` when config is present. +The field is typed `Option`. When no config is provided it is `None` and is absent from the JSON entirely (`#[serde(skip_serializing_if = "Option::is_none")]`). Scaffold always injects the real value from the project's config when it is present. --- diff --git a/crates/stellar-scaffold-reporter/README.md b/crates/stellar-scaffold-reporter/README.md index 516be5ab..1b079c11 100644 --- a/crates/stellar-scaffold-reporter/README.md +++ b/crates/stellar-scaffold-reporter/README.md @@ -22,11 +22,11 @@ To add it to an existing project, install the binary with [Cargo](https://doc.ru cargo install stellar-scaffold-reporter ``` -Then register it in your `environments.toml`: +Then register it in your `scaffold.yml`: -```toml -[development] -extensions = ["reporter"] +```yaml +extensions: + reporter: ``` ## 🏃 Running diff --git a/docs/site/docs/cli.md b/docs/site/docs/cli.md index c293d284..8b0e8eb5 100644 --- a/docs/site/docs/cli.md +++ b/docs/site/docs/cli.md @@ -30,7 +30,7 @@ The init command creates: Official templates come from the [Stellar Scaffold templates repo](https://github.com/stellar-scaffold/ui). -With `--no-template` (or `--template none`), the frontend layer is omitted entirely: no `app/`, no JS workspaces, no dependency install. Every contract in `environments.toml` is written with `client = false`, so `stellar scaffold build` and `watch` skip client package generation. Flip a contract back to `client = true` if you later need TypeScript clients. +With `--no-template` (or `--template none`), the frontend layer is omitted entirely: no `app/`, no `app-lib/`, no JS workspaces, no dependency install. Use plain `stellar scaffold build` to compile the contracts. Adding `--build-clients` still deploys and generates a client for every contract listed in [`scaffold.yml`](./environments.md), even though there is no app to consume them. ## Generate Command @@ -52,7 +52,7 @@ Options: The generate command downloads the example, caches it locally, writes it to `contracts//`, and merges the dependencies it needs into your workspace `Cargo.toml`. Each example set is pinned to a supported release rather than tracking upstream `main`, so the version you get is the one this CLI release was tested against. -If the contract takes constructor arguments, add them to `environments.toml` yourself — `generate` does not write them for you. +`generate` does not add the contract to `scaffold.yml`. Add an entry under `contracts:` yourself, with `type: workspace`, the crate's package name as `source`, its constructor `args`, and the networks it belongs on. See [Configuration](./environments.md#contracts). ## Upgrade Command @@ -70,7 +70,7 @@ The upgrade command: - Validates the existing workspace (requires `Cargo.toml` and `contracts/` directory) - Downloads and integrates the frontend template -- Generates `environments.toml` with discovered contracts +- Generates `environments.toml` with discovered contracts. This is the legacy config format; see [Configuration](./environments.md#legacy-environmentstoml) to move it to `scaffold.yml` version 2 - Analyzes contracts for constructor arguments and prompts for configuration - Preserves all existing contract code and project structure - Adds development tools and configurations @@ -91,10 +91,13 @@ stellar scaffold build [options] Options: -- `--build-clients`: Generate TypeScript client packages for contracts +- `--build-clients`: Deploy contracts and generate TypeScript client packages for them, as configured in [`scaffold.yml`](./environments.md) +- `--network `: Network from `scaffold.yml` to build for. Defaults to `STELLAR_NETWORK`, then the network set with `stellar network use`, then `local` - `--list` or `--ls`: List package names in order of build - [Standard Soroban contract build options also supported] +`build` validates `scaffold.yml` before compiling and stops if it has errors. Contracts that do not list the selected network are skipped: they are not deployed and not exported from the generated clients `index.ts`. Clients removed from `scaffold.yml` are dropped from `index.ts` on the next build. + ## Dev Command Start development mode with hot reloading: @@ -106,7 +109,9 @@ stellar scaffold watch [options] Options: - `--build-clients`: Generate TypeScript client packages while watching -- All options from the build command are also supported +- All options from the build command are also supported, including `--network` + +`watch` also rebuilds when `scaffold.yml` changes. ## Clean Command @@ -126,7 +131,7 @@ Clean removes: - Everything generated into your clients directory, while leaving checked-in files such as `.gitkeep` alone - The contract and identity aliases the CLI created for the local and test networks -Reach for it when you want a genuinely fresh deployment. Because Stellar Scaffold only redeploys a contract when the contract itself changes, editing something else — an `after_deploy` script, for instance — will not produce a new instance on its own. Clearing the aliases makes the next `stellar scaffold watch` deploy from scratch. +Reach for it when you want a genuinely fresh deployment. Because Stellar Scaffold only redeploys a contract when the contract itself changes, editing something else — constructor `args` or `after-deploy` in `scaffold.yml`, for instance — will not produce a new instance on its own. Clearing the aliases makes the next `stellar scaffold watch` deploy from scratch. ## Doctor Command @@ -138,7 +143,7 @@ stellar scaffold doctor [options] Options: -- `--env `: Scaffold environment to diagnose (defaults to `development`, or `STELLAR_SCAFFOLD_ENV`) +- `--env `: Environment to diagnose in projects still on `environments.toml` (defaults to `development`, or `STELLAR_SCAFFOLD_ENV`). Ignored for version 2 projects - `--manifest-path `: Path to `Cargo.toml` (defaults to the current directory) - `--json`: Emit findings as JSON instead of a report - `--strict`: Treat warnings as failures @@ -165,14 +170,14 @@ Findings that have a single-command remedy print it on a `fix:` line beneath. **Project** — needs a Cargo workspace: -- `scaffold.yml` exists and uses a schema version this CLI supports -- The directories its `config:` section points at exist +- `scaffold.yml` exists and uses a schema version this CLI supports. For version 2, every problem `stellar scaffold config check` would report is listed under the `scaffold-yml` check +- The `contracts-dir` and `clients-dir` directories exist - The running CLI satisfies the project's `engines.stellar-scaffold` constraint - `.env` exists when there is a `.env.example` to copy from -- `environments.toml` parses, defines the selected environment, has a usable network, and names contracts that exist in the workspace -- Every extension the environment lists can be found and reports a valid manifest +- For projects still on `environments.toml`: it parses, defines the selected environment, has a usable network, and names contracts that exist in the workspace +- For projects still on `environments.toml`: every extension the environment lists can be found and reports a valid manifest -**Network** — only when the selected environment sets `run-locally = true`: +**Network** — only for projects on `environments.toml` whose selected environment sets `run_locally = true`. These checks are skipped for version 2 projects: - The Docker daemon is running, not merely installed - The local network's RPC endpoint reports healthy @@ -190,15 +195,15 @@ $ stellar scaffold doctor ⚠️ package-manager package.json pins npm@11.7.0, found 11.13.0 fix: corepack use npm@11.7.0 Project -✅ scaffold-yml scaffold.yml schema version 1 +✅ scaffold-yml scaffold.yml schema version 2 ✅ contracts-dir contracts ✅ clients-dir app-lib/clients ✅ engine-constraint stellar-scaffold 0.0.27 ⚠️ dot-env .env is missing fix: cp .env.example .env Network -ℹ️ docker-daemon network is not run locally -ℹ️ localnet network is not run locally +ℹ️ docker-daemon no environment configured +ℹ️ localnet no environment configured 0 problems, 2 warnings ``` @@ -214,6 +219,36 @@ stellar scaffold doctor --json --strict The JSON holds a `checks` array — each entry with `name`, `category`, `severity`, `message`, and `fix` — plus a `summary` object tallying each severity. +## Config Command + +Validate and inspect [`scaffold.yml`](./environments.md): + +```bash +stellar scaffold config check [options] +stellar scaffold config show [options] +``` + +`config check` validates the whole file and reports every problem at once, each with its line, a code, and a suggested fix. It makes no network calls and reads no keys, so it is safe to run in CI. It exits non-zero on errors. + +- `--network `: Also check that this network is declared under `networks:` (defaults to `STELLAR_NETWORK` if set) +- `--strict`: Treat warnings as failures +- `--json`: Print diagnostics as JSON (`[]` when clean) +- `--manifest-path `: Path to `Cargo.toml` (defaults to the current directory) + +`config show` prints the fully resolved config for one network, in YAML: `extends` applied, built-in defaults filled in, per-network contract overrides merged, and `${env.…}`/`${network.…}` substituted. `${account.…}` references are left as written. + +- `--network `: Network to resolve (defaults to `STELLAR_NETWORK`, then `local`) +- `--json`: Print JSON instead of YAML +- `--manifest-path `: Path to `Cargo.toml` (defaults to the current directory) + +```bash +$ stellar scaffold config check +✅ scaffold.yml is valid +$ stellar scaffold config show --network testnet +``` + +Both commands read only version 2 files. + ## Update Environment Command Update environment variables in the .env file: diff --git a/docs/site/docs/environments.md b/docs/site/docs/environments.md index a8ec5208..86dec3b4 100644 --- a/docs/site/docs/environments.md +++ b/docs/site/docs/environments.md @@ -1,223 +1,295 @@ -# Environment Configuration +# Configuration -Stellar Scaffold uses an `environments.toml` file to manage different deployment environments and contract configurations. +Every Stellar Scaffold project is configured by one file at its root: `scaffold.yml`. It says where your contracts and generated clients live, which Stellar networks you build for, which accounts exist on each, and which contracts to deploy or connect to on each network. -## Two configuration files +```yaml +version: 2 + +project: + contracts-dir: contracts + clients-dir: app-lib/clients + +networks: + local: + accounts: [me] + testnet: + accounts: [testnet-user] + allow-deploy: true + +contracts: + guess-the-number: + type: workspace + source: guess-the-number + args: + admin: ${account.me} + after-deploy: [reset] + networks: + local: + testnet: + args: + admin: ${account.testnet-user} +``` -A scaffolded project has two config files at its root, and they answer different questions: +Machine-local settings and secrets stay out of this file. They go in `.env`, which is gitignored and also read by your frontend. -| File | Answers | -| --- | --- | -| `scaffold.yml` | _Where_ things live — which directories the CLI reads contracts from and writes clients to | -| `environments.toml` | _What_ to deploy — networks, accounts, and contracts, per environment | +:::note Projects created before `scaffold.yml` version 2 use `environments.toml` instead. `build` and `watch` still read it as long as `scaffold.yml` says `version: 1`. See [Legacy: `environments.toml`](#legacy-environmentstoml) below. ::: -Most of this page is about `environments.toml`. `scaffold.yml` is short and you will rarely change it: +## Choosing a network -```yaml -version: 1 +`build` and `watch` work against one network at a time. They pick it in this order: -config: - contracts_dir: contracts - clients_dir: app-lib/clients -``` +1. `--network `, or the `STELLAR_NETWORK` environment variable +2. The default set with `stellar network use ` +3. `local` -- `version` is the schema version. It is required, and version `1` is the only value this CLI accepts. -- `contracts_dir` is where your Rust contract crates live. Default: `contracts`. -- `clients_dir` is where your generated contract clients are written: one package per contract in `clients_dir//`, plus a `clients_dir/index.ts` your app imports as `@stellar-scaffold/app-lib/clients`. Default: `app-lib/clients`. Everything here is regenerated on each build, so changes you make by hand will be overwritten. +The name must be declared under `networks:`. New projects set `STELLAR_NETWORK=local` in `.env`; switch it to `testnet` to build against testnet. Keep the `PUBLIC_STELLAR_*` values in `.env` pointed at the same network, since those are what your frontend connects to. -Both keys are optional and fall back to the defaults above, so a `scaffold.yml` containing only `version: 1` is valid. +`STELLAR_SCAFFOLD_ENV` is ignored with a version 2 `scaffold.yml`. The CLI warns if it is set. -## Configuration File Structure +## `version` -```toml -[development] -network = { - name = "local", # use local network - run_locally = true # start up the local docker container -} -accounts = ["account1", "account2"] # Account aliases to create - -[staging] -network = { - name = "testnet", # Use Stellar testnet -} - -[production] -network = { - name = "mainnet", # Use Stellar mainnet -} -``` +Required, and must be `2`. The CLI checks it before anything else, so a file written for a different schema version gets one clear error instead of a list of unknown keys. -## Network Configuration +## `project` -Each environment can specify network settings: +Where things live. Both keys are optional. -```toml -network = { - name = "", # Optional: Use predefined network (mainnet/testnet/local) - rpc_url = "", # Optional: Custom RPC endpoint - network_passphrase = "", # Optional: Network passphrase - rpc_headers = [["key", "value"]], # Optional: Custom RPC headers - run_locally = false # Optional: Whether to run local network (default: false) -} +| Key | Default | Meaning | +| --- | --- | --- | +| `contracts-dir` | `contracts` | Where your Rust contract crates live | +| `clients-dir` | `app-lib/clients` | Where generated clients are written: one package per contract in `//`, plus `/index.ts`, which your app imports as `@stellar-scaffold/app-lib/clients` | + +Everything in `clients-dir` is regenerated on each build, so hand edits are overwritten. To customize a client, import it into your own code instead. + +## `networks` + +Each key is a network name you choose. Names may contain letters, digits, `-` and `_`, and must start with a letter or digit. + +```yaml +networks: + local: + accounts: [me, alice] + default-account: me + + testnet: + accounts: [testnet-user] + allow-deploy: true + + # A second testnet setup with its own accounts, inheriting testnet's RPC + preview: + extends: testnet + accounts: [preview-user] + + mainnet: + rpc-url: https://your-rpc-provider.example + rpc-headers: + Authorization: "Bearer ${env.MAINNET_RPC_TOKEN}" + accounts: [mainnet-user] ``` -## Account Configuration +A network with no settings can be written as a bare `testnet:`, which is the same as `testnet: {}`. -Configure accounts for contract deployment and testing: +| Key | Default | Meaning | +| --- | --- | --- | +| `rpc-url` | stellar-cli's URL for `local`, `testnet` and `futurenet` | RPC endpoint. Required for `mainnet` and for any custom name, unless inherited through `extends` | +| `network-passphrase` | stellar-cli's passphrase for the built-in names | Required for custom names, unless inherited | +| `rpc-headers` | none | Map of HTTP headers sent with every RPC request | +| `accounts` | none | Account aliases Scaffold creates on this network, funding them where it can (Friendbot or the local container) | +| `default-account` | first entry in `accounts` | Which account signs deploys when a contract sets no `signer`. Must be listed in `accounts` | +| `start-container` | `true` only for the local (standalone) passphrase | Whether `build` starts a `stellar/quickstart` container. Set `false` if a local chain is already running at `rpc-url` | +| `allow-deploy` | `false` for the testnet, futurenet and mainnet passphrases; `true` otherwise | Whether Scaffold may deploy `workspace` contracts here | +| `extends` | none | Another declared network to inherit settings from | -```toml -accounts = [ - "account1", # Simple account alias - { name = "admin", default = true } # Account with additional settings -] +`allow-deploy` and `start-container` defaults come from the network's passphrase, not its name. A custom network pointed at testnet is treated like testnet, and a custom network with the standalone passphrase gets a local container. + +### `extends` + +A network that `extends` another starts from the parent's resolved settings and applies its own on top. Scalars override, maps (such as `rpc-headers`) merge, and lists (such as `accounts`) replace rather than append. An explicit `allow-deploy` is inherited; an unset one is recomputed from the child's own passphrase. Chains may be at most three hops deep and may not loop. + +Use `extends` when you need a second configuration on the same chain, for example a `preview` deployment on testnet with its own accounts. + +### Interpolation in network settings + +`rpc-url`, `network-passphrase` and `rpc-headers` values may use `${env.NAME}` to read an environment variable at build time, so secrets such as RPC tokens stay in `.env`. See [Interpolation](#interpolation). + +## `contracts` + +Each key is the client name: the name of the generated package in `//` and, in camelCase, the name exported from `index.ts` (`guess-the-number` becomes `guessTheNumber`). Client names are independent of crate names. + +```yaml +contracts: + my-token: + type: workspace + source: fungible-token # the crate's package name + signer: admin + args: + owner: ${account.admin} + initial_supply: 1000000000000000000000000 + after-deploy: [unpause] + networks: + local: + testnet: + args: + owner: ${account.testnet-admin} + signer: testnet-admin + mainnet: + type: contract + source: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC ``` -## Contract Configuration +Keys directly under a contract are defaults for every network it lists. Entries under its `networks:` override them, using the same merge rules as `extends`. A per-network entry that changes `type` must also set `source`, and the other way round. -Configure smart contracts for each environment: +**A contract only exists on the networks it lists.** Building any other network skips it: it is not built, not deployed, and not exported from `index.ts`. A contract with no `networks:` is never built, and `config check` warns about it. -```toml -[development.contracts.my_contract] -client = true # Generate TypeScript client (default: true) -constructor_args = """ # Initialization script if needed - --arg1 param1 --arg2 param2 -""" -after_deploy = """ # contract setup invocation logic for after initial deployment - STELLAR_ACCOUNT=admin fund --to admin --amount 100 -""" - -[production.contracts.my_contract] -id = "C..." # Contract ID for production/staging -client = true # Generate TypeScript client +| Key | Meaning | +| --- | --- | +| `type` | What `source` refers to. Required. See [Contract types](#contract-types) | +| `source` | The crate, address, asset or Wasm, in the format `type` expects. Required | +| `args` | Constructor arguments, as a map from `__constructor` parameter name to value | +| `signer` | Account that deploys the contract and runs `after-deploy`. Defaults to the network's `default-account` | +| `after-deploy` | List of contract methods to call, with no arguments, right after deploying | +| `after-deploy-script` | Path to an executable to run after deploying (not yet supported by `build`) | +| `from-network` | Copy the Wasm of a contract deployed on another network and deploy it here (not yet supported by `build`) | +| `networks` | Per-network entries. Only valid at the top level of a contract | + +`signer`, `args`, `after-deploy` and `after-deploy-script` only apply when Scaffold deploys the contract, which means `type: workspace` (or `from-network`). Setting them on a contract that only references an existing deployment is an error. + +### Contract types + +| `type` | `source` format | What Scaffold does | +| --- | --- | --- | +| `workspace` | A crate's package name in `contracts-dir` | Builds, deploys and generates a client | +| `contract` | A deployed contract address (`C…`) | Generates a client bound to that address | +| `registry` | `[namespace/]name@version` from the Stellar Registry | Generates a client bound to the registered deployment | +| `asset` | `CODE:ISSUER`, or `native` | Generates a client for the asset's Stellar Asset Contract | +| `wasm-file` | Path to a local `.wasm` file | Generates a types-only client, with no contract ID | +| `wasm-hash` | A 64-character hex Wasm hash installed on the network | Generates a types-only client | +| `wasm-registry` | `[namespace/]name@version` of a Wasm in the Stellar Registry | Generates a types-only client | + +:::caution `build` and `watch` currently support only `workspace` and `contract`. The other types pass `config check` but fail at build time with `uses type: , which build does not support yet`. The same applies to `from-network` and `after-deploy-script`. ::: + +`registry` and `wasm-registry` sources without an `@version` are accepted with a warning, because the thing they point at can change under you. + +A `workspace` contract can only be deployed to a network with deploys allowed. To use the same contract on testnet or mainnet, either set `allow-deploy: true` on that network, or override the entry to point at a deployment you made separately: + +```yaml +networks: + mainnet: + type: contract + source: C... # the deployed contract's ID ``` -### Configuration Options +`allow-deploy: true` on a network with the mainnet passphrase is allowed, with a warning. -#### `client` (boolean, default: true) +### Constructor arguments -- Controls whether a TypeScript client package is generated for this contract -- Set to `false` to skip client generation for utility contracts +`args` maps each `__constructor` parameter, by name, to a value. Values are checked against the contract's spec by stellar-cli when it deploys, so write them as you would on the command line: strings, numbers and booleans as YAML scalars, vectors as lists, and structs or maps as maps. -```toml -[development.contracts.my_contract] -client = false # Skip TypeScript client generation +```yaml +args: + name: ExampleToken + decimals: 7 + admin: ${account.me} + initial_supply: 1000000000000000000000000 # 128-bit values keep full precision + metadata: null # omitted, so an Option parameter becomes None ``` -#### `id` (string, optional) +Integers wider than 64 bits are read exactly, so `i128`, `u128` and 256-bit values do not need quoting. `null` leaves the argument out. No shell is involved: values are passed straight to stellar-cli, so `$(…)` is just text. -- Specifies a fixed contract ID for the contract -- Required in production/staging environments -- Must be a valid Stellar contract ID +### `after-deploy` -```toml -[production.contracts.my_contract] -id = "C..." # Use specific contract ID +A list of method names called, in order, with no arguments, signed by the contract's `signer`. They run only when the contract was actually deployed or upgraded on this build. A build that finds the Wasm unchanged skips them. + +```yaml +after-deploy: [reset, unpause] ``` -#### `constructor_args` (string, optional) +### Deploying and redeploying -- Arguments passed to contract constructor during deployment -- Executes as part of the deployment transaction -- Single line of space-separated arguments -- Can use `STELLAR_ACCOUNT=` to specify the deployer account -- Supports command substitution with `$(command)` +When a `workspace` contract's Wasm changes, the next build upgrades the existing deployment if the contract supports it, or redeploys it otherwise. Changing only `args` or `after-deploy` does not trigger a redeploy. Run `stellar scaffold clean` to start fresh. -```toml -[development.contracts.my_contract] -constructor_args = "--arg1 1000 --account $(stellar keys address admin)" # Basic args +If one contract fails to deploy, the build carries on with the rest, exports what it can, and then exits with an error. -# With specific deployer account -constructor_args = "STELLAR_ACCOUNT=admin --arg1 value1 --arg2 value2" +## `extensions` -# With command substitution -constructor_args = "--account1 $(stellar keys address user1) --account2 $(stellar keys address user2)" +[Extensions](./extensions.md) run in the order listed, on every network. Each key is an extension name; its value is passed to the extension as its `config`, which is left out when the value is empty. Keys inside the value are passed exactly as written, so use whatever spelling the extension expects. + +```yaml +extensions: + reporter: + warn_size_kb: 128 + my-ext: ``` -#### `after_deploy` (string, optional) +## Interpolation -- Initialization script to run after contract deployment -- Only runs in development/testing environments -- Supports multiple commands on separate lines -- Can use `STELLAR_ACCOUNT=` to specify the source account -- Supports command substitution with `$(command)` +String values can reference other values with `${namespace.name}`. -```toml -[development.contracts.my_contract] -after_deploy = """ -# Basic initialization -initialize --param1 value1 --param2 value2 - -# Use specific account -STELLAR_ACCOUNT=admin set_admin --admin "new_admin" - -# Command substitution -set_value --value "$(stellar keys address admin)" - -# Multiple operations -create_pool --name "Pool A" -add_liquidity --amount 1000 -set_fee_rate --rate 0.003 -""" +| Reference | Value | Allowed in | +| --- | --- | --- | +| `${env.NAME}` | Environment variable `NAME` at build time; an error if unset | Network settings and `args` | +| `${env.NAME:-fallback}` | `NAME`, or `fallback` when unset | Network settings and `args` | +| `${account.NAME}` | Address of account `NAME`, which must be listed in the network's `accounts` | `args` | +| `${network.name}`, `${network.rpc-url}`, `${network.passphrase}` | The selected network's resolved values | `args` | + +Write `$${` for a literal `${`. The namespaces `contract`, `wasm-hash` and `registry` are reserved for future use and currently rejected. + +## Validating your config + +```bash +stellar scaffold config check # validate the whole file +stellar scaffold config check --network testnet # also check that testnet is declared +stellar scaffold config show --network testnet # print the fully resolved config for testnet ``` -### Example Configurations +`config check` reports every problem at once, with the line, a short code such as `unknown-account` or `deploy-not-allowed`, and a suggested fix. It makes no network calls, so it is safe to run in CI. `--strict` also fails on warnings, and `--json` prints diagnostics as JSON. -```toml -# Token contract with constructor args -[development.contracts.token] -client = true -constructor_args = "--name Token --symbol TKN --decimals 8" +`config show` applies `extends`, fills in built-in defaults, merges per-network contract overrides and substitutes `${env.…}` and `${network.…}`, so you can see exactly what `build` will use. `${account.…}` references stay as written, since the accounts may not exist yet. -# Contract deployed by admin with dynamic arguments -[development.contracts.marketplace] -client = true -constructor_args = "STELLAR_ACCOUNT=admin --treasury-account $(stellar keys address treasury)" +`build`, `watch` and `stellar scaffold doctor` run the same checks. -# Contract with both constructor args and after_deploy script -[development.contracts.game] -client = true -constructor_args = "STELLAR_ACCOUNT=admin --name GameV1 --start 1000" -after_deploy = """ - # Additional setup after deployment - add_player --address "$(stellar keys address player1)" - set_difficulty --difficulty 3 -""" - -# Production environment with fixed contract ID -[production.contracts.token] -client = true -id = "CC5YYARE2TSLA..." # Must be valid contract ID +## Environment variables -# Utility contract without client generation -[development.contracts.utils] -client = false +| Variable | Effect | +| --- | --- | +| `STELLAR_NETWORK` | Network to build for, same as `--network` | +| `STELLAR_SCAFFOLD_ENV` | Ignored, with a warning. Only read by projects still on `environments.toml` | +| Anything referenced by `${env.…}` | Read at build time | -# Complex initialization with multiple accounts -[development.contracts.marketplace] -client = true -after_deploy = """ - # Set up admin - STELLAR_ACCOUNT=admin set_admin_account --account "$(stellar keys address admin)" +## Legacy: `environments.toml` - # Configure fees - STELLAR_ACCOUNT=admin set_fee_rate --rate 250 +Projects whose `scaffold.yml` says `version: 1` keep their networks, accounts and contracts in `environments.toml`, keyed by environment (`development`, `testing`, `staging`, `production`) and selected with `STELLAR_SCAFFOLD_ENV`. In those projects `scaffold.yml` only holds directories: - # Add initial listing - STELLAR_ACCOUNT=seller create_listing --name "Item A" --price 1000 -""" +```yaml +version: 1 + +config: + contracts_dir: contracts + clients_dir: app-lib/clients ``` -## Environment Variables +```toml +[development] +network = { name = "local", run_locally = true } +accounts = ["me"] + +[development.contracts.guess_the_number] +client = true +constructor_args = "--admin me" +after_deploy = "reset" + +[production.contracts.guess_the_number] +id = "C..." +``` -- `STELLAR_SCAFFOLD_ENV`: Set the current environment (development/testing/staging/production) -- `STELLAR_ACCOUNT`: Default account for transactions (set automatically) -- `STELLAR_RPC_URL`: RPC endpoint URL (set from network config) -- `STELLAR_NETWORK_PASSPHRASE`: Network passphrase (set from network config) +`build` and `watch` still read this format, and `--network` is ignored for it. To move to version 2: -## Usage +- Replace `config:` with `project:`, renaming `contracts_dir`/`clients_dir` to `contracts-dir`/`clients-dir`, and set `version: 2`. +- Turn each environment into an entry under `networks:`. `run_locally` becomes `start-container`; an explicit `default = true` account becomes `default-account`. +- Turn each contract into an entry under `contracts:` with `type: workspace` and its crate name as `source`, or `type: contract` and the `id` as `source`, and list the networks it belongs on. +- Rewrite `constructor_args` as an `args` map, `$(stellar keys address x)` as `${account.x}`, and a `STELLAR_ACCOUNT=` prefix as `signer`. +- Rewrite `after_deploy` as a list of method names. Calls that take arguments are not supported by `after-deploy` yet. +- Move `extensions` into the top-level `extensions:` map. +- Replace `STELLAR_SCAFFOLD_ENV` in `.env` with `STELLAR_NETWORK`, then delete `environments.toml`. -1. Create `environments.toml` in your project root -2. Configure environments, networks, and contracts -3. Set `STELLAR_SCAFFOLD_ENV` to choose environment -4. Use `stellar scaffold build` or `stellar scaffold watch` to deploy and generate clients +Run `stellar scaffold config check` when you are done. diff --git a/docs/site/docs/extensions.md b/docs/site/docs/extensions.md index b8679aaf..6e63b74d 100644 --- a/docs/site/docs/extensions.md +++ b/docs/site/docs/extensions.md @@ -32,27 +32,28 @@ You only need to handle the hooks relevant to your extension. Hooks you do not l ## Registering an extension -Add your extension to `environments.toml` under the environments where it should run: +List your extension under `extensions:` in [`scaffold.yml`](./environments.md). Extensions run in the order listed, on every network: -```toml -[development] -extensions = ["reporter"] - -[staging] -extensions = ["reporter", "audit-tool"] +```yaml +extensions: + reporter: + audit-tool: ``` ### Per-extension configuration -You can pass arbitrary configuration to an extension via `[.ext.]`: +Anything you write under an extension's name is passed to it as configuration: -```toml -[development.ext.reporter] -warn_size_kb = 128 -log_file = ".scaffold/reports/dev.log" +```yaml +extensions: + reporter: + warn_size_kb: 128 + log_file: .scaffold/reports/dev.log ``` -Scaffold serializes this table and injects it as the `config` field in every hook invocation for that extension. If no config section exists, `config` is absent from the JSON. +Scaffold converts this value to JSON and injects it as the `config` field in every hook invocation for that extension. Keys are passed exactly as written. If the extension has no value (a bare `reporter:`), `config` is absent from the JSON. + +:::note Projects still on `environments.toml` register extensions per environment instead, with `extensions = ["reporter"]` under `[development]` and configuration in a `[development.ext.reporter]` table. ::: --- @@ -82,7 +83,7 @@ Only list hooks your extension actually handles. Listing a hook you do not handl ## The stdin JSON -At each hook invocation, Scaffold writes a flat JSON object to the extension's stdin. The object always includes `config` (your extension's config from `environments.toml`, or `null` if none was provided) plus context fields that depend on which hook is firing. +At each hook invocation, Scaffold writes a flat JSON object to the extension's stdin. The object always includes `config` (your extension's config from `scaffold.yml`, or `null` if none was provided) plus context fields that depend on which hook is firing. ### Field reference @@ -108,9 +109,9 @@ At each hook invocation, Scaffold writes a flat JSON object to the extension's s | Field | Type | Description | | --- | --- | --- | -| `config` | object \| null | Your extension's config table from `environments.toml`, or `null` | +| `config` | object \| null | Your extension's config from `scaffold.yml`, or `null` | | `project_root` | string (path) | Absolute path to the Cargo workspace root | -| `env` | string | Active environment: `"development"`, `"testing"`, `"staging"`, or `"production"` | +| `env` | string | The selected network's name, such as `"local"` or `"testnet"`. In projects still on `environments.toml`, the active environment: `"development"`, `"testing"`, `"staging"`, or `"production"` | | `wasm_out_dir` | string (path) | Directory where compiled WASM files are written | | `source_dirs` | string[] | Contract source directories in topological build order | | `wasm_paths` | object | Map of `contract_name → wasm_path`; empty at `pre-compile` | @@ -211,11 +212,11 @@ cargo install --path . Or copy the compiled binary somewhere on your `PATH`. -### Step 5: Register it in `environments.toml` +### Step 5: Register it in `scaffold.yml` -```toml -[development] -extensions = ["my-extension"] +```yaml +extensions: + my-extension: ``` Run `stellar scaffold build` or `stellar scaffold watch` and your extension will be called at each registered hook. @@ -322,11 +323,11 @@ You can install it standalone with: cargo install stellar-scaffold-reporter ``` -And register it in `environments.toml`: +And register it in `scaffold.yml`: -```toml -[development] -extensions = ["reporter"] +```yaml +extensions: + reporter: ``` Browse the [source code](https://github.com/stellar-scaffold/cli/tree/main/crates/stellar-scaffold-reporter) and its [README](https://github.com/stellar-scaffold/cli/tree/main/crates/stellar-scaffold-reporter/README.md) to see a complete, real-world extension that handles all eight hooks. diff --git a/docs/site/docs/quick-start.mdx b/docs/site/docs/quick-start.mdx index 26522fbb..b4de3724 100644 --- a/docs/site/docs/quick-start.mdx +++ b/docs/site/docs/quick-start.mdx @@ -91,8 +91,7 @@ my-project/ │ ├── wallet.ts # Wallet connection │ └── ... # Network config, storage, formatting helpers ├── e2e/ # End-to-end tests -├── environments.toml # Networks, accounts, and contracts per environment -├── scaffold.yml # CLI configuration +├── scaffold.yml # Networks, accounts, and contracts ├── .env # Local environment variables ├── Cargo.toml # Rust workspace ├── package.json # npm workspace root @@ -110,7 +109,7 @@ The one exception is `app-lib/clients/`, which is generated. Every time your contracts build or redeploy, Stellar Scaffold rewrites `app-lib/clients/` to match them. Treat it as build output: - `app-lib/clients//` — one package per contract: a type-safe interface with every method, argument, and return type taken from the contract itself. -- `app-lib/clients/index.ts` — connects each of those to your current environment's network and deployed address, and exports one ready-to-call object per contract. +- `app-lib/clients/index.ts` — connects each of those to the selected network and its deployed address, and exports one ready-to-call object per contract. That means your components never hold a contract address or an RPC URL. They import a name and call a method: @@ -120,11 +119,11 @@ import { guessTheNumber } from "@stellar-scaffold/app-lib/clients"; const { result } = await guessTheNumber.get_prize(); ``` -Switching environments — local to testnet, say — changes what those objects point at, with no change to your code. +Switching networks — local to testnet, say — changes what those objects point at, with no change to your code. Because the directory is rewritten on every build, edits inside it are lost. To customize how a contract is called, wrap it in your own module under `app/` and import that instead. -See the [CLI Documentation](./cli.md) for detailed command information and the [Environments Guide](./environments.md) for configuration details. +See the [CLI Documentation](./cli.md) for detailed command information and the [Configuration Guide](./environments.md) for configuring networks, accounts, and contracts in `scaffold.yml`. --- @@ -204,6 +203,6 @@ After installation, you can interact with the contract using `stellar-cli`: stellar contract invoke --id my-contract-instance -- --help ``` -> You can deploy to testnet or mainnet depending on your `.env` and `environments.toml`. Names are normalized (underscores → hyphens, lowercase). +> You can deploy to testnet or mainnet depending on the network you select in `.env` (`STELLAR_NETWORK`) and `scaffold.yml`. Names are normalized (underscores → hyphens, lowercase). Happy hacking! diff --git a/docs/site/docs/tutorial/01-getting-started.md b/docs/site/docs/tutorial/01-getting-started.md index f9d82479..1e7647e7 100644 --- a/docs/site/docs/tutorial/01-getting-started.md +++ b/docs/site/docs/tutorial/01-getting-started.md @@ -158,7 +158,6 @@ Open the project in your editor. You will see a generated project structure incl │   └── src │   ├── lib.rs │   └── test.rs -├── environments.toml ├── scaffold.yml ├── app/ ├── app-lib/ @@ -178,25 +177,26 @@ There are a few more files than the ones listed here, but let's highlight some i - `app/`: our frontend. This is our code, and we'll spend most of the tutorial here. - `app-lib/`: utility code that came with the project — connecting a wallet, reading network settings, funding an account, formatting balances. It saves us writing the same plumbing every Stellar app needs, but it isn't special: it's plain source we can read and change, and we can ignore it entirely if we'd rather write our own. We import it as `@stellar-scaffold/app-lib`. - `app-lib/clients/`: the one directory we should _not_ edit. Stellar Scaffold generates our contract clients here and rewrites them every time the contracts build, so anything we change by hand gets overwritten. More on this below. -- `.env`: is where we store environment variables that we'll be used by Stellar Scaffold commands. -- `scaffold.yml`: tells the CLI where things live — which directory holds our contracts, and where to write the generated clients. The defaults are fine for this tutorial. -- `environments.toml`: This is the Stellar Scaffold secret sauce! This file is where we configure: - - our project's various _environments_, ... - - which _networks_ are used by each environment, ... - - all in service of which _contracts_ our project depends on in each of those environments. +- `.env`: is where we store environment variables that will be used by Stellar Scaffold commands and by our frontend. +- `scaffold.yml`: This is the Stellar Scaffold secret sauce! This file is where we configure: + - the _networks_ our project runs on, ... + - the _accounts_ we use on each network, ... + - all in service of which _contracts_ our project depends on, on each of those networks. + + It also tells the CLI where things live — which directory holds our contracts, and where to write the generated clients. So how do all these pieces work together? Here's what Stellar Scaffold handles for you: 1. Our `npm run dev` command runs `stellar scaffold watch --build-clients` -2. Our `.env` file set an environment variable to say we're in our _development_ environment (`STELLAR_SCAFFOLD_ENV=development`) -3. Stellar Scaffold looked to `environments.toml` for the development environment's configuration, which told it to: +2. Our `.env` file set an environment variable to say we're building for the _local_ network (`STELLAR_NETWORK=local`) +3. Stellar Scaffold looked to `scaffold.yml` for the local network's configuration, which told it to: - Start up a local Stellar network - Create an account on the network - Build the contracts - Deploy them to the network - Generate their clients for the frontend -That's a lot of heavy lifting! Normally you'd have to do all this yourself, perhaps in a procedural script, but Stellar Scaffold does it for you. And it's deterministic, meaning you can always reproduce the same results from the same environment configuration. You set configuration values, specifying the desired starting state for your app, and Stellar Scaffold does all the work to get your app into that state. +That's a lot of heavy lifting! Normally you'd have to do all this yourself, perhaps in a procedural script, but Stellar Scaffold does it for you. And it's deterministic, meaning you can always reproduce the same results from the same configuration. You set configuration values, specifying the desired starting state for your app, and Stellar Scaffold does all the work to get your app into that state. We deployed the example contract, but we don't even know what it does. Luckily, we built a tool to help with that! @@ -204,7 +204,7 @@ We deployed the example contract, but we don't even know what it does. Luckily, Go back to your browser and look at the application again. Click the "</> Debugger" button in the header. These are our contract developer tools. They'll let you explore the contracts available to your application, view documentation, and even run methods to help debug them right from your app! -Select the `guess_the_number` contract and you should see its Contract ID from the local network deployment. You'll also see the contract's documentation for methods like: +Select the `guess-the-number` contract and you should see its Contract ID from the local network deployment. You'll also see the contract's documentation for methods like: - `reset`: Update the number. Only callable by admin. - `guess`: Guess a number between 1 and 10 @@ -354,7 +354,7 @@ import { guessTheNumber } from "@stellar-scaffold/app-lib/clients"; // AUTO-GENERATED by `stellar scaffold` — do not edit. import { network } from "@stellar-scaffold/app-lib"; -import { Client as GuessTheNumber } from "guess_the_number"; +import { Client as GuessTheNumber } from "guess-the-number"; const { passphrase: networkPassphrase, rpcUrl, id } = network; @@ -367,7 +367,7 @@ export const guessTheNumber = new GuessTheNumber({ }); ``` -Two generated pieces are at work here. The `guess_the_number` package under `app-lib/clients/guess_the_number/` knows the _shape_ of our contract — every method, argument, and return type, read straight from the Rust we wrote. `app-lib/clients/index.ts` then connects it to the network we're running against and the address it got deployed to. +Two generated pieces are at work here. The `guess-the-number` package under `app-lib/clients/guess-the-number/` knows the _shape_ of our contract — every method, argument, and return type, read straight from the Rust we wrote. `app-lib/clients/index.ts` then connects it to the network we're running against and the address it got deployed to. That's why our component can call `guessTheNumber.guess(...)` without knowing a contract address, an RPC URL, or a network passphrase. When we move from local to testnet later, this file changes and our component doesn't. diff --git a/docs/site/docs/tutorial/02-making-improvements.md b/docs/site/docs/tutorial/02-making-improvements.md index 4ec4915d..c9e2aa3e 100644 --- a/docs/site/docs/tutorial/02-making-improvements.md +++ b/docs/site/docs/tutorial/02-making-improvements.md @@ -17,23 +17,25 @@ By the end of this step, you'll have: ## 🪲 Let's break the app! -To understand the bug in our code, let's trigger it. We'll do this by making a small change in `environments.toml`. On our way to finding the line we need to change, we'll learn more about how `environments.toml` works. +To understand the bug in our code, let's trigger it. We'll do this by making a small change in `scaffold.yml`. On our way to finding the line we need to change, we'll learn more about how `scaffold.yml` works. -Open up `environments.toml` in your editor. Put it side-by-side with the output from `npm run dev`. We'll walk through it bit by bit. +Open up `scaffold.yml` in your editor. Put it side-by-side with the output from `npm run dev`. We'll walk through it bit by bit. ### 1. The Network -At the top, you'll see settings for the development network: +Under `networks:`, you'll see settings for the local network: -````toml -```toml -[development.network] -rpc-url = "http://localhost:8000/rpc" -network-passphrase = "Standalone Network ; February 2017" -run-locally = true -```` +```yaml +networks: + local: + accounts: [me] +``` + +That doesn't look like much! Every Stellar network is identified by a _network passphrase_; it's like the fingerprint of the network and helps keep transactions cryptographically secure between networks. And you connect to any given Stellar network via an _RPC URL_. Because `local` is one of the network names Stellar CLI already knows (along with `testnet`, `futurenet` and `mainnet`), Scaffold fills in both for us. For a network with a name of your own, you'd set them yourself with `rpc-url` and `network-passphrase`. + +Scaffold also notices that `local` uses the local (standalone) passphrase, so it uses _Stellar_ CLI to run a local network container (`stellar container start`) and waits for it to finish startup before moving on. -Every Stellar network is identified by a `network-passphrase`; it's like the fingerprint of the network and helps keep transactions cryptographically secure between networks. And you connect to any given Stellar network via a configurable `rpc-url`. If you look at the rest of `environments.toml`, every environment's network requires these settings. For our development environment, we also want to run the network locally. `run-locally` tells Scaffold CLI to use _Stellar_ CLI to run a local network container (`stellar container start`) and wait for it to finish startup before moving on to parse the rest of the `development` settings. +Which network does `npm run dev` use? Look in `.env`: `STELLAR_NETWORK=local`. You could also pass `--network ` to `stellar scaffold watch`. These settings correspond to the following `npm run dev` output: @@ -45,21 +47,12 @@ These settings correspond to the following `npm run dev` output: ### 2. The Accounts -Next you'll see this: - -```toml -[[development.accounts]] -name = "me" -default = true -``` +That `accounts: [me]` line is a [YAML list](https://yaml.org/spec/1.2.2/#flow-sequences) with one entry. It tells Scaffold CLI to use Stellar CLI to generate a keypair for an account named "me" (`stellar keys generate me`) and fund it. Because it's the first account in the list, it also signs every deploy that doesn't say otherwise. -The double brackets, `[[ ... ]]`, are one way to [make an array in toml](https://toml.io/en/v1.0.0-rc.2#array-of-tables). The snippet above tells Scaffold CLI to use Stellar CLI to generate a keypair for an account named "me" (`stellar keys generate me`) and set this account as the default for all transactions to follow. +If you wanted another named account/keypair to use throughout the rest of `scaffold.yml`, you could add it to the list: -If you wanted to create another named account/keypair to use throughout the rest of `environments.toml`, you could do so by adding another `[[development.accounts]]` block: - -```toml -[[development.accounts]] -name = "alice" +```yaml +accounts: [me, alice] ``` If you look at the `npm run dev` output again, this is the corresponding output: @@ -74,43 +67,46 @@ On subsequent runs, the key will already exist and the account will already be f ### 3. The Contracts (aka "The Contract Clients") -This is what it's all about! You can think of everything in `environments.toml` as existing to configure contract clients. +This is what it's all about! You can think of everything in `scaffold.yml` as existing to configure contract clients. -Here's what that means: your frontend app relies on contracts. Depending on which version of your frontend you are using, those contracts will live on different networks. When you're working in your development environment, you probably want to use the local network (as configured in Stellar Scaffold by default). When you are ready to share an early, staging build of your app with others, you will probably use contracts deployed on Stellar's testnet. When you deploy your production app, you will make calls to mainnet contracts. +Here's what that means: your frontend app relies on contracts. Depending on which version of your frontend you are using, those contracts will live on different networks. When you're developing, you probably want to use the local network (as configured in Stellar Scaffold by default). When you are ready to share an early build of your app with others, you will probably use contracts deployed on Stellar's testnet. When you deploy your production app, you will make calls to mainnet contracts. -Stellar Scaffold encourages you to build separate versions of your frontend for each of these environments. And for each, you specify the contracts you rely on. +Stellar Scaffold encourages you to build separate versions of your frontend for each of these networks. And for each, you specify the contracts you rely on. :::tip But wait. Isn't the behavior of a given contract the same across different networks? 🤔🤔🤔 -If you think about the lifecycle of a contract like our Guess The Number game, you might imagine finalizing the contract, then deploying the exact same contract to your local network, to testnet, and even eventually to mainnet. Why does Stellar Scaffold and `environments.toml` make you specify the contract for each? Why does it rebuild the contract clients for each, as if they might be entirely different? Couldn't we just generate the contract client once, and then change the RPC URL and Network Passphrase that the client gets instantiated with? Same _behavior_, different networks & contracts? +If you think about the lifecycle of a contract like our Guess The Number game, you might imagine finalizing the contract, then deploying the exact same contract to your local network, to testnet, and even eventually to mainnet. Why does Stellar Scaffold make you list the networks for each contract? Why does it rebuild the contract clients for each, as if they might be entirely different? Couldn't we just generate the contract client once, and then change the RPC URL and Network Passphrase that the client gets instantiated with? Same _behavior_, different networks & contracts? -In theory, this sounds reasonable. In practice, contracts rarely have the same exact implementation across different networks. Your local contract will have all the latest changes; it will be like your `main` branch or a nightly build. Messy, fast-paced, experimental. Your staging contract will be like a `beta` release—it will have stuff you haven't yet pushed to your main app. And even more, you could add feature flags to permanently ship different versions of your contract to staging and mainnet. Imagine a contract that adds admin backdoors in staging, but strips them out in production. +In theory, this sounds reasonable. In practice, contracts rarely have the same exact implementation across different networks. Your local contract will have all the latest changes; it will be like your `main` branch or a nightly build. Messy, fast-paced, experimental. Your testnet contract will be like a `beta` release—it will have stuff you haven't yet pushed to your main app. And even more, you could add feature flags to permanently ship different versions of your contract to testnet and mainnet. Imagine a contract that adds admin backdoors on testnet, but strips them out on mainnet. -Stellar Scaffold wants to help you avoid bugs in all these situations. The contract clients are rebuilt for each environment, and they're built _in strict TypeScript_. So if you worked locally on a cool new feature with a smart contract method `my_cool_new_method`, and your frontend makes unguarded calls to this, then your frontend build for staging and production will fail, because those contracts don't implement `my_cool_new_method`. +Stellar Scaffold wants to help you avoid bugs in all these situations. The contract clients are rebuilt for each network, and they're built _in strict TypeScript_. So if you worked locally on a cool new feature with a smart contract method `my_cool_new_method`, and your frontend makes unguarded calls to this, then your frontend build for testnet and mainnet will fail, because those contracts don't implement `my_cool_new_method`. ::: -For staging and production, these must be live, deployed contracts. But in development, you are likely working on your contracts at the same time as your frontend! So the `development.contracts` handling has some allowances, some superpowers, that `staging.contracts` and `production.contracts` lack. Let's see: - -```toml -[development.contracts.guess_the_number] -client = true - -constructor_args = """ ---admin me -""" - -after_deploy = """ -reset -""" +On public networks like mainnet, these will usually be live contracts you deployed and reviewed separately. But locally, you are likely working on your contracts at the same time as your frontend! So Scaffold will build and deploy them for you, with some extra settings for doing so. Let's see: + +```yaml +contracts: + guess-the-number: + type: workspace + source: guess-the-number + args: + admin: ${account.me} + after-deploy: [reset] + networks: + local: + testnet: + args: + admin: ${account.testnet-user} ``` -This is a [Toml table](https://toml.io/en/v1.0.0-rc.2#table). See the TOML spec for other ways you could specify the same information. - Let's walk through this line by line: -- `[development.contracts.guess_the_number]`: this project only has one contract, so we can specify the settings for its contract clients here. You could also have a `[development.contracts]` with a more JSON-like specification for `guess_the_number` (`guess_the_number = { client = true, … }`). -- `guess_the_number`: this name must match the name of the contract specified in its `Cargo.toml` file, but in underscore-case. Compare it to the `name` field in `contracts/guess-the-number/Cargo.toml` and the generated Wasm files (`ls target/wasm32v1-none/release/*.wasm`). +- `guess-the-number:`: the name of the contract _client_. It's what you'll import in your frontend, converted to camelCase: `guessTheNumber`. It can be anything you like. + +- `type: workspace`: says this contract lives in our own `contracts/` directory, so Scaffold should build it, deploy it, and generate a client for it. Other types point at contracts that are already deployed, like `type: contract` with a contract ID. + +- `source: guess-the-number`: which crate to build. This must match the `name` field in `contracts/guess-the-number/Cargo.toml`. The `npm run dev` output that corresponds to this came out right at the top: @@ -118,18 +114,16 @@ Let's walk through this line by line: [0] ℹ️ Watching …/guessing-game-tutorial/contracts/guess-the-number ``` -- `client = true`: this tells Scaffold CLI to generate a contract client for this contract. - - This results in the `npm run dev` output: + Scaffold always generates a client for every contract listed here. This results in the `npm run dev` output: ``` - [0] ℹ️ Binding "guess_the_number" contract - [0] ✅ 'npm run build' succeeded in …/guessing-game-tutorial/target/packages/guess_the_number + [0] ℹ️ Binding "guess-the-number" contract + [0] ✅ 'npm run build' succeeded in …/guessing-game-tutorial/app-lib/clients/guess-the-number ``` The contract _client_ also gets called the "TS (or TypeScript) Bindings" for the contract, because they are generated with the Stellar CLI command `stellar contract bindings typescript`. -- `constructor_args`: the contract has a `constructor`, as we saw in the previous step. This `constructor_args` setting specifies the arguments to use when deploying & initializing the contract. You could deploy the contract yourself with: +- `args`: the contract has a `constructor`, as we saw in the previous step. This `args` setting specifies the arguments to use when deploying & initializing the contract, by parameter name. `${account.me}` means "the address of the `me` account". You could deploy the contract yourself with: ```bash stellar contract deploy \ @@ -139,14 +133,14 @@ Let's walk through this line by line: --admin me ``` - As you can see, the `constructor_args` get passed directly along to this `stellar contract deploy` command. + As you can see, each entry in `args` becomes a `--name value` pair after the `--` of this `stellar contract deploy` command. - `client = true` and the `constructor_args` settings together resulted in this `npm run dev` output: + The `args` settings resulted in this `npm run dev` output: ``` - [0] ℹ️ Installing "guess_the_number" wasm bytecode on-chain... + [0] ℹ️ Installing "guess-the-number" wasm bytecode on-chain... [0] ℹ️ ↳ hash: d801a98511519b2e9d4f2fadffc4215fc81f91426381dbdb328d10252e8298ac - [0] ℹ️ Instantiating "guess_the_number" smart contract + [0] ℹ️ Instantiating "guess-the-number" smart contract [0] ℹ️ ↳ contract_id: CCMMU6UYIPGSBR7ZP4DTQEEOQDHL3PJ52ZD7FJIFG4O46Q3QPVGVHAAV ``` @@ -154,36 +148,36 @@ Let's walk through this line by line: 1. The Wasm gets uploaded to the blockchain, so that many contracts could use it. 2. A contract gets deployed (aka "instantiated", in the current parlance of this output) so that there is an actual smart contract that refers to, or points to, that Wasm. -- `after_deploy`: method calls to make to the contract after it gets deployed. Kind of like the `constructor_args`, but, these are specified using _only_ the part that comes after the `--`. The setting above tells Scaffold CLI to make call the `reset` method, after deploying the contract: +- `after-deploy`: a list of contract methods to call, with no arguments, right after the contract gets deployed. The setting above tells Scaffold CLI to call the `reset` method, after deploying the contract: ```bash - stellar contract deploy \ - --id guess_the_number - --source me + stellar contract invoke \ + --id guess-the-number \ + --source me \ -- \ reset ``` - This `after_deploy` script produces this `npm run dev` output: + This `after-deploy` setting produces this `npm run dev` output: ``` - [0] ℹ️ Running after_deploy script for "guess_the_number" - [0] ℹ️ ↳ Executing: stellar contract invoke --id CCMMU6UYIPGSBR7ZP4DTQEEOQDHL3PJ52ZD7FJIFG4O46Q3QPVGVHAAV --config-dir /Users/chadoh/code/scast/frontend -- reset - [0] ℹ️ ↳ Result: Res("") + [0] ℹ️ ↳ Calling reset on "guess-the-number" + [0] ✅ After deploy calls for "guess-the-number" completed ``` +- `networks`: the networks this contract exists on. A contract only exists on the networks it lists, so if you built for a network that isn't here, Scaffold would skip it and your frontend wouldn't get a `guessTheNumber` client at all. Entries here can also override the settings above for one network: on `testnet`, the `admin` is a testnet account instead, because accounts belong to a network. + +Want to see exactly what Scaffold will use for a network, with all the defaults filled in? Run `stellar scaffold config show --network local`. + ### Let's break it already! -That's it! That last line! That's how we break things. Go ahead and remove the `after_deploy` script entirely. +That's it! That `after-deploy` line! That's how we break things. Go ahead and remove it entirely. ```diff - constructor_args = """ - --admin me - """ -- --after_deploy = """ --reset --""" + args: + admin: ${account.me} +- after-deploy: [reset] + networks: ``` Can you guess what will happen? @@ -200,7 +194,7 @@ To get a fresh deployment, clear the artifacts Stellar Scaffold is tracking — stellar scaffold clean ``` -Re-run `npm run dev` and you'll see it churn through re-deploying the contract. This time you won't see the output about running the `after_deploy` script. +Re-run `npm run dev` and you'll see it churn through re-deploying the contract. This time you won't see the output about the after-deploy calls. Now you can trigger the bug in two exciting ways! @@ -291,12 +285,12 @@ Much cleaner! The logic is now centralized in our helper function. Note that thi $ npm run dev ``` -Click over to the Debugger if you're not there already and select the `guess_the_number` contract. You'll see that `reset` is listed here, but `set_random_number` is not. +Click over to the Debugger if you're not there already and select the `guess-the-number` contract. You'll see that `reset` is listed here, but `set_random_number` is not. Our `reset` method is available to be called by code _outside_ our contract because we opted in to it being a public method with the `pub` keyword. Our `set_random_number` is private by default, it's not visible to the outside world. It's not listed in the Contract Explorer. It's not listed in the CLI help either: ```bash -$ stellar contract invoke --id guess_the_number --source me --network local -- help +$ stellar contract invoke --id guess-the-number --source me --network local -- help Commands: reset Update the number. Only callable by admin. guess Guess a number between 1 and 10 @@ -307,7 +301,7 @@ Commands: It would error if you tried to invoke it: ```bash -$ stellar contract invoke --id guess_the_number --source me --network local -- set_random_number +$ stellar contract invoke --id guess-the-number --source me --network local -- set_random_number error: unrecognized subcommand 'set_random_number' ``` @@ -324,7 +318,7 @@ $ stellar keys generate bob --network local --fund ✅ Key saved with alias bob in ".config/stellar/identity/bob.toml" ✅ Account bob funded on "Standalone Network ; February 2017" -$ stellar contract invoke --id guess_the_number --source bob --network local -- reset +$ stellar contract invoke --id guess-the-number --source bob --network local -- reset ❌ error: Missing signing key for account GDAQWVA6REGN47BBCFY6SGQ4YTIGMDZZFHDOVUZXMVRAAT6OEZGCACGH ``` @@ -472,11 +466,11 @@ _🏗️✨ Coming soon._ In this step, we covered several important concepts: -1. `environments.toml` structure +1. `scaffold.yml` structure -- **network**: Configure which network each enviroment connects to, and automatically run a local node -- **accounts**: Create account keypairs for an environment -- **contracts**: Specify "contract dependencies," for which to build contract clients, for each environment. In `development`, Scaffold CLI will also automatically build & deploy the contracts, too, with an optional `after_deploy` script +- **networks**: Configure the networks your app runs on, and automatically run a local node +- **accounts**: Create account keypairs for each network +- **contracts**: Specify "contract dependencies," for which to build contract clients, and which networks each one exists on. For `type: workspace` contracts, Scaffold CLI will also automatically build & deploy them, with constructor `args` and optional `after-deploy` calls 2. Code Organization - **Private functions**: Help organize code and prevent external access to internal logic From c5d4847b0de6dc2e44e7ec79d25db30d40e552c6 Mon Sep 17 00:00:00 2001 From: Zach Fedor Date: Thu, 1 Oct 2026 18:09:00 -0400 Subject: [PATCH 2/5] chore: rename environments page to configuration --- README.md | 4 ++-- crates/stellar-scaffold-cli/README.md | 4 ++-- docs/site/docs/cli.md | 10 +++++----- docs/site/docs/{environments.md => configuration.md} | 0 docs/site/docs/extensions.md | 2 +- docs/site/docs/quick-start.mdx | 2 +- docs/site/docusaurus.config.ts | 7 +++++++ .../current/{environments.md => configuration.md} | 0 .../current/quick-start.mdx | 2 +- docs/site/sidebars.ts | 2 +- 10 files changed, 20 insertions(+), 13 deletions(-) rename docs/site/docs/{environments.md => configuration.md} (100%) rename docs/site/i18n/es/docusaurus-plugin-content-docs/current/{environments.md => configuration.md} (100%) diff --git a/README.md b/README.md index bd6dc1de..54b2c2f9 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ my-project/ This template provides a ready-to-use frontend application with example smart contracts and their TypeScript clients. You can use these as reference while building your own contracts and UI. The frontend is set up with `Vite`, `React`, and includes basic components for interacting with the contracts. -See the [CLI Documentation](https://scaffoldstellar.com/docs/cli) for detailed command information and the [Configuration Guide](https://scaffoldstellar.com/docs/environments) for configuration details. +See the [CLI Documentation](https://scaffoldstellar.com/docs/cli) for detailed command information and the [Configuration Guide](https://scaffoldstellar.com/docs/configuration) for configuration details. --- @@ -235,7 +235,7 @@ Your repo contains the following key folders: ## Documentation * [CLI Commands](https://scaffoldstellar.com/docs/cli) -* [Configuration](https://scaffoldstellar.com/docs/environments) +* [Configuration](https://scaffoldstellar.com/docs/configuration) * [Registry Guide](https://scaffoldstellar.com/docs/registry) --- diff --git a/crates/stellar-scaffold-cli/README.md b/crates/stellar-scaffold-cli/README.md index 7f8fe338..083c87c8 100644 --- a/crates/stellar-scaffold-cli/README.md +++ b/crates/stellar-scaffold-cli/README.md @@ -93,7 +93,7 @@ contracts: admin: ${account.testnet-user} ``` -Validate it with `stellar scaffold config check`, and print the resolved config for one network with `stellar scaffold config show --network `. See the [Configuration Guide](https://scaffoldstellar.com/docs/environments) for every option. +Validate it with `stellar scaffold config check`, and print the resolved config for one network with `stellar scaffold config show --network `. See the [Configuration Guide](https://scaffoldstellar.com/docs/configuration) for every option. Projects created before version 2 keep this configuration in `environments.toml`, selected with `STELLAR_SCAFFOLD_ENV`. `build` and `watch` still read it. @@ -136,5 +136,5 @@ repository = "https://github.com/your-org/your-project" See the full documentation: - [CLI Commands Guide](https://scaffoldstellar.com/docs/cli) -- [Configuration](https://scaffoldstellar.com/docs/environments) +- [Configuration](https://scaffoldstellar.com/docs/configuration) - [Deployment Guide](https://scaffoldstellar.com/docs/deploy) \ No newline at end of file diff --git a/docs/site/docs/cli.md b/docs/site/docs/cli.md index 8b0e8eb5..223bbb51 100644 --- a/docs/site/docs/cli.md +++ b/docs/site/docs/cli.md @@ -30,7 +30,7 @@ The init command creates: Official templates come from the [Stellar Scaffold templates repo](https://github.com/stellar-scaffold/ui). -With `--no-template` (or `--template none`), the frontend layer is omitted entirely: no `app/`, no `app-lib/`, no JS workspaces, no dependency install. Use plain `stellar scaffold build` to compile the contracts. Adding `--build-clients` still deploys and generates a client for every contract listed in [`scaffold.yml`](./environments.md), even though there is no app to consume them. +With `--no-template` (or `--template none`), the frontend layer is omitted entirely: no `app/`, no `app-lib/`, no JS workspaces, no dependency install. Use plain `stellar scaffold build` to compile the contracts. Adding `--build-clients` still deploys and generates a client for every contract listed in [`scaffold.yml`](./configuration.md), even though there is no app to consume them. ## Generate Command @@ -52,7 +52,7 @@ Options: The generate command downloads the example, caches it locally, writes it to `contracts//`, and merges the dependencies it needs into your workspace `Cargo.toml`. Each example set is pinned to a supported release rather than tracking upstream `main`, so the version you get is the one this CLI release was tested against. -`generate` does not add the contract to `scaffold.yml`. Add an entry under `contracts:` yourself, with `type: workspace`, the crate's package name as `source`, its constructor `args`, and the networks it belongs on. See [Configuration](./environments.md#contracts). +`generate` does not add the contract to `scaffold.yml`. Add an entry under `contracts:` yourself, with `type: workspace`, the crate's package name as `source`, its constructor `args`, and the networks it belongs on. See [Configuration](./configuration.md#contracts). ## Upgrade Command @@ -70,7 +70,7 @@ The upgrade command: - Validates the existing workspace (requires `Cargo.toml` and `contracts/` directory) - Downloads and integrates the frontend template -- Generates `environments.toml` with discovered contracts. This is the legacy config format; see [Configuration](./environments.md#legacy-environmentstoml) to move it to `scaffold.yml` version 2 +- Generates `environments.toml` with discovered contracts. This is the legacy config format; see [Configuration](./configuration.md#legacy-environmentstoml) to move it to `scaffold.yml` version 2 - Analyzes contracts for constructor arguments and prompts for configuration - Preserves all existing contract code and project structure - Adds development tools and configurations @@ -91,7 +91,7 @@ stellar scaffold build [options] Options: -- `--build-clients`: Deploy contracts and generate TypeScript client packages for them, as configured in [`scaffold.yml`](./environments.md) +- `--build-clients`: Deploy contracts and generate TypeScript client packages for them, as configured in [`scaffold.yml`](./configuration.md) - `--network `: Network from `scaffold.yml` to build for. Defaults to `STELLAR_NETWORK`, then the network set with `stellar network use`, then `local` - `--list` or `--ls`: List package names in order of build - [Standard Soroban contract build options also supported] @@ -221,7 +221,7 @@ The JSON holds a `checks` array — each entry with `name`, `category`, `severit ## Config Command -Validate and inspect [`scaffold.yml`](./environments.md): +Validate and inspect [`scaffold.yml`](./configuration.md): ```bash stellar scaffold config check [options] diff --git a/docs/site/docs/environments.md b/docs/site/docs/configuration.md similarity index 100% rename from docs/site/docs/environments.md rename to docs/site/docs/configuration.md diff --git a/docs/site/docs/extensions.md b/docs/site/docs/extensions.md index 6e63b74d..3c47a702 100644 --- a/docs/site/docs/extensions.md +++ b/docs/site/docs/extensions.md @@ -32,7 +32,7 @@ You only need to handle the hooks relevant to your extension. Hooks you do not l ## Registering an extension -List your extension under `extensions:` in [`scaffold.yml`](./environments.md). Extensions run in the order listed, on every network: +List your extension under `extensions:` in [`scaffold.yml`](./configuration.md). Extensions run in the order listed, on every network: ```yaml extensions: diff --git a/docs/site/docs/quick-start.mdx b/docs/site/docs/quick-start.mdx index b4de3724..bb6d5ec8 100644 --- a/docs/site/docs/quick-start.mdx +++ b/docs/site/docs/quick-start.mdx @@ -123,7 +123,7 @@ Switching networks — local to testnet, say — changes what those objects poin Because the directory is rewritten on every build, edits inside it are lost. To customize how a contract is called, wrap it in your own module under `app/` and import that instead. -See the [CLI Documentation](./cli.md) for detailed command information and the [Configuration Guide](./environments.md) for configuring networks, accounts, and contracts in `scaffold.yml`. +See the [CLI Documentation](./cli.md) for detailed command information and the [Configuration Guide](./configuration.md) for configuring networks, accounts, and contracts in `scaffold.yml`. --- diff --git a/docs/site/docusaurus.config.ts b/docs/site/docusaurus.config.ts index 8c0a1b50..254d31a4 100644 --- a/docs/site/docusaurus.config.ts +++ b/docs/site/docusaurus.config.ts @@ -50,6 +50,13 @@ const config: Config = { disableInDev: false, }, ], + [ + '@docusaurus/plugin-client-redirects', + { + // Keep links to renamed pages working. + redirects: [{ from: '/docs/environments', to: '/docs/configuration' }], + }, + ], ], presets: [ diff --git a/docs/site/i18n/es/docusaurus-plugin-content-docs/current/environments.md b/docs/site/i18n/es/docusaurus-plugin-content-docs/current/configuration.md similarity index 100% rename from docs/site/i18n/es/docusaurus-plugin-content-docs/current/environments.md rename to docs/site/i18n/es/docusaurus-plugin-content-docs/current/configuration.md diff --git a/docs/site/i18n/es/docusaurus-plugin-content-docs/current/quick-start.mdx b/docs/site/i18n/es/docusaurus-plugin-content-docs/current/quick-start.mdx index 518c0bef..42c04fde 100644 --- a/docs/site/i18n/es/docusaurus-plugin-content-docs/current/quick-start.mdx +++ b/docs/site/i18n/es/docusaurus-plugin-content-docs/current/quick-start.mdx @@ -94,7 +94,7 @@ my-project/ This template provides a ready-to-use frontend application with example smart contracts and their TypeScript clients. You can use these as reference while building your own contracts and UI. The frontend is set up with `Vite`, `React`, and includes basic components for interacting with the contracts. -See the [CLI Documentation](./cli.md) for detailed command information and the [Environments Guide](./environments.md) for configuration details. +See the [CLI Documentation](./cli.md) for detailed command information and the [Configuration Guide](./configuration.md) for configuration details. --- diff --git a/docs/site/sidebars.ts b/docs/site/sidebars.ts index c080e650..c824622e 100644 --- a/docs/site/sidebars.ts +++ b/docs/site/sidebars.ts @@ -31,7 +31,7 @@ const sidebars: SidebarsConfig = { ], }, "cli", - "environments", + "configuration", "extensions", "registry", "deploy", From c5ad42db266a75f765f1cd51ce714c2ee194b64e Mon Sep 17 00:00:00 2001 From: Zach Fedor Date: Thu, 1 Oct 2026 18:14:25 -0400 Subject: [PATCH 3/5] fix: install redirect package --- docs/site/docs/extensions.md | 2 +- docs/site/package-lock.json | 25 +++++++++++++++++++++++++ docs/site/package.json | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/site/docs/extensions.md b/docs/site/docs/extensions.md index 3c47a702..46a5448b 100644 --- a/docs/site/docs/extensions.md +++ b/docs/site/docs/extensions.md @@ -32,7 +32,7 @@ You only need to handle the hooks relevant to your extension. Hooks you do not l ## Registering an extension -List your extension under `extensions:` in [`scaffold.yml`](./configuration.md). Extensions run in the order listed, on every network: +List your extension under `extensions:` in [`scaffold.yml`](/docs/configuration). Extensions run in the order listed, on every network: ```yaml extensions: diff --git a/docs/site/package-lock.json b/docs/site/package-lock.json index d380f4f5..4d11b023 100644 --- a/docs/site/package-lock.json +++ b/docs/site/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@docusaurus/core": "3.9.2", + "@docusaurus/plugin-client-redirects": "^3.9.2", "@docusaurus/plugin-ideal-image": "^3.9.2", "@docusaurus/preset-classic": "3.9.2", "@mdx-js/react": "^3.1.1", @@ -3564,6 +3565,30 @@ "react-dom": "*" } }, + "node_modules/@docusaurus/plugin-client-redirects": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-client-redirects/-/plugin-client-redirects-3.9.2.tgz", + "integrity": "sha512-lUgMArI9vyOYMzLRBUILcg9vcPTCyyI2aiuXq/4npcMVqOr6GfmwtmBYWSbNMlIUM0147smm4WhpXD0KFboffw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.9.2", + "@docusaurus/logger": "3.9.2", + "@docusaurus/utils": "3.9.2", + "@docusaurus/utils-common": "3.9.2", + "@docusaurus/utils-validation": "3.9.2", + "eta": "^2.2.0", + "fs-extra": "^11.1.1", + "lodash": "^4.17.21", + "tslib": "^2.6.0" + }, + "engines": { + "node": ">=20.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + } + }, "node_modules/@docusaurus/plugin-content-blog": { "version": "3.9.2", "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.9.2.tgz", diff --git a/docs/site/package.json b/docs/site/package.json index 276d8b59..c646864c 100644 --- a/docs/site/package.json +++ b/docs/site/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@docusaurus/core": "3.9.2", + "@docusaurus/plugin-client-redirects": "^3.9.2", "@docusaurus/plugin-ideal-image": "^3.9.2", "@docusaurus/preset-classic": "3.9.2", "@mdx-js/react": "^3.1.1", From 4be4abdd5570031f622de673998f50ad5bc2eeed Mon Sep 17 00:00:00 2001 From: Zach Fedor Date: Thu, 1 Oct 2026 18:28:36 -0400 Subject: [PATCH 4/5] feat: add agent skill --- README.md | 1 + skills/stellar-scaffold/SKILL.md | 204 ++++++++++++++++++ skills/stellar-scaffold/references/cli.md | 70 ++++++ skills/stellar-scaffold/references/config.md | 187 ++++++++++++++++ .../stellar-scaffold/references/frontend.md | 83 +++++++ 5 files changed, 545 insertions(+) create mode 100644 skills/stellar-scaffold/SKILL.md create mode 100644 skills/stellar-scaffold/references/cli.md create mode 100644 skills/stellar-scaffold/references/config.md create mode 100644 skills/stellar-scaffold/references/frontend.md diff --git a/README.md b/README.md index 54b2c2f9..6d2164ed 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,7 @@ Your repo contains the following key folders: * [CLI Commands](https://scaffoldstellar.com/docs/cli) * [Configuration](https://scaffoldstellar.com/docs/configuration) * [Registry Guide](https://scaffoldstellar.com/docs/registry) +* [Agent skill](./skills/stellar-scaffold/SKILL.md): teaches AI coding agents to create and work in Scaffold projects. Point your agent at `https://raw.githubusercontent.com/stellar-scaffold/cli/main/skills/stellar-scaffold/SKILL.md` --- ## Additional Developer Resources diff --git a/skills/stellar-scaffold/SKILL.md b/skills/stellar-scaffold/SKILL.md new file mode 100644 index 00000000..f98eabf6 --- /dev/null +++ b/skills/stellar-scaffold/SKILL.md @@ -0,0 +1,204 @@ +--- +name: stellar-scaffold +description: Build full-stack Stellar dApps with Stellar Scaffold, the `stellar scaffold` CLI plugin. Use when creating a new Soroban project with a frontend (`stellar scaffold init`), adding a frontend to an existing Soroban workspace (`upgrade`), configuring networks, accounts, and contracts in `scaffold.yml`, running the local dev loop (`npm run dev` / `stellar scaffold watch --build-clients`), calling contracts from a React or Svelte app through the generated clients in `@stellar-scaffold/app-lib/clients`, adding example contracts (`generate contract`), building for testnet or mainnet, or debugging a Scaffold build with `config check` and `doctor`. +--- + +# Stellar Scaffold + +Stellar Scaffold turns a Rust/Soroban contract workspace and a TypeScript frontend into one project: + +- **`stellar scaffold` CLI plugin**: creates projects, builds and deploys contracts, and generates a typed TypeScript **client** for each one. +- **`scaffold.yml`**: one config file at the project root that says which networks you build for, which accounts exist on each, and which contracts to deploy or connect to on each network. +- **`@stellar-scaffold/app-lib`**: a workspace package in every project with wallet, network, and formatting helpers, plus the generated clients at `@stellar-scaffold/app-lib/clients`. + +Docs: https://stellarscaffold.org/docs + +## Setup + +Prerequisites: Rust (with the `wasm32v1-none` target), the Stellar CLI (`stellar`), Node.js, and Docker (for the local network). + +```bash +cargo install --locked stellar-scaffold-cli # or: cargo binstall stellar-scaffold-cli +stellar scaffold version +``` + +`stellar-scaffold` is a plugin: the Stellar CLI finds it on `PATH`, so you always run it as `stellar scaffold `. + +## Start a project + +```bash +stellar scaffold init my-app --template react -y # or: --template svelte +cd my-app +npm run dev # app at http://localhost:5173 +``` + +- `--template` takes `react`, `svelte`, `none` (contracts only, no JS at all), or a community repo as `org/repo[#branch]`. Omit it to be prompted. `--no-template` is the same as `--template none`. +- `-p ` picks the package manager. With `--template` or `-y` it defaults to npm without prompting. +- `init` does everything up front: copies `.env.example` to `.env`, installs dependencies, compiles the contracts, deploys them to the local network and generates their clients, then `git init`s and commits. If that build fails (most often because Docker isn't running), `init` only warns and still creates the project. Fix the cause, then run `npm run dev`. + +Already have a Soroban workspace (`Cargo.toml` plus `contracts/`)? Run `stellar scaffold upgrade --template react` in it instead. It adds the frontend files and never overwrites files that already exist. **Then fix the config by hand**, because `upgrade` currently copies the template's `scaffold.yml`, whose `contracts:` lists the template's example crates rather than yours, and also writes a legacy `environments.toml` that version 2 ignores: + +1. In `scaffold.yml`, replace every entry under `contracts:` with one per crate in your `contracts/` (`type: workspace`, `source: `, constructor `args`, `networks:`). Use the generated `environments.toml` as a guide to each constructor's arguments. +2. Delete `environments.toml`. +3. Run `stellar scaffold config check` until it reports no errors. + +## Project layout + +``` +my-app/ +├── contracts// # Soroban contracts; the workspace builds every cdylib in contracts/* +├── app/ # your React or Svelte app +├── app-lib/ # @stellar-scaffold/app-lib: wallet, network, format helpers +│ └── clients/ # GENERATED: one package per contract + index.ts. Never edit. +├── scaffold.yml # networks, accounts, contracts +├── .env # STELLAR_NETWORK + PUBLIC_STELLAR_* for the frontend (gitignored) +├── Cargo.toml # Rust workspace +└── package.json # npm workspaces root +``` + +`app/CLAUDE.md` (`app/AGENTS.md` in newer projects) describes the app's own structure: routes, providers, stores. Read it before editing the frontend. + +## The dev loop + +`npm run dev` runs two processes: `stellar scaffold watch --build-clients` and Vite. On every change to a contract or to `scaffold.yml`, `watch`: + +1. Builds the contracts (Wasm in `target/stellar//.wasm`, with `-` replaced by `_`). +2. For the selected network: starts the local container if needed, creates and funds the accounts, deploys or upgrades contracts whose Wasm changed, and runs their `after-deploy` calls. +3. Regenerates `app-lib/clients//` and `app-lib/clients/index.ts`, which Vite hot-reloads. + +Changing only constructor `args` or `after-deploy` does **not** redeploy. Run `stellar scaffold clean` and restart to get fresh deployments. + +## `scaffold.yml` at a glance + +```yaml +version: 2 + +networks: + local: + accounts: [me] # created and funded; the first one signs deploys + testnet: + accounts: [testnet-user] + allow-deploy: true # public networks don't deploy unless you opt in + +contracts: + guess-the-number: # client name: exported as `guessTheNumber` + type: workspace # build + deploy this crate from contracts/ + source: guess-the-number # the crate's package name + args: # __constructor arguments, by parameter name + admin: ${account.me} + after-deploy: [reset] # no-argument methods called after deploy + networks: # the contract ONLY exists on networks listed here + local: + testnet: + args: + admin: ${account.testnet-user} # accounts belong to one network +``` + +Rules that trip agents up: + +- **A contract only exists on the networks it lists.** Building any other network skips it, and its client is not exported, so imports of it fail to typecheck. +- **`build` supports only `type: workspace` and `type: contract`** (a deployed `C…` address) today. `registry`, `asset`, `wasm-file`, `wasm-hash`, `wasm-registry`, `from-network` and `after-deploy-script` pass validation but fail at build time. +- **Keys are kebab-case.** Unknown keys are errors, with a "did you mean" hint. +- **`args` values are typed YAML, not shell.** Use `${account.}` for addresses, `${env.VAR}` for secrets. `$(…)` is literal text. + +After every edit, run `stellar scaffold config check`. To see exactly what a build will use, run `stellar scaffold config show --network `. Full schema: [references/config.md](references/config.md). + +## Calling contracts from the app + +Import the generated client by its camelCase name. Never import from `app-lib/clients//` directly, and never hardcode a contract ID or RPC URL. + +```ts +import { guessTheNumber } from "@stellar-scaffold/app-lib/clients" + +// Read: simulate only, no signature +const { result: admin } = await guessTheNumber.admin() + +// Write: build with the caller as source, then sign with the connected wallet and send +const tx = await guessTheNumber.guess( + { a_number: BigInt(5), guesser: address }, + { publicKey: address }, +) +const { result } = await tx.signAndSend({ signTransaction }) +if (result.isErr()) console.error(result.unwrapErr()) +``` + +Method names and argument names come straight from the Rust contract (`snake_case`), so `i128`/`u64` and similar values are `bigint`. Wallet connection, `signTransaction`, balances, and network info come from `@stellar-scaffold/app-lib`. Details: [references/frontend.md](references/frontend.md). + +## Adding a contract + +```bash +stellar scaffold generate contract --ls # list examples +stellar scaffold generate contract --from oz/fungible-pausable +# or write your own crate under contracts// with crate-type = ["cdylib"] +``` + +`generate` writes `contracts//` and merges its dependencies into the workspace `Cargo.toml`. It does **not** touch `scaffold.yml`, so add an entry yourself: + +```yaml +contracts: + my-token: + type: workspace + source: fungible-pausable-example # `name` from contracts//Cargo.toml + args: + owner: ${account.me} + networks: + local: +``` + +Then `stellar scaffold config check`. The client appears as `myToken` in `@stellar-scaffold/app-lib/clients` on the next build. + +## Testnet and mainnet + +The network comes from `--network `, else `STELLAR_NETWORK` (set in `.env`), else `stellar network use`, else `local`. Switching networks takes two changes in `.env`: + +```bash +STELLAR_NETWORK=testnet # what the CLI builds and deploys to +PUBLIC_STELLAR_NETWORK="TESTNET" # what the frontend connects to +PUBLIC_STELLAR_NETWORK_PASSPHRASE="Test SDF Network ; September 2015" +PUBLIC_STELLAR_RPC_URL="https://soroban-testnet.stellar.org" +PUBLIC_STELLAR_HORIZON_URL="https://horizon-testnet.stellar.org" +``` + +`testnet`, `futurenet` and `mainnet` refuse Scaffold deploys until the network sets `allow-deploy: true`. For production, deploy once yourself and point Scaffold at the result instead of deploying from the dev loop: + +```yaml +networks: + mainnet: + rpc-url: https://your-rpc-provider.example # stellar-cli has no default mainnet RPC + rpc-headers: + Authorization: "Bearer ${env.MAINNET_RPC_TOKEN}" + +contracts: + guess-the-number: + # ... + networks: + mainnet: + type: contract + source: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC +``` + +To publish and deploy by name, use the Stellar Registry: https://raw.githubusercontent.com/stellar-registry/cli/main/skills/stellar-registry/SKILL.md (then `stellar registry fetch-contract-id ` gives the `C…` address for `source`). Build the frontend with `npm run build --workspace app` (output in `app/dist/` for React, `app/build/` for Svelte). + +## Diagnosing problems + +```bash +stellar scaffold config check # every scaffold.yml problem, with line numbers and fixes +stellar scaffold config show --network local +stellar scaffold doctor # toolchain, project, and local network health +``` + +Full command list: [references/cli.md](references/cli.md). + +## Common pitfalls + +- **Edited `app-lib/clients/`**: it is regenerated on every build. Wrap a client in your own module under `app/` instead. +- **`Module has no exported member 'myContract'`**: the contract has no entry for the network you built (`networks:` under the contract), the build failed before codegen, or the client name doesn't match. Client `my-contract` exports as `myContract`. +- **Frontend talks to the wrong network**: `STELLAR_NETWORK` and the `PUBLIC_STELLAR_*` values in `.env` disagree. The CLI bakes contract IDs for `STELLAR_NETWORK`; the app connects with `PUBLIC_STELLAR_*`. +- **`would be deployed to testnet, which has deploys disabled`**: add `allow-deploy: true` to that network, or point the contract at an existing deployment with `type: contract`. +- **`account is not listed in network 's accounts`**: each network has its own accounts. Add it to `networks..accounts`, or override `args` for that network. +- **`uses type: registry, which build does not support yet`**: see the supported types above. Use `type: contract` with the address for now. +- **Changed constructor `args`, nothing happened**: only Wasm changes redeploy. Stop the dev server, `stellar scaffold clean`, restart. +- **`STELLAR_SCAFFOLD_ENV is ignored`**: that variable and the `development`/`staging`/`production` environments belong to the legacy `environments.toml`. Use networks and `STELLAR_NETWORK`. +- **Local deploy fails or hangs**: Docker isn't running. `stellar scaffold doctor` checks the toolchain; `docker ps` and `stellar container start local` check the network. +- **`parse-error: while parsing a flow sequence`**: `${…}` inside `[ ]` or `{ }` must be quoted: `["${account.me}"]`. Block style (`admin: ${account.me}`) needs no quotes. +- **`--tutorial`, `client = true`, `constructor_args`, `run_locally`** show up in old docs and blog posts. None of them exist in `scaffold.yml` version 2. diff --git a/skills/stellar-scaffold/references/cli.md b/skills/stellar-scaffold/references/cli.md new file mode 100644 index 00000000..98d2d299 --- /dev/null +++ b/skills/stellar-scaffold/references/cli.md @@ -0,0 +1,70 @@ +# `stellar scaffold` command reference + +Run every command as `stellar scaffold `. All commands accept the Stellar CLI global flags: `--config-dir`, `-q/--quiet`, `-v/--verbose`, `--very-verbose`, `-f/--filter-logs`, `--no-cache`. + +Network selection, wherever a command takes `--network`: the flag, else `STELLAR_NETWORK`, else the `stellar network use` default, else `local`. The name must be declared under `networks:` in `scaffold.yml`. + +## Projects + +| Command | Purpose | Key arguments | +|---|---|---| +| `init ` | Create a project from the official template monorepo, or a community repo | `--template `, `--no-template` (same as `none`), `-p/--package-manager `, `-y/--yes` | +| `upgrade [WORKSPACE_PATH]` | Add a frontend to an existing Soroban workspace (needs `Cargo.toml` and `contracts/`) | `--template `, `--skip-prompt` (don't ask for constructor arguments) | +| `generate contract` | Add a contract crate to `contracts/` | `--from `, `--ls`, `--from-wizard`, `-o/--output ` (default `contracts/`), `--force` | +| `update-env --name ` | Set one variable in `.env` | `--value ` (else read from stdin), `--env-file ` (default `.env`) | +| `version` | Print the plugin version | | + +`init` without `-y` or `--template` prompts for a framework and a package manager. With `--template none` it creates a contracts-only workspace with no `app/`, no `app-lib/`, and no JS dependencies. + +`upgrade` copies template files without overwriting anything that exists, then writes a legacy `environments.toml`. Fix `scaffold.yml` afterwards as described in the main skill. + +`generate contract --from` picks from two example sets, each pinned to a release this CLI was tested with: `oz/` ([OpenZeppelin stellar-contracts](https://github.com/OpenZeppelin/stellar-contracts) examples) and `stellar/` (soroban-examples). It does not edit `scaffold.yml`. + +## Building + +| Command | Purpose | Key arguments | +|---|---|---| +| `build` | Compile every `cdylib` crate in dependency order | `--build-clients` (also deploy and generate clients for the selected network), `--network `, `--package `, `--manifest-path `, `--profile ` (default `release`), `--out-dir `, `--features`, `--all-features`, `--no-default-features`, `--locked`, `--optimize[=false]`, `--meta `, `--list/--ls`, `--print-commands-only` | +| `watch` | `build`, then rebuild on any change to a contract or `scaffold.yml` | Same as `build`. The project's `npm run dev` runs `stellar scaffold watch --build-clients` | +| `clean` | Remove Scaffold-generated artifacts | `--manifest-path ` | + +Without `--build-clients`, `build` only compiles. With it, for the selected network it also: starts the local container (when the network's passphrase is the standalone one, or `start-container: true`), creates and funds the network's `accounts`, deploys or upgrades `workspace` contracts whose Wasm changed, runs their `after-deploy` methods, and regenerates `//` and `/index.ts`. If one contract fails, the rest still deploy and export, then the build exits non-zero. + +`build` and `watch` also accept a positional `[ENV]` (`development`, `testing`, `staging`, `production`, or `STELLAR_SCAFFOLD_ENV`). It only applies to legacy projects with `environments.toml`, and their `--help` text still describes that legacy flow. + +`clean` deletes `target/stellar/`, everything generated in the clients directory (keeping checked-in files like `.gitkeep`), and the contract and identity aliases Scaffold created for the local and test networks. Use it to force fresh deployments, then rerun `build --build-clients` or `npm run dev`. + +## Config and diagnostics + +| Command | Purpose | Key arguments | +|---|---|---| +| `config check` | Validate `scaffold.yml`; exit non-zero on errors | `--network ` (also check that it's declared), `--strict` (fail on warnings), `--json`, `--manifest-path` | +| `config show` | Print the fully resolved config for one network | `--network ` (default `STELLAR_NETWORK`, then `local`), `--json` (else YAML), `--manifest-path` | +| `doctor` | Check toolchain, project, and local network | `--json`, `--strict`, `--manifest-path`, `--env ` (legacy projects only) | +| `ext ls` | List configured extensions and their hooks | `[ENV]` (legacy projects only) | + +`config check` makes no network calls and reads no keys, so it is safe in CI. Each diagnostic has a code (e.g. `unknown-key`, `crate-not-found`, `unknown-account`, `deploy-not-allowed`), the line, and a suggested fix. `--json` prints `[]` when clean. + +`config show` applies `extends`, fills in built-in RPC URLs and passphrases, merges per-network contract overrides, and substitutes `${env.…}` and `${network.…}`. `${account.…}` stays symbolic. Contracts that don't list the network are omitted. + +`doctor` checks `rustc` (against `rust-toolchain.toml`), the `wasm32v1-none` target, the `stellar` CLI version, Node and the `packageManager` pin, `scaffold.yml` (listing every `config check` problem), the `contracts-dir` and `clients-dir` directories, the project's `engines.stellar-scaffold` constraint, and `.env`. Its Docker, local RPC, and extension checks currently run only for legacy `environments.toml` projects. Exit code is `1` on any error, or on warnings with `--strict`. + +## Examples + +```bash +# New React project, no prompts, pnpm +stellar scaffold init my-app --template react -p pnpm -y + +# Contracts-only project +stellar scaffold init my-contracts --template none + +# Build and deploy for testnet once, without the dev server +stellar scaffold build --build-clients --network testnet + +# Validate in CI +stellar scaffold config check --strict --json +stellar scaffold doctor --strict --json + +# Start over with fresh local deployments +stellar scaffold clean && npm run dev +``` diff --git a/skills/stellar-scaffold/references/config.md b/skills/stellar-scaffold/references/config.md new file mode 100644 index 00000000..d5dda2bb --- /dev/null +++ b/skills/stellar-scaffold/references/config.md @@ -0,0 +1,187 @@ +# Configuration reference + +A Scaffold project has three config surfaces: + +| File | Holds | Committed | +|---|---|---| +| `scaffold.yml` | Networks, accounts, contracts, extensions, project directories | Yes | +| `.env` | `STELLAR_NETWORK`, `PUBLIC_STELLAR_*` for the frontend, secrets referenced by `${env.…}` | No (copy from `.env.example`) | +| `Cargo.toml` | Rust workspace (`members = ["contracts/*"]`), per-crate `[package.metadata.stellar]` contract metadata | Yes | + +## `scaffold.yml` (schema version 2) + +```yaml +version: 2 # required, must be 2 + +project: + contracts-dir: contracts # default + clients-dir: app-lib/clients # default + +networks: + local: + accounts: [me, alice] + default-account: me + testnet: + accounts: [testnet-user] + allow-deploy: true + preview: # a second setup on testnet, with its own accounts + extends: testnet + accounts: [preview-user] + mainnet: + rpc-url: https://your-rpc-provider.example + rpc-headers: + Authorization: "Bearer ${env.MAINNET_RPC_TOKEN}" + accounts: [mainnet-user] + +contracts: + my-token: + type: workspace + source: fungible-token + signer: alice + args: + owner: ${account.alice} + initial_supply: 1000000000000000000000000 + after-deploy: [unpause] + networks: + local: + testnet: + signer: testnet-user + args: + owner: ${account.testnet-user} + mainnet: + type: contract + source: CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC + +extensions: + reporter: + warn_size_kb: 128 +``` + +The parser is strict: unknown keys, duplicate keys and wrong types are errors. Keys are kebab-case. Names of networks and accounts use letters, digits, `-` and `_`, starting with a letter or digit. A bare `name:` entry is the same as `name: {}`. + +### `networks` + +| Key | Default | Meaning | +|---|---|---| +| `rpc-url` | stellar-cli's URL for `local`, `testnet`, `futurenet` | Required for `mainnet` and custom names unless inherited | +| `network-passphrase` | stellar-cli's passphrase for built-in names | Required for custom names unless inherited | +| `rpc-headers` | none | Map of headers sent with every RPC request | +| `accounts` | none | Aliases Scaffold creates (`stellar keys generate`) and funds where it can | +| `default-account` | first of `accounts` | Signs deploys when a contract has no `signer`. Must be in `accounts` | +| `start-container` | `true` only for the standalone passphrase | Start a `stellar/quickstart` container. `false` if a chain already runs at `rpc-url` | +| `allow-deploy` | `false` for testnet, futurenet, mainnet passphrases; else `true` | Whether `workspace` contracts may deploy here. `true` on mainnet is a warning | +| `extends` | none | Inherit another declared network's settings | + +Defaults come from the **passphrase**, never the name: a custom network on testnet's passphrase behaves like testnet. + +`extends` merge rules (also used for contract per-network overrides): scalars override, maps merge, lists replace. An explicit `allow-deploy` is inherited; an unset one is recomputed from the child's passphrase. Chains are at most three hops, with no cycles. + +Network fields may use `${env.…}` only. + +### `contracts` + +The key is the **client name**: the package directory `//` and, camelCased, the export from `index.ts` (`my-token` → `myToken`). It is independent of the crate name. It is also the contract alias Scaffold saves, so `stellar contract invoke --id my-token --network local -- …` works after a deploy. + +| Key | Meaning | +|---|---| +| `type` | Required. What `source` is. Must be paired with `source` | +| `source` | Required. Format depends on `type` | +| `args` | Constructor arguments by `__constructor` parameter name | +| `signer` | Account that deploys and runs `after-deploy`. Defaults to the network's `default-account` | +| `after-deploy` | List of no-argument methods called, in order, right after a deploy or upgrade | +| `after-deploy-script` | Path to an executable run after deploy (**not yet supported by `build`**) | +| `from-network` | Copy another network's deployed Wasm and deploy it here (**not yet supported by `build`**) | +| `networks` | Per-network entries. **Required**: the contract exists only on networks listed here | + +Keys at the contract's top level are defaults for every listed network. Per-network entries override them. Changing `type` in an override requires `source` too. + +`signer`, `args`, `after-deploy` and `after-deploy-script` only apply to contracts Scaffold deploys (`type: workspace`). Setting them on a reference type is an error. + +#### Contract types + +| `type` | `source` | `build` support | +|---|---|---| +| `workspace` | Crate package name in `contracts-dir` (`-` and `_` are interchangeable) | Builds, deploys, generates client | +| `contract` | Deployed address, `C…` | Generates a client bound to it | +| `registry` | `[namespace/]name@version` | Not yet | +| `asset` | `CODE:ISSUER` or `native` | Not yet | +| `wasm-file` | Path to a `.wasm` (types-only client) | Not yet | +| `wasm-hash` | 64 hex chars (types-only client) | Not yet | +| `wasm-registry` | `[namespace/]name@version` (types-only client) | Not yet | + +Unpinned `registry`/`wasm-registry` sources (no `@version`) are a warning. A `workspace` contract on a network with `allow-deploy: false` is an error: allow deploys there, or override that network with `type: contract`. + +#### Constructor `args` + +Written as YAML values and checked by stellar-cli against the contract's spec at deploy time: + +```yaml +args: + name: ExampleToken # String / Symbol + decimals: 7 # u32 + admin: ${account.me} # Address (the account alias resolves to its G… address) + initial_supply: 1000000000000000000000000 # i128/u128/u256 read exactly, no quotes needed + recipients: ["${account.me}", "${account.alice}"] # Vec; quote ${…} inside [ ] or { } + config: { fee_bps: 30, paused: false } # struct / map + metadata: null # left out, so an Option parameter is None +``` + +No shell is involved: `$(stellar keys address me)` is passed as literal text. Use `${account.me}`. + +### Interpolation + +| Reference | Value | Allowed in | +|---|---|---| +| `${env.NAME}` | Environment variable; error if unset | Network fields, `args` | +| `${env.NAME:-default}` | Variable, or `default` if unset | Network fields, `args` | +| `${account.NAME}` | That account's address; must be in the network's `accounts` | `args` | +| `${network.name}`, `${network.rpc-url}`, `${network.passphrase}` | The selected network's resolved values | `args` | + +`$${` is a literal `${`. `contract`, `wasm-hash` and `registry` namespaces are reserved and rejected today. + +### `extensions` + +Map of extension name to its config, run in order on every network. The value is passed verbatim as the extension's `config` JSON (keys keep their spelling, e.g. the reporter reads `warn_size_kb`). Extensions are binaries named `stellar-scaffold-` on `PATH`. + +## `.env` + +```bash +STELLAR_NETWORK=local # network the CLI builds and deploys for +XDG_CONFIG_HOME=".config" # keeps keys and aliases inside the project + +PUBLIC_STELLAR_NETWORK="LOCAL" # LOCAL | TESTNET | FUTURENET | PUBLIC (mainnet) +PUBLIC_STELLAR_NETWORK_PASSPHRASE="Standalone Network ; February 2017" +PUBLIC_STELLAR_RPC_URL="http://localhost:8000/rpc" +PUBLIC_STELLAR_HORIZON_URL="http://localhost:8000" +``` + +`PUBLIC_*` variables are exposed to the frontend by Vite. Keep them on the same network as `STELLAR_NETWORK`. `.env.example` in the project lists the testnet and mainnet values. + +The `stellar` CLI loads `.env` from the current directory before running a plugin, so `stellar scaffold …` (and `npm run dev`) see `STELLAR_NETWORK`. Running the `stellar-scaffold` binary directly does not load `.env`. + +## `Cargo.toml` contract metadata + +```toml +[package.metadata.stellar] +cargo_inherit = true # copy name, authors, homepage → home_domain, repository → source_repo, version → binver (SEP-47) +name = "my-awesome-contract" # override an inherited value +``` + +`build` writes this metadata into each contract's Wasm. + +## Legacy: `environments.toml` + +Projects whose `scaffold.yml` says `version: 1` (only a `config:` section with `contracts_dir`/`clients_dir`) keep networks, accounts and contracts in `environments.toml`, per environment (`development`, `testing`, `staging`, `production`), selected by `STELLAR_SCAFFOLD_ENV`. `build` still reads them, and ignores `--network`. + +To convert: + +- `version: 2`; `config:` → `project:` with `contracts-dir`/`clients-dir`. +- Each `[.network]` → a `networks:` entry. `run_locally` → `start-container`. An account with `default = true` → `default-account`. +- Each `[.contracts.]` → one `contracts.` entry with `type: workspace` + `source: ` (or `type: contract` + `source: ` where it had `id`), listing each network it belongs on. +- `constructor_args = "--admin me"` → `args: { admin: ${account.me} }`. `$(stellar keys address x)` → `${account.x}`. `STELLAR_ACCOUNT=x` prefix → `signer: x`. +- `after_deploy` → `after-deploy: [method, …]`, no-argument calls only. +- `client = false` has no equivalent. Every listed contract gets a client. +- `extensions = [...]` and `[.ext.]` → the top-level `extensions:` map. +- `.env`: `STELLAR_SCAFFOLD_ENV=…` → `STELLAR_NETWORK=…`. Delete `environments.toml`. + +Run `stellar scaffold config check` until clean. diff --git a/skills/stellar-scaffold/references/frontend.md b/skills/stellar-scaffold/references/frontend.md new file mode 100644 index 00000000..ecc3443b --- /dev/null +++ b/skills/stellar-scaffold/references/frontend.md @@ -0,0 +1,83 @@ +# Frontend: clients and `app-lib` + +Every official template (React and Svelte) shares one framework-agnostic package, `@stellar-scaffold/app-lib` in `app-lib/`. Framework-specific code lives in `app/`. Read `app/CLAUDE.md` (`app/AGENTS.md` in newer projects) for its routes, providers and stores. + +## Generated clients + +`stellar scaffold build --build-clients` (run by `npm run dev`) writes, for the selected network: + +- `app-lib/clients//`: a TypeScript **binding** package per contract, generated by `stellar contract bindings typescript`. It holds the contract's types and a `Client` class. +- `app-lib/clients/index.ts`: one configured **client** per contract, exported under the camelCase contract name from `scaffold.yml` and bound to that network's RPC URL, passphrase, and deployed contract ID. + +Import clients from the index, never from the binding packages: + +```ts +import { guessTheNumber, myToken } from "@stellar-scaffold/app-lib/clients" +``` + +Both directories are regenerated on every build. To add behavior, wrap a client in a module under `app/` and import that. + +A contract appears in `index.ts` only if it lists the network you built for in `scaffold.yml`. Code that imports a contract that doesn't exist on some network fails to typecheck when you build for that network. That is intentional: it catches calls to contracts that aren't deployed there. + +## Calling contract methods + +Methods and arguments mirror the Rust contract: same `snake_case` names, arguments passed as one object. Rust integer types of 64 bits and wider (`u64`, `i128`, `u128`, `u256`) are `bigint` in TypeScript; `Address` is a `G…`/`C…` string; `Option` is `T | undefined`; `Result` becomes a `Result` with `isOk()`, `isErr()`, `unwrap()`, `unwrapErr()`. + +Every call returns an assembled transaction that has already been **simulated**. + +**Read-only calls**: use the simulated result. No wallet, no fee. + +```ts +const { result: admin } = await guessTheNumber.admin() +``` + +**State-changing calls**: pass the signer's address as `publicKey`, then sign with the connected wallet and submit. + +```ts +import { signTransaction } from "@stellar-scaffold/app-lib" + +const tx = await guessTheNumber.guess( + { a_number: BigInt(guess), guesser: address }, + { publicKey: address }, // required: clients are created without a default source +) +const { result } = await tx.signAndSend({ signTransaction }) +if (result.isErr()) { + console.error(result.unwrapErr()) // the contract's error enum +} else { + console.log(result.unwrap()) +} +``` + +If a call needs another account's authorization (for example a `require_auth` on an address other than the source), inspect `tx.needsNonInvokerSigningBy()` before `signAndSend`. + +To get a client's contract ID (for event subscriptions or explorer links), read `guessTheNumber.options.contractId`. + +## `@stellar-scaffold/app-lib` + +| Export | What it does | +|---|---| +| `connectWallet()`, `disconnectWallet()`, `profileModal()` | Stellar Wallets Kit modals. On `LOCAL` and `FUTURENET` only wallets that support custom networks are offered (Freighter, xBull, Hana) | +| `signTransaction` | Pass to `signAndSend` | +| `onWalletChange(cb)` | Subscribe to `{ address, networkPassphrase }`; fires immediately and on connect, disconnect, or wallet network switch. Returns an unsubscribe function | +| `getWalletNetwork()` | The network the wallet reports, if it supports reporting one | +| `fetchBalances(address)`, `MappedBalances` | Horizon balances keyed by asset | +| `fundAccount(address)`, `getFriendbotUrl(address)` | Friendbot funding on local, testnet and futurenet | +| `subscribeToEvents(contractId, topic, onEvent, pollInterval?)` | Poll RPC for a contract's events; returns a stop function | +| `network`, `stellarNetwork`, `networkPassphrase`, `rpcUrl`, `horizonUrl` | The app's network, from `PUBLIC_STELLAR_*` in `.env` | +| `networkStatus(...)`, `NetworkState` | Compare the wallet's network with the app's (`ok`, `mismatch`, `disconnected`, `unverified`) | +| `shortAddress(addr)`, `formatNetworkName(name)`, `labPrefix()` | Formatting, and Stellar Lab link prefix | +| `@stellar-scaffold/app-lib/styles.css` | Shared base styles | + +The templates wrap the wallet state for their framework: React has `useWallet()` (from `app/src/hooks/useWallet`), returning `address`, `balances`, `isPending`, `signTransaction`, and `updateBalances`; Svelte has readable stores `address`, `balances`, `isPending`, `networkPassphrase` plus `signTransaction` and `updateBalances()` in `app/src/lib/stores/wallet`. Use these in components rather than calling `onWalletChange` again. + +## Network consistency + +The CLI bakes contract IDs into `index.ts` for the network it built (`STELLAR_NETWORK`). The app connects using `PUBLIC_STELLAR_NETWORK`, `PUBLIC_STELLAR_NETWORK_PASSPHRASE`, `PUBLIC_STELLAR_RPC_URL`, and `PUBLIC_STELLAR_HORIZON_URL`. If they point at different networks, every call fails with a missing contract or a passphrase mismatch. Change both together, then restart `npm run dev` so Vite picks up the new `.env`. + +## Building the app + +```bash +npm run build --workspace app # typecheck + Vite production build; there is no root `build` script +``` + +Build the clients for the production network first (`stellar scaffold build --build-clients --network mainnet`, with the `.env` `PUBLIC_*` values set for mainnet), so `index.ts` holds the production contract IDs. From ff66242542d6ad88aaed260b575771460b7b41df Mon Sep 17 00:00:00 2001 From: Zach Fedor Date: Thu, 1 Oct 2026 18:31:06 -0400 Subject: [PATCH 5/5] docs: add agent skills to docs site --- docs/site/docs/agent-skills.md | 70 ++++++++++++++++++++++++++++++++++ docs/site/sidebars.ts | 1 + 2 files changed, 71 insertions(+) create mode 100644 docs/site/docs/agent-skills.md diff --git a/docs/site/docs/agent-skills.md b/docs/site/docs/agent-skills.md new file mode 100644 index 00000000..324cc6df --- /dev/null +++ b/docs/site/docs/agent-skills.md @@ -0,0 +1,70 @@ +--- +sidebar_label: AI Agent Skills +--- + +# AI Agent Skills + +Stellar Scaffold publishes an [Agent Skill](https://agentskills.io): a folder of Markdown instructions that teaches AI coding agents (Claude Code, Codex, Cursor, and others) how to create and work in Scaffold projects. With it loaded, an agent knows the `stellar scaffold` commands, writes valid `scaffold.yml`, calls your contracts through the generated clients instead of hardcoding IDs, and avoids the common mistakes that come from outdated examples online. + +## What's in the skill + +The skill lives in the CLI repository at [`skills/stellar-scaffold/`](https://github.com/stellar-scaffold/cli/tree/main/skills/stellar-scaffold), versioned alongside the CLI it describes. + +| File | Covers | +| --- | --- | +| `SKILL.md` | Setup, `init` and `upgrade`, project layout, the dev loop, `scaffold.yml` essentials, calling contracts from your app, adding contracts, testnet and mainnet, diagnostics, and common pitfalls | +| `references/cli.md` | Every `stellar scaffold` command and flag | +| `references/config.md` | The full `scaffold.yml` schema, `.env`, contract metadata, and migrating from `environments.toml` | +| `references/frontend.md` | Generated clients, `@stellar-scaffold/app-lib`, wallets, and keeping the app and CLI on the same network | + +Agents read `SKILL.md` first and open a reference file only when a task needs it, so the skill costs little context until it's used. + +## Using the skill + +### Point your agent at it + +Any agent that can fetch a URL can use the skill directly. Start your session with: + +```text +Read https://raw.githubusercontent.com/stellar-scaffold/cli/main/skills/stellar-scaffold/SKILL.md +and follow it while you work on this project. +``` + +The agent follows the relative links in `SKILL.md` to the reference files when it needs them. This always gets the latest version, and needs no setup. + +### Install it for Claude Code + +Claude Code loads skills automatically from `~/.claude/skills/` (all your projects) or `.claude/skills/` (one project), and decides when to use them from the skill's description. Copy the skill folder into one of those: + +```bash +git clone --depth 1 https://github.com/stellar-scaffold/cli.git /tmp/stellar-scaffold-cli +mkdir -p ~/.claude/skills +cp -R /tmp/stellar-scaffold-cli/skills/stellar-scaffold ~/.claude/skills/ +``` + +Then ask for Scaffold work in plain language, such as "create a Scaffold project with a Svelte frontend" or "add a token contract and call it from the app", and Claude Code loads the skill when it's relevant. + +### Other agents + +Agents that support the Agent Skills format load them from their own skills directory. Copy the same `skills/stellar-scaffold` folder there; check your agent's documentation for the location. For agents without skill support, use the URL prompt above, or add it to your project's `AGENTS.md` so every session starts with it: + +```markdown +## Stellar Scaffold + +This project uses Stellar Scaffold. Before changing contracts, `scaffold.yml`, or contract calls in the app, read https://raw.githubusercontent.com/stellar-scaffold/cli/main/skills/stellar-scaffold/SKILL.md +``` + +### Keeping it current + +The skill tracks the CLI's `main` branch, which can be slightly ahead of the latest release. An installed copy doesn't update itself, so re-copy it when you upgrade `stellar-scaffold-cli`, or use the URL approach to always read the current version. + +## Related skills + +The Scaffold skill covers Scaffold itself and links out for neighboring tasks: + +- **Stellar Registry**: publishing and deploying contracts by name, and calling registry contracts from Rust. Skill: https://raw.githubusercontent.com/stellar-registry/cli/main/skills/stellar-registry/SKILL.md +- **Stellar development in general**: [stellar/stellar-dev-skill](https://github.com/stellar/stellar-dev-skill) indexes skills for the wider Stellar ecosystem, including Soroban, the SDKs, and wallets. + +## Feedback + +If an agent gets something wrong while using the skill, [open an issue](https://github.com/stellar-scaffold/cli/issues) with the prompt and what it did. Fixes reach the URL as soon as they merge, and installed copies the next time you re-copy the folder. diff --git a/docs/site/sidebars.ts b/docs/site/sidebars.ts index c824622e..8709df1d 100644 --- a/docs/site/sidebars.ts +++ b/docs/site/sidebars.ts @@ -35,6 +35,7 @@ const sidebars: SidebarsConfig = { "extensions", "registry", "deploy", + "agent-skills", ], };