diff --git a/README.md b/README.md index d91269a..3fac5af 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,10 @@ scripts/deploy_local.sh # or stellar-cli directly Tear down with `docker-compose down` (`-v` to remove data). +For the full walkthrough — generating a signing identity, building WASM, +deploying each contract to the sandbox, and troubleshooting — see +[`docs/LOCAL_SANDBOX_SETUP.md`](docs/LOCAL_SANDBOX_SETUP.md). + ### Development tasks Uses [`just`](https://github.com/casey/just) for common commands: diff --git a/docs/LOCAL_SANDBOX_SETUP.md b/docs/LOCAL_SANDBOX_SETUP.md new file mode 100644 index 0000000..23659d6 --- /dev/null +++ b/docs/LOCAL_SANDBOX_SETUP.md @@ -0,0 +1,204 @@ +# Local Stellar Sandbox Setup + +This walks through running a local Stellar **standalone network** ("sandbox") for +building, deploying, and exercising the `invoice`, `treasury`, and `compliance` +contracts without touching testnet or mainnet. + +Use this when you want to: + +- Deploy freshly built WASM and invoke contracts against a real ledger, without + testnet funding or network latency. +- Reproduce a bug that only shows up once contracts are actually deployed (as + opposed to `cargo test`, which runs entirely in-process against + `soroban-sdk`'s test host and never touches a network). + +`cargo test` does **not** require the sandbox — the contract test suites run +against the in-process Soroban test environment. Contracts CI +(`.github/workflows/contracts-ci.yml`) only runs `cargo fmt`, `cargo clippy`, +`cargo test`, and a release WASM build; it never starts a network. The sandbox +described here is for manual/local integration work only. + +## Prerequisites + +- **Rust**, pinned by [`rust-toolchain.toml`](../rust-toolchain.toml) + (`1.95.0`, with the `wasm32-unknown-unknown` target and the `rustfmt` / + `clippy` components). If you have `rustup` installed, running any `cargo` + command from the repo root auto-installs the pinned toolchain — no manual + step needed. +- **Soroban CLI**, matching the `soroban-sdk = "20.0.0"` dependency pinned in + the workspace [`Cargo.toml`](../Cargo.toml): + + ```sh + cargo install soroban-cli + ``` + + (This is the same command the dev container runs — see + [`.devcontainer/post-create.sh`](../.devcontainer/post-create.sh).) +- **Docker** and **Docker Compose**, to run the standalone network defined in + [`docker-compose.yml`](../docker-compose.yml). + +## 1. Start the sandbox + +The repo's [`docker-compose.yml`](../docker-compose.yml) already defines a +local standalone network using the official `stellar/quickstart` image, plus +Redis (used by the backend's webhook delivery, not required for +contract-only work): + +```sh +docker-compose up -d +docker-compose ps +``` + +Wait for the `soroban` service to report healthy, then confirm it's serving: + +```sh +curl http://localhost:8000/health +``` + +This gives you: + +- Horizon / Soroban RPC on `http://localhost:8000` +- Stellar Core on ports `11626` (HTTP) / `11625` (peer) + +Tear it down later with `docker-compose down` (add `-v` to also drop the +`soroban_data` / `redis_data` volumes and reset ledger state). + +## 2. Configure your environment + +Copy the local env template — its defaults already match the sandbox started +above: + +```sh +cp .env.local.example .env.local +``` + +[`.env.local.example`](../.env.local.example) sets: + +```sh +SOROBAN_RPC_HOST=http://localhost:8000 +SOROBAN_NETWORK_PASSPHRASE=Standalone Network ; February 2025 +``` + +Fill in `CONTRACT_ADMIN_KEY` once you've generated a local identity in the +next step. + +## 3. Create a local signing identity + +Generate a keypair and fund it via the sandbox's built-in friendbot: + +```sh +soroban config identity generate dev +soroban config identity fund dev --rpc-url http://localhost:8000 \ + --network-passphrase "Standalone Network ; February 2025" +``` + +Grab the public key to use as `CONTRACT_ADMIN_KEY` in `.env.local`: + +```sh +soroban config identity address dev +``` + +## 4. Build the contracts + +```sh +cargo build --target wasm32-unknown-unknown --release +``` + +This produces one `.wasm` per contract (crate `[lib] name`, not the +`stargate-*` package name) under +`target/wasm32-unknown-unknown/release/`: + +- `invoice.wasm` +- `treasury.wasm` +- `compliance.wasm` + +## 5. Deploy to the sandbox + +There is no scripted end-to-end deploy for a standalone network today — the +checked-in `scripts/deploy_testnet.sh` targets testnet specifically, and +mainnet deploys are intentionally blocked (see +[`docs/MAINNET_DEPLOYMENT.md`](./MAINNET_DEPLOYMENT.md)). Deploy each +contract directly with the Soroban CLI against the sandbox: + +```sh +INVOICE_CONTRACT_ID=$(soroban contract deploy \ + --wasm target/wasm32-unknown-unknown/release/invoice.wasm \ + --source dev \ + --rpc-url http://localhost:8000 \ + --network-passphrase "Standalone Network ; February 2025") + +COMPLIANCE_CONTRACT_ID=$(soroban contract deploy \ + --wasm target/wasm32-unknown-unknown/release/compliance.wasm \ + --source dev \ + --rpc-url http://localhost:8000 \ + --network-passphrase "Standalone Network ; February 2025") + +TREASURY_CONTRACT_ID=$(soroban contract deploy \ + --wasm target/wasm32-unknown-unknown/release/treasury.wasm \ + --source dev \ + --rpc-url http://localhost:8000 \ + --network-passphrase "Standalone Network ; February 2025") + +echo "invoice=$INVOICE_CONTRACT_ID treasury=$TREASURY_CONTRACT_ID compliance=$COMPLIANCE_CONTRACT_ID" +``` + +Once you have all three IDs, you can record them the same way the testnet +flow does, via [`scripts/export_deployed_addresses.sh`](../scripts/export_deployed_addresses.sh) +(writes `artifacts/addresses.json`): + +```sh +STELLAR_NETWORK=standalone \ +INVOICE_CONTRACT_ID=$INVOICE_CONTRACT_ID \ +TREASURY_CONTRACT_ID=$TREASURY_CONTRACT_ID \ +COMPLIANCE_CONTRACT_ID=$COMPLIANCE_CONTRACT_ID \ +scripts/export_deployed_addresses.sh +``` + +## 6. Invoke a contract + +Sanity-check a deployment by invoking a read-only function, e.g. the +compliance contract's `is_allowed`: + +```sh +soroban contract invoke \ + --id "$COMPLIANCE_CONTRACT_ID" \ + --source dev \ + --rpc-url http://localhost:8000 \ + --network-passphrase "Standalone Network ; February 2025" \ + -- \ + is_allowed --address "$(soroban config identity address dev)" +``` + +## Running the test suite + +Unit/integration tests do not need the sandbox running: + +```sh +cargo test +scripts/coverage.sh # coverage/index.html + LCOV +``` + +## Troubleshooting + +- **`docker-compose ps` shows `soroban` unhealthy**: give it the full + `start_period` (30s) before checking; `stellar/quickstart` takes a few + ledger closes to come up. Check logs with `docker-compose logs soroban`. +- **`soroban contract deploy` fails with a connection error**: confirm + `curl http://localhost:8000/health` succeeds first, and that + `--rpc-url`/`--network-passphrase` exactly match the values in + `.env.local.example`. +- **Identity has no funds**: re-run + `soroban config identity fund dev --rpc-url http://localhost:8000 --network-passphrase "Standalone Network ; February 2025"` + — the sandbox's friendbot only funds on request, it doesn't pre-fund + identities. +- **Stale ledger state / weird contract errors after repeated runs**: tear + down with `docker-compose down -v` to drop the `soroban_data` volume and + start from a clean ledger. + +## Further reading + +- [`docs/dev-environment.md`](./dev-environment.md) — full-stack setup + spanning this repo, `stargate-backend`, and `stargate-frontend`. +- [`docs/MAINNET_DEPLOYMENT.md`](./MAINNET_DEPLOYMENT.md) — the mainnet + signing ceremony (does not apply locally). +- [Soroban CLI docs](https://developers.stellar.org/docs/tools/developer-tools/cli/soroban-cli)