> For the complete documentation index, see [llms.txt](https://lumagg.gitbook.io/lumagg/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lumagg.gitbook.io/lumagg/on-chain-products/limit-orders-testnet.md).

# Limit Orders on Testnet

**Network:** Stellar **testnet only**. Do not point these scripts at mainnet.

Scripts refuse `Public Global Stellar Network` (mainnet) passphrases.

## Prerequisites

* [stellar CLI](https://developers.stellar.org/docs/tools/cli)
* Funded testnet identity (Friendbot) registered locally, e.g. `stellar keys add admin --secret-key …`
* Working directory: repo root

## Deploy

One shot (aggregator + escrow + env file):

```bash
ADMIN=admin ADMIN_G=G... ./scripts/deploy-limit-testnet.sh
```

Or stepwise:

```bash
ADMIN=admin ADMIN_G=G... ./contracts/aggregator/deploy-testnet.sh
# optional: AGGREGATOR=C... to reuse an existing testnet aggregator
AGGREGATOR=C... ADMIN=admin ADMIN_G=G... ./contracts/order-escrow/deploy-testnet.sh
```

### Register venues before enabling fills

The patched escrow calls `aggregator.swap_restricted`. Register every pool address that the keeper may use, together with its exact `DexType`, before starting live fills:

```bash
export AGGREGATOR=C...
export ADMIN=admin
export AQUARIUS_POOL=C...

stellar contract invoke \
  --id "$AGGREGATOR" \
  --source "$ADMIN" \
  --network lumagg-testnet \
  -- \
  set_venue --dex-type Aquarius --dex-id "$AQUARIUS_POOL" --allowed true

stellar contract invoke \
  --id "$AGGREGATOR" \
  --source "$ADMIN" \
  --network lumagg-testnet \
  --send=no \
  -- \
  is_venue --dex-type Aquarius --dex-id "$AQUARIUS_POOL"
```

Repeat this for Soroswap pairs, Phoenix pools, Sushi pools, and Comet venues as applicable. Do not register an address solely because it implements a compatible ABI; verify the address against the intended DEX deployment and record the registration transaction. An unregistered route is rejected before the escrow transfers any input token.

Artifacts (gitignored):

| Path                                          | Contents                       |
| --------------------------------------------- | ------------------------------ |
| `contracts/aggregator/.testnet-aggregator-id` | Aggregator `C…`                |
| `contracts/order-escrow/.testnet-escrow-id`   | Escrow `C…`                    |
| `deploy/.env.limit-testnet.local`             | Env for API / indexer / keeper |

Default RPC: `https://soroban-testnet.stellar.org`\
Passphrase: `Test SDF Network ; September 2015`

## Point services at testnet

The preferred keeper deployment uses a TOML file and a separate secret file:

```bash
cp packaging/lumagg-limit-keeper.toml limit-keeper-testnet.toml
sed -i 's/network = "public"/network = "testnet"/' limit-keeper-testnet.toml
sed -i 's#https://your-stellar-rpc.example.com#https://soroban-testnet.stellar.org#' limit-keeper-testnet.toml
# Set escrow_contract, aggregator_contract, quote_api_url, and cursor_path.
./target/release/limit-keeper --config limit-keeper-testnet.toml
```

Use `dry_run = true` while validating discovery and fillability. For live testnet fills, set `dry_run = false`, create the file referenced by `secret_file` with mode `0600`, and keep it outside Git. The deployed testnet systemd service uses the TOML configuration directly; the environment-variable form below is retained only for local compatibility.

```bash
set -a && source deploy/.env.limit-testnet.local && set +a

# lumagg-analytics-indexer (polls escrow events into indexer.db_path)
# api-server (GET /orders + build_create/build_cancel use indexer.db_path + features.escrow_contract)
# limit-keeper (KEEPER_NETWORK=testnet, ESCROW_CONTRACT, AGGREGATOR_CONTRACT)
```

Use **testnet** token contract ids and a user account that exists on testnet for builds. The currently deployed Soroswap testnet route uses `CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC` (`native` / UI label `XLM`) and `CB3TLW74NBIOT3BUWOZ3TUM6RFDF6A4GVIRUQRQZABG5KPOUL4JJOV2F` (`USDC`). The intermediate bridge asset is `CCZGLAUBDKJSQK72QOZHVU7CUWKW45OZWYWCLL27AEK74U2OIBK6LXF2` (`XTAR`). These values are specific to the current testnet deployment and must not be reused on mainnet.

Current testnet Escrow deployment (with structured validation errors): `CCI3U3P7MPZNCA5L7KWTXNS7H7KV6AIZQ6ZY2FEOZPHTJIAVCRYPKXTM`. The previous Escrow IDs below are retained as historical evidence only. For example, `create_dca` with a past start ledger now returns contract error `#17` (`StartLedgerInPast`) during simulation.

On the server, copy the generated env to `/opt/stellar-dex-aggregator/deploy/.env.limit-testnet.local`, then install and start the API and indexer units. The keeper starts in dry-run mode by default:

```bash
RESET_TESTNET_DB=1 ./ops/deploy_limit_testnet_server.sh
```

The deployment uses separate `*-testnet` binary names, a Soroswap testnet worker, and Redis DB 15. It does not replace the running mainnet API, worker, or indexer binaries. Before the first deployment, create the server-only Redis configuration:

```bash
sudo install -d -m 700 /etc/lumagg
sudo sh -c 'umask 077; printf "%s\n" \
  "SNAPSHOT_REDIS_URL=redis://default:PASSWORD@127.0.0.1:6379/15" \
  > /etc/lumagg/testnet-redis.env'
```

`RESET_TESTNET_DB=1` backs up the old testnet database and keeper cursor before starting from the newly deployed Escrow. For manual installation:

```bash
sudo install -m 644 deploy/lumagg-api-testnet.service /etc/systemd/system/
sudo install -m 644 deploy/lumagg-indexer-testnet.service /etc/systemd/system/
sudo install -m 644 deploy/lumagg-limit-keeper-testnet.service /etc/systemd/system/
sudo install -m 644 deploy/lumagg-worker-testnet.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now lumagg-worker-testnet lumagg-api-testnet lumagg-indexer-testnet
sudo systemctl enable --now lumagg-limit-keeper-testnet
```

For live fills, place the funded keeper seed only on the server:

```bash
sudo install -d -m 700 /etc/lumagg
sudo sh -c 'umask 077; printf "%s\n" \
  "S..." \
  > /etc/lumagg/limit-keeper-testnet.secret'
# Edit /opt/stellar-dex-aggregator/deploy/limit-keeper-testnet.toml:
#   dry_run = false
sudo systemctl restart lumagg-limit-keeper-testnet
```

Never add `KEEPER_SECRET` to the generated shared env or repository.

## Smoke checklist

The unsigned transaction builders can be checked without a wallet or chain write using:

```bash
API=http://127.0.0.1:3200 USER_G=G... ./scripts/limit-dca-testnet-smoke.sh
```

The script obtains the current testnet ledger, uses future ledger bounds, and verifies that both Limit and DCA endpoints return non-empty unsigned XDR. It also sends one deliberately invalid DCA request with a past start ledger and requires the structured `Error(Contract, #17)` response. It does not sign or submit any transaction.

For the Protocol 28 quote regression, use the testnet reverse-proxy path:

```bash
API=https://api.lumagg.xyz/limit-testnet ./scripts/p28-testnet-smoke.sh
```

| # | Step                                                        | Expect                                         |
| - | ----------------------------------------------------------- | ---------------------------------------------- |
| 1 | Deploy scripts complete                                     | Two `C…` ids; explorer links under `/testnet/` |
| 2 | Start indexer + api-server with env snippet                 | No mainnet defaults                            |
| 3 | Register and verify every testnet venue                     | `is_venue` returns `true`                      |
| 4 | `POST /api/v1/orders/build_create` with testnet user/tokens | `unsigned_tx_xdr`                              |
| 5 | Sign + submit on testnet                                    | `order_created` on escrow                      |
| 6 | Wait for indexer poll → `GET /api/v1/orders?user=G…`        | Open order listed                              |
| 7 | Optional: `POST .../build_cancel` or keeper dry-run         | Cancel XDR / dry-run fill log                  |
| 8 | Create DCA and run keeper after its due ledger              | One chunk fills and next ledger advances       |

**Verified live fill (2026-07-31):**

* Escrow: `CDCNJOKHKC7HG5A46RKG7QSNBIU3ES2A5PFOYPORJTUQKP4WXMX4OFD6`
* Aggregator: `CDJI26DXFQ4MD7VICA3Q6NEGWF53A3Z6IK7WTNMQ6UZUHL5XGQMEKJRE`
* DCA fill transaction: [`64f23f734397e88c17fc57dee91dcfdec7636c9a1a00f5a363ef6aa7657b689c`](https://stellar.expert/explorer/testnet/tx/64f23f734397e88c17fc57dee91dcfdec7636c9a1a00f5a363ef6aa7657b689c)
* Route: XLM → XTAR → test-USDC across two Soroswap pairs
* Result: `500000` input, `326666` output, `500000` remaining; the remainder was cancelled and refunded in transaction [`ac487d9f416f19c34a449cf6e8114eeb6e3a98ac1fa080b66e76492a37ef93eb`](https://stellar.expert/explorer/testnet/tx/ac487d9f416f19c34a449cf6e8114eeb6e3a98ac1fa080b66e76492a37ef93eb).
* Post-test balances: Escrow XLM/test-USDC = `0`/`0`; Aggregator XLM/test-USDC = `0`/`0`.

**Verified restricted fill after venue-authentication fix (2026-08-20):**

* Aggregator: `CDJI26DXFQ4MD7VICA3Q6NEGWF53A3Z6IK7WTNMQ6UZUHL5XGQMEKJRE`
* Escrow: `CDHPKN4XDD3VLTWFMKTL46JKDRXOIQYA75LX77ENY4WPQLKIFYWM66M6`
* Route: testnet XLM → XTAR → test-USDC across two administrator-registered Soroswap pairs.
* Input/output: `500000` / `302763` token units.
* The transaction executed through `swap_restricted`, emitted both `leg` events, and completed `dca_filled`: [`df2c468895b698d605786afe11f652af5c057addd5dc95caf54f4ca0e9e27417`](https://stellar.expert/explorer/testnet/tx/df2c468895b698d605786afe11f652af5c057addd5dc95caf54f4ca0e9e27417).

The previous Escrow deployment predates the `upgrade` entrypoint and cannot be upgraded in place; future testnet fixes should use a new Escrow deployment or an Escrow instance that includes the upgrade entrypoint from deployment.

## Out of scope

* Mainnet deploy of aggregator/escrow for limits
* Changing production `api.lumagg.xyz`

## Frontend (Phase 3d)

On `/`, switch Order rail to **Limit** or **DCA**. DCA supports a total amount, fixed chunk, hourly/6-hour/daily frequency, and an optional minimum execution price. Its API surface is `/api/v1/dca`, `/dca/build_create`, and `/dca/build_cancel` under the same `/api/v1` prefix.

| Piece        | Value                                                                                                  |
| ------------ | ------------------------------------------------------------------------------------------------------ |
| Frontend env | `NEXT_PUBLIC_LIMIT_API_URL=https://api.lumagg.xyz/limit-testnet`                                       |
| Nginx        | `api.lumagg.xyz/limit-testnet/` → `127.0.0.1:3200`                                                     |
| systemd      | `lumagg-worker-testnet`, `lumagg-api-testnet`, `lumagg-indexer-testnet`, `lumagg-limit-keeper-testnet` |
| Escrow       | Read from `deploy/.env.limit-testnet.local` after each deployment                                      |

Wallet must be on **Testnet** when signing create/cancel. Instant still uses `NEXT_PUBLIC_API_URL` (mainnet).

Local: `packages/frontend/.env.local`. Deploy UI: `./ops/deploy_site.sh`. For the structured contract error code mapping, see [Order Escrow Errors](/lumagg/on-chain-products/order-escrow-errors.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://lumagg.gitbook.io/lumagg/on-chain-products/limit-orders-testnet.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
