Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
204 changes: 204 additions & 0 deletions docs/LOCAL_SANDBOX_SETUP.md
Original file line number Diff line number Diff line change
@@ -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)
Loading