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

# Deposits

# Deposits

This guide covers depositing tokens from your wallet into your private balance.

## Basic Deposit

```kotlin theme={null}
try {
    val result = withContext(Dispatchers.IO) {
        sdk.shield(
            tokenAddress = "0x...token-address",
            amount = "1000000000000000000" // 1 token (18 decimals)
        )
    }
    println("Transaction hash: ${result.txHash}")
    println("Commitment: ${result.commitment}")
} catch (e: SDKError) {
    println("Deposit failed: $e")
}
```

## Deposit Parameters

| Parameter      | Type     | Required | Description                   |
| -------------- | -------- | -------- | ----------------------------- |
| `tokenAddress` | `String` | Yes      | ERC-20 token contract address |
| `amount`       | `String` | Yes      | Amount in wei (smallest unit) |

## Deposit Steps

When a deposit is submitted, the SDK executes these steps in order:

| Step        | Description                                     |
| ----------- | ----------------------------------------------- |
| Wrapping    | Wrapping ETH to WETH (if depositing ETH)        |
| Approving   | Approving token spending on the shield contract |
| Shielding   | Executing the deposit transaction               |
| Registering | Registering the deposit with the indexer        |
| Compliance  | Waiting for compliance verification             |

## Deposit Result

```kotlin theme={null}
data class ShieldResult(
    val txHash: String,      // Main deposit transaction hash
    val commitment: String,  // Note commitment
    val fee: String          // Fee paid (in wei)
)
```

## Depositing ETH

To deposit native ETH, use the zero address. The SDK automatically wraps ETH to WETH:

```kotlin theme={null}
val ethAddress = "0x0000000000000000000000000000000000000000"

val result = withContext(Dispatchers.IO) {
    sdk.shield(
        tokenAddress = ethAddress,
        amount = "1000000000000000000" // 1 ETH
    )
}
println("Deposit tx: ${result.txHash}")
```

The SDK automatically:

1. Wraps ETH to WETH
2. Approves WETH spending
3. Deposits WETH to the shield contract

## Parsing Amounts

Use helper functions to convert between human-readable and wei formats:

```kotlin theme={null}
// Parse human-readable amount to wei string
val weiAmount = sdk.parseAmount("1.5", decimals = 18u)
// Returns: "1500000000000000000"

// Format wei string to human-readable
val formatted = sdk.formatAmount("1500000000000000000", decimals = 18u)
// Returns: "1.5"

// For USDC (6 decimals)
val usdcWei = sdk.parseAmount("100.0", decimals = 6u)
// Returns: "100000000"
```

## Error Handling

```kotlin theme={null}
try {
    val result = withContext(Dispatchers.IO) {
        sdk.shield(tokenAddress = tokenAddress, amount = amount)
    }
} catch (e: SDKError) {
    when (e) {
        is SDKError.InsufficientBalance ->
            println("Not enough tokens in wallet")
        is SDKError.InvalidAmount ->
            println("Invalid amount format")
        is SDKError.WalletError ->
            println("Wallet error: ${e.message}")
        is SDKError.SignatureRejected ->
            println("User rejected the transaction")
        is SDKError.NetworkError ->
            println("Network error: ${e.message}")
        else ->
            println("Deposit error: $e")
    }
}
```

## Best Practices

### 1. Validate Amounts Before Depositing

```kotlin theme={null}
fun validateDepositAmount(amount: String, walletBalance: String) {
    val amountValue = amount.toBigIntegerOrNull()
        ?: throw SDKError.InvalidAmount
    require(amountValue > BigInteger.ZERO) { "Amount must be positive" }

    val balanceValue = walletBalance.toBigIntegerOrNull()
        ?: throw SDKError.InsufficientBalance("Invalid wallet balance")
    require(amountValue <= balanceValue) { "Insufficient wallet balance" }
}
```

### 2. Refresh Balance After Deposit

```kotlin theme={null}
val result = withContext(Dispatchers.IO) {
    sdk.shield(tokenAddress = tokenAddress, amount = amount)
}

// Refresh balance to reflect the deposit
val updatedBalance = withContext(Dispatchers.IO) {
    sdk.getBalance(tokenAddress = tokenAddress)
}
println("New shielded balance: ${updatedBalance.shieldedBalance}")
```

### 3. Always Use Background Dispatcher

SDK methods are blocking calls. Always wrap them in `withContext(Dispatchers.IO)` to avoid blocking the main thread:

```kotlin theme={null}
// In a ViewModel
viewModelScope.launch {
    val result = withContext(Dispatchers.IO) {
        sdk.shield(tokenAddress = token, amount = amount)
    }
    // Update UI on main thread
    _depositState.value = DepositState.Success(result.txHash)
}
```

## Relayed Deposits (build calldata, relay yourself)

If your users don't submit transactions directly — they hold funds at addresses
you relay for (a gasless relayer, an EIP-7702 session key, a custom pipeline) —
build the deposit calldata without submitting it, relay it through your own
infrastructure, then finalize from the mined receipt.

```kotlin theme={null}
// 1. Build the calldata (nothing submitted)
val prepared = withContext(Dispatchers.IO) {
    sdk.prepareShield(
        tokenAddress = "0x...",
        amount = "1000000000000000000",
        recipient = null  // pass a privacy address to shield to someone else
    )
}

// prepared.wrap / prepared.approve / prepared.shield are Call(to, value, data)
// values — relay them in order (wrap?, approve?, shield).
val calls = listOfNotNull(prepared.wrap, prepared.approve, prepared.shield)
val receipt = myRelayer.submit(calls)

// 2. Finalize from the mined receipt to recover the request id
val result = finalizeShield(
    receipt = receipt,
    shieldContract = prepared.finalize.shieldContract,
    commitment = prepared.commitment
)
println("Request ID: ${result.requestId}")
```

Each `Call` is `(to, value, data)`, so the list drops directly into an
EIP-5792 / Porto `wallet_prepareCalls` bundle.

### Server-side (no session)

A backend that holds only a recipient's privacy address can build a deposit on
their behalf with the top-level `buildShieldPayload` — no signed-in session and
no private keys:

```kotlin theme={null}
val prepared = buildShieldPayload(
    tokenAddress = "0x...",
    amount = "1000000000000000000",
    recipient = userPrivacyAddress,
    tokenId = tokenId,                      // from the token catalog
    shieldContractAddress = shieldContractAddress,
    wethContractAddress = null,             // required only for native ETH
    teePublicKey = teePublicKey,
    minShieldAmount = minShieldAmount,      // optional: reject below the per-token minimum
    emitApprove = true
)
```

The note's owner is bound cryptographically to the recipient's privacy address,
so only they can spend it; the ephemeral sender used to encrypt the deposit is
generated and discarded internally.

## Next Steps

* [Withdrawals Guide](./withdrawals)
* [Transfers Guide](./transfers)
* [Balances Guide](./balances)
* [Error Handling](./error-handling)
