> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://v3.viem.sh/api/mcp` to find what you need.

# Deposits

## Overview

An OP Stack deposit starts with a transaction to the Optimism Portal on L1. The portal emits a `TransactionDeposited` event, which the L2 derives into a deposit transaction with type `0x7e`.

Viem exposes the cross-layer workflow through [`Actions.l2.buildDepositTransaction`](/op-stack/actions/l2/buildDepositTransaction) and [`Actions.l1.depositTransaction`](/op-stack/actions/l1/depositTransaction). The `Deposit` namespace extracts deposit logs and derives L2 transaction hashes. `TxEnvelopeDeposit` handles the typed deposit envelope directly.

Deposit one Ether from Mainnet to OP Mainnet by preparing the L2 request, submitting it on L1, and following the derived transaction to L2.

:::code-group
```ts twoslash [deposit.ts]
import { Deposit } from 'viem/op-stack'
import { Value } from 'viem/utils'
import { account, l1Client, l2Client } from './viem.config'

const deposit = await l2Client.deposit.buildDepositTransaction({
  account,
  mint: Value.fromEther('1'),
  to: account.address,
})

const hash = await l1Client.deposit.depositTransaction(deposit)
const l1Receipt = await l1Client.transaction.waitForReceipt({ hash }).receipt

const [l2Hash] = Deposit.getL2TransactionHashes({
  logs: l1Receipt.logs,
})
if (!l2Hash) throw new Error('Deposit transaction not found.')

const l2Receipt = await l2Client.transaction.waitForReceipt({
  hash: l2Hash,
}).receipt
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/op-stack/deposit.config.ts:setup]
```
:::

## Steps

Follow these steps to execute and track the deposit across both chains.

::::steps
### Set Up the Clients

Create an L1 Client with an Account and an L2 Client for OP Mainnet. Extend both with [Public Actions](/docs/actions/public) for receipt tracking and their corresponding OP Stack Actions.

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/op-stack/deposit.config.ts:setup]
```

### Build the Deposit Transaction

Use [`l2.buildDepositTransaction`](/op-stack/actions/l2/buildDepositTransaction) to prepare the L2 transaction request. Here, one Ether is deposited to the sending Account.

:::code-group
```ts twoslash [deposit.ts]
import { Value } from 'viem/utils'
import { account, l2Client } from './viem.config'

const deposit = await l2Client.deposit.buildDepositTransaction({ // [!code focus]
  account, // [!code focus]
  mint: Value.fromEther('1'), // [!code focus]
  to: account.address, // [!code focus]
}) // [!code focus]
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/op-stack/deposit.config.ts:setup]
```
:::

:::info
`mint` is credited on L2 and debited from the Account's L1 balance. `to` can be any L2 recipient.
:::

### Submit the Deposit on L1

Pass the prepared deposit to [`l1.depositTransaction`](/op-stack/actions/l1/depositTransaction). It already contains the L2 request, Account, and target chain.

:::code-group
```ts twoslash [deposit.ts]
import { Value } from 'viem/utils'
import { account, l1Client, l2Client } from './viem.config'

const deposit = await l2Client.deposit.buildDepositTransaction({
  account,
  mint: Value.fromEther('1'),
  to: account.address,
})

const hash = await l1Client.deposit.depositTransaction(deposit) // [!code focus]
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/op-stack/deposit.config.ts:setup]
```
:::

### Wait for the L1 Receipt

Use [`transaction.waitForReceipt`](/docs/actions/public/transaction/waitForReceipt) to wait until the portal transaction is included on L1.

:::code-group
```ts twoslash [deposit.ts]
import { Value } from 'viem/utils'
import { account, l1Client, l2Client } from './viem.config'

const deposit = await l2Client.deposit.buildDepositTransaction({
  account,
  mint: Value.fromEther('1'),
  to: account.address,
})
const hash = await l1Client.deposit.depositTransaction(deposit)

const l1Receipt = await l1Client.transaction.waitForReceipt({ // [!code focus]
  hash, // [!code focus]
}).receipt // [!code focus]
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/op-stack/deposit.config.ts:setup]
```
:::

:::info
Once the L1 transaction is included, the deposited Ether has been debited from the Account's L1 balance.
:::

### Derive the L2 Transaction Hash

Use `Deposit.getL2TransactionHashes` with the L1 receipt logs to derive the transaction created on L2.

:::code-group
```ts twoslash [deposit.ts]
import { Deposit } from 'viem/op-stack'
import { Value } from 'viem/utils'
import { account, l1Client, l2Client } from './viem.config'

const deposit = await l2Client.deposit.buildDepositTransaction({
  account,
  mint: Value.fromEther('1'),
  to: account.address,
})
const hash = await l1Client.deposit.depositTransaction(deposit)
const l1Receipt = await l1Client.transaction.waitForReceipt({ hash }).receipt

const [l2Hash] = Deposit.getL2TransactionHashes({ // [!code focus]
  logs: l1Receipt.logs, // [!code focus]
}) // [!code focus]
if (!l2Hash) throw new Error('Deposit transaction not found.')
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/op-stack/deposit.config.ts:setup]
```
:::

### Wait for the L2 Receipt

Wait for the derived transaction to be included on L2. Once it resolves, the recipient has the deposited Ether.

:::code-group
```ts twoslash [deposit.ts]
import { Deposit } from 'viem/op-stack'
import { Value } from 'viem/utils'
import { account, l1Client, l2Client } from './viem.config'

const deposit = await l2Client.deposit.buildDepositTransaction({
  account,
  mint: Value.fromEther('1'),
  to: account.address,
})
const hash = await l1Client.deposit.depositTransaction(deposit)
const l1Receipt = await l1Client.transaction.waitForReceipt({ hash }).receipt

const [l2Hash] = Deposit.getL2TransactionHashes({
  logs: l1Receipt.logs,
})
if (!l2Hash) throw new Error('Deposit transaction not found.')

const l2Receipt = await l2Client.transaction.waitForReceipt({ // [!code focus]
  hash: l2Hash, // [!code focus]
}).receipt // [!code focus]
```

```ts twoslash [viem.config.ts] filename="viem.config.ts"
// [!include ~/snippets/op-stack/deposit.config.ts:setup]
```
:::
::::

## Recipes

Use these recipes for lower-level deposit data and envelope handling.

### Decode Deposit Event Data

Decode the opaque portal event payload into the values carried by the L2 transaction.

```ts twoslash
import { Deposit } from 'viem/op-stack'

const deposit = Deposit.opaqueDataToDepositData( // [!code focus]
  '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000520800',
) // [!code focus]
// @log: { data: '0x', gas: 21000n, isCreation: false, mint: 0n, value: 1n }
```

### Compute an Upgrade Deposit Source Hash

Upgrade deposits derive their source hash from a UTF-8 intent and the upgrade domain.

```ts twoslash
import { Deposit } from 'viem/op-stack'

const sourceHash = Deposit.getSourceHash({ // [!code focus]
  domain: 'upgradeDeposit', // [!code focus]
  intent: 'Interop: CrossL2Inbox Proxy Update', // [!code focus]
}) // [!code focus]
// @log: 0x88c6b48354c367125a59792a93a7b60ad7cd66e516157dbba16558c68a46d3cb
```

### Serialize a Deposit Envelope

Use `TxEnvelopeDeposit` when reading or writing the raw EIP-2718 envelope. OP Stack chain definitions call this serializer automatically for deposit envelopes.

```ts twoslash
import { TxEnvelopeDeposit } from 'viem/op-stack'
import { Value } from 'viem/utils'

const serialized = TxEnvelopeDeposit.serialize({ // [!code focus]
  from: '0x0000000000000000000000000000000000000001',
  gas: 21_000n,
  mint: Value.fromEther('1'),
  sourceHash:
    '0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
  to: '0x0000000000000000000000000000000000000002',
  type: 'deposit',
  value: Value.fromEther('1'),
}) // [!code focus]

const envelope = TxEnvelopeDeposit.deserialize(serialized) // [!code focus]
```

## `Deposit`

The `Deposit` namespace contains the request type and helpers for portal deposit events.

### Functions

| Function | Purpose |
| --- | --- |
| `Deposit.extractTransactionDepositedLogs` | Extracts typed `TransactionDeposited` events from L1 logs. |
| `Deposit.getL2TransactionHash` | Computes the derived L2 transaction hash for one deposit event. |
| `Deposit.getL2TransactionHashes` | Extracts all deposit events and computes their L2 hashes. |
| `Deposit.getSourceHash` | Computes the source hash for a user, L1 information, or upgrade deposit. |
| `Deposit.opaqueDataToDepositData` | Decodes the opaque portal event payload. |

### Types

| Type | Purpose |
| --- | --- |
| `Deposit.Request` | Describes the L2 request submitted through the portal. |
| `Deposit.TransactionDepositedLog` | Represents a decoded portal deposit event. |

## `TxEnvelopeDeposit`

The `TxEnvelopeDeposit` namespace models the OP Stack deposit transaction envelope.

### Functions

| Function | Purpose |
| --- | --- |
| `TxEnvelopeDeposit.assert` | Validates a deposit envelope. |
| `TxEnvelopeDeposit.deserialize` | Parses a serialized type `0x7e` envelope. |
| `TxEnvelopeDeposit.is` | Narrows an unknown value to a deposit envelope. |
| `TxEnvelopeDeposit.serialize` | Serializes a deposit envelope with its `0x7e` type prefix. |

### Constants and Types

| Export | Purpose |
| --- | --- |
| `TxEnvelopeDeposit.serializedType` | The serialized transaction prefix, `0x7e`. |
| `TxEnvelopeDeposit.type` | The native transaction discriminant, `deposit`. |
| `TxEnvelopeDeposit.TxEnvelopeDeposit` | The native envelope shape. |
| `TxEnvelopeDeposit.Serialized` | A serialized deposit transaction. |
