# Environments

The frozen configuration object that tells the SDK where to connect, and why it is fingerprinted.

A `PolyesterEnvironment` describes one Polyester deployment: API, WebSocket, RPC, chain, account-abstraction infra, and venue contracts. Every client, signer, and session is created against an environment.

## Built-in environments

```ts
import { POLYESTER_DEVNET_ENVIRONMENT, POLYESTER_TESTNET_ENVIRONMENT } from "@polyester/sdk";
```

| Field          | Devnet                             | Testnet                             |
| -------------- | ---------------------------------- | ----------------------------------- |
| `name`         | `polyester-devnet`                 | `polyester-testnet`                 |
| `apiUrl`       | `https://api.devnet.polyester.com` | `https://api.testnet.polyester.com` |
| `websocketUrl` | `wss://api.devnet.polyester.com`   | `wss://api.testnet.polyester.com`   |
| `rpcUrl`       | `https://rpc.polyester.tech`       | `https://rpc.polyester.live`        |
| `chain.id`     | `888168`                           | `888169`                            |
| Block explorer | `https://devnet.polyesterscan.com` | `https://testnet.polyesterscan.com` |

Both presets include Multicall3 for batched contract reads. Devnet uses `0xF35A6AE5408fa1356064849D0BC3855f801aa6aC` (block 563457); testnet uses `0xfc9B0991DC84E419C9b164dE89795958a5B0A0cF` (block 179823).

## Defining an environment

```ts
import { createPolyesterEnvironment, POLYESTER_DEVNET_ENVIRONMENT } from "@polyester/sdk";

const environment = createPolyesterEnvironment({
	name: "my-environment",
	apiUrl: "https://api.example.com",
	websocketUrl: "wss://api.example.com",
	rpcUrl: "https://rpc.example.com",
	chain: POLYESTER_DEVNET_ENVIRONMENT.chain,
	accountAbstraction: POLYESTER_DEVNET_ENVIRONMENT.accountAbstraction,
	contracts: POLYESTER_DEVNET_ENVIRONMENT.contracts,
});
```

`createPolyesterEnvironment` validates and freezes the result:

- URLs must parse with the right scheme. Plain `http:` / `ws:` only for `localhost`.
- `apiUrl` must not carry query parameters.
- Addresses are checksummed. Invalid ones throw `ConfigurationError`.
- Entry point version must be `0.7`.
- The chain's default RPC is rewritten to `rpcUrl`, so viem clients derived from the environment agree with it.

Misconfiguration fails at startup with a precise message, not at request time.

## Why account abstraction is in here

Polyester accounts are Safe smart accounts. The `accountAbstraction` block pins the Safe deployment used to derive account addresses (proxy factory, singleton, 4337 module, and related addresses).

The account signer and smart-account helpers read those fields. Same owner key always maps to the same Polyester account. That is why this block lives on the environment, not as a helper option.

## The fingerprint

Every environment gets a `fingerprint`: a hash of the RPC URL, chain ID, account-abstraction configuration, and venue contracts. API and WebSocket URLs are excluded, so switching regional gateways for the same deployment preserves signer and session identity.

The SDK uses it to fail fast:

- An `AccountSigner` records the fingerprint it was derived for. The browser client refuses a signer from a different environment.
- Stored sessions are fingerprint-bound. Changing the chain, RPC, account-abstraction config, or contracts changes that identity; changing only API or WebSocket gateways does not.
- Smart-account instances are checked against the client's environment before sending user operations.

Treat environments as singletons: define once, import everywhere. Frozen and validated, so sharing is safe.
