# Idempotency & redeployment

`getOrDeploy` declares desired state: *this contract should exist on this network.* A recorded deployment is reused with no transaction; whether a **change** triggers a redeploy is set by `redeploymentStrategy`.

## Strategies

| Strategy    | Behavior                                                                             |
| ----------- | ------------------------------------------------------------------------------------ |
| `on-change` | **Default.** Reuse the record unless the deploy identity changed, then redeploy.     |
| `never`     | Always reuse an existing record; warn on drift. Deploy only when there is no record. |
| `always`    | Redeploy on every call — new address and tx.                                         |

Set it per call, or as a config default (globally or [per chain](/guides/configuration#options)):

```ts
await getOrDeployToken({ ...clients, args: [owner], redeploymentStrategy: "always" });
```

## Deploy identity

`on-change` compares the **deploy identity**, not the source:

> identity = metadata-stripped **runtime bytecode** + **constructor args** + **linked library addresses**

* A comment-only recompile changes solc's trailing metadata hash but not the identity — **no redeploy** (the CBOR metadata is stripped before comparing).
* Changing a constructor arg **does** redeploy — including when that arg is a dependency's address. Redeploy a token and the vault that took its address redeploys too: **changes cascade through the stack**.
* A changed linked-library address redeploys as well.

Every comparison is canonical, never textual. Args are compared by their ABI encoding, so `1`, `1n`, and the `"1"` a record's JSON stores are one value, and an address matches in either casing — representation drift alone never redeploys.

### What the cascade does not cover

The cascade follows **constructor args**, because that is what the identity contains. Two things it cannot see:

* **Addresses installed after deployment.** If a contract learns its dependency through `setToken(addr)` rather than a constructor arg, redeploying that dependency does not move the dependent's identity. You have to call the setter yourself.
* **State.** A redeploy is a new, empty contract — balances, ownership, and storage stay with the old one.

## Choosing a strategy

`on-change` is built for the loop where contracts are still moving: edit, re-run the script, and what actually changed redeploys. Nothing is silent — every redeploy is logged loudly and appended to the record's [history](/concepts/deployment-records#history) with its reason (`contract bytecode changed`, `constructor args changed from […] to […]`, …), so the git diff says what moved and why.

On a chain carrying **live state or real users**, pin `never`:

```ts
export default defineConfig({
  redeploymentStrategyByChainId: { [mainnet.id]: "never" },
});
```

A redeploy does not destroy the old contract — it keeps its address, balances, and storage. But your record now names the new one, so anything still holding the old address (another deployed contract, a subgraph, a cached frontend build) is talking to a different contract than your app is. Undoing that means redeploying or rewiring the rest of the system, which is why the choice belongs in config per chain rather than in the moment.

## Stale-record warning (`never`)

Under `never`, if the compiled bytecode or constructor args differ from the record, deployoor **warns** and reuses it — so drift never passes silently. Switch to `on-change` (the default) or `always` to act on it, or `reset()` to forget the record first.

## External vs deployed

`register` records an address you did not deploy. It will not overwrite a real deployment at the same `(chain, deploymentName)` — reset first or pick a different name. External records are never redeployed.
