> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rhinestone.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# One-time use

> Limit a session to one settlement transaction per participating chain.

`oneTimeUse` pins a one-time-use ID on a session. The session can then settle at most **one settlement transaction per participating chain**. It does not limit the session to one operation.

Examples on this page use `@rhinestone/sdk`, where `rhinestone` is the `RhinestoneSDK` instance from [Create a session with a custom setup](/wallets/session-keys/custom-setup/create-a-session).

```ts {8,17-20} theme={null}
import { bytesToBigInt, parseUnits } from "viem";
import { arbitrum, base } from "viem/chains";

const usdcOnBase = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
const usdcOnArbitrum = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";

// A fresh, random, non-zero 256-bit ID for every session.
const id = bytesToBigInt(crypto.getRandomValues(new Uint8Array(32)));

const session = await rhinestone.createSession({
  chain: base,
  owners: {
    type: "ecdsa",
    accounts: [sessionOwnerAccount],
  },
  account: rhinestoneAccount.getAddress("evm"),
  oneTimeUse: {
    id,
    validUntil: new Date(Date.now() + 24 * 60 * 60 * 1000),
  },
  crossChainPermits: [
    {
      from: { chain: base, token: usdcOnBase, maxAmount: parseUnits("100", 6) },
      to: { chain: arbitrum, token: usdcOnArbitrum },
    },
  ],
});
```

With `oneTimeUse`, the permit's `maxAmount` caps that one settlement instead of acting as a lifetime budget.

## How it works

* Every intent the SDK prepares for the session burns the ID first, on each chain it settles on. The policy refuses any settlement that does not burn it.
* Intents need an explicit `source`.
* `validUntil` is optional. When set, it must be a future `Date` and bounds every action in the session. Permit, swap, and function windows stay separate policies.
* `OFT` and `LZ` [settlement layers](/wallets/session-keys/custom-setup/policies/cross-chain#settlement-layers) require `oneTimeUse`.
* A one-time-use session always gets its own salted permission ID.

## Policy availability

The SDK uses the deployed OneTimeUseIdPolicy for the session's chain. Its addresses are exported as `ONE_TIME_USE_ID_POLICY_ADDRESS` and `ONE_TIME_USE_ID_POLICY_ADDRESS_DEV` (for `useDevContracts`) from `@rhinestone/sdk/smart-sessions`.

On a chain without the policy, creating the session throws:

```text theme={null}
oneTimeUse: no OneTimeUseIdPolicy is deployed on chain <chainId>; pass its address as policyAddresses.oneTimeUseId
```

## Signing

Leave `signing` unset. Unless the session holds a Permit2-layer permit such as `ACROSS`, which compiles claim policies, it has no signing surface and refuses a non-disabled `signing` mode. See [Session signing](/wallets/session-keys/custom-setup/session-signing).

## Use the session

Name the funding `source` explicitly and sign with the session:

```ts {2,9-12} theme={null}
const prepared = await rhinestoneAccount.prepareTransaction({
  source: { chain: base, token: usdcOnBase },
  destination: {
    chain: arbitrum,
    token: usdcOnArbitrum,
    amount: parseUnits("10", 6),
  },
  sponsored: true,
  signers: {
    type: "session",
    session,
  },
});
const signed = await rhinestoneAccount.signTransaction(prepared);
const result = await rhinestoneAccount.submitTransaction(signed);
```

Enable the session first, as described in [Use a session](/wallets/session-keys/custom-setup/use-a-session). After the ID is burned on a chain, create a new session with a fresh ID for the next settlement there.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.