> ## 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

```typescript theme={null}
const result = await sdk.vault.shield({
  tokenAddress: '0x...token-address',
  amount: 1000000000000000000n, // 1 token (18 decimals)
});

console.log('Transaction hash:', result.txHash);
console.log('Commitment:', result.commitment);
```

## Deposit Parameters

```typescript theme={null}
interface DepositParams {
  tokenAddress: Hex;         // ERC-20 token address
  amount: bigint;            // Amount in wei
  recipientMpk?: bigint;     // Optional: deposit to another user
  onProgress?: OnProgress;   // Optional: progress callback
}
```

| Parameter      | Type       | Required | Description                   |
| -------------- | ---------- | -------- | ----------------------------- |
| `tokenAddress` | `Hex`      | Yes      | ERC-20 token contract address |
| `amount`       | `bigint`   | Yes      | Amount in smallest unit (wei) |
| `recipientMpk` | `bigint`   | No       | Deposit to another user's MPK |
| `onProgress`   | `function` | No       | Progress callback function    |

## Progress Tracking

Track shield progress with a callback:

```typescript theme={null}
await sdk.vault.shield({
  tokenAddress: '0x...',
  amount: 1000000000000000000n,
  onProgress: ({ step, message, txHash }) => {
    console.log(`Step: ${step}`);
    console.log(`Message: ${message}`);
    if (txHash) {
      console.log(`Transaction: ${txHash}`);
    }
  },
});
```

### Deposit Steps

| Step         | Description                              |
| ------------ | ---------------------------------------- |
| `wrapping`   | Wrapping ETH to WETH (if depositing ETH) |
| `approving`  | Approving token spending                 |
| `shielding`  | Executing the deposit transaction        |
| `compliance` | Waiting for compliance verification      |

```typescript theme={null}
enum ShieldStep {
  wrapping = 'wrapping',
  approving = 'approving',
  shielding = 'shielding',
  compliance = 'compliance',
}
```

## Deposit Result

```typescript theme={null}
interface ShieldResult {
  /** Hash of the shield transaction submitted on-chain. */
  txHash: Hex;
  /** Note commitment registered in the shielded pool. */
  commitment: Hex;
  /** Fee paid for this shield, in token wei. Not surfaced by the shield response, so usually undefined; query `vault.getFees()` for the schedule. */
  fee?: bigint;
  /** On-chain shield request ID, from the `ShieldRequested` event. Use this to track status via `sdk.transactions.getShieldStatus(requestId)`. */
  requestId: string;
  /** WETH wrap transaction hash, set only when ETH was wrapped to WETH as part of the flow. */
  wrapTxHash?: Hex;
}
```

## Depositing ETH

To deposit native ETH, use the zero address:

```typescript theme={null}
const ETH_ADDRESS = '0x0000000000000000000000000000000000000000';

const result = await sdk.vault.shield({
  tokenAddress: ETH_ADDRESS,
  amount: 1000000000000000000n, // 1 ETH
  onProgress: ({ step, message, txHash }) => {
    if (step === 'wrapping') {
      console.log('Wrapping ETH to WETH...');
    }
  },
});

// Result includes wrap transaction
console.log('Wrap tx:', result.wrapTxHash);
console.log('Deposit tx:', result.txHash);
```

The SDK automatically:

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

## Deposit to Another User

You can deposit directly to another user's private balance:

```typescript theme={null}
// Get recipient's MPK from their privacy address
import { extractMpkFromPrivacyAddress } from '@sunnyside-io/privacy-boost';

const recipientPrivacyAddress = '0x04...';
const recipientMpk = extractMpkFromPrivacyAddress(recipientPrivacyAddress);

const result = await sdk.vault.shield({
  tokenAddress: '0x...',
  amount: 1000000000000000000n,
  recipientMpk,
});
```

## Token Approval

The SDK handles token approval automatically. For large amounts, you may want to approve unlimited spending:

```typescript theme={null}
// The SDK checks current allowance and only approves if needed
// If allowance is sufficient, the approve step is skipped
```

## Parsing Amounts

Use the standalone `parseAmount` / `formatAmount` helpers exported from the SDK package:

```typescript theme={null}
import { parseAmount, formatAmount } from '@sunnyside-io/privacy-boost';

// Parse human-readable amount to bigint (token has 18 decimals)
const amount = parseAmount('1.5', 18);
// Returns: 1500000000000000000n

// Format bigint to human-readable
const formatted = formatAmount(1500000000000000000n, 18);
// Returns: "1.5"
```

## Error Handling

```typescript theme={null}
try {
  await sdk.vault.shield({ tokenAddress, amount });
} catch (error) {
  switch (error.code) {
    case 'INSUFFICIENT_BALANCE':
      console.log('Not enough tokens in wallet');
      break;
    case 'USER_REJECTED':
      console.log('User rejected transaction');
      break;
    case 'APPROVAL_FAILED':
      console.log('Token approval failed');
      break;
    case 'DEPOSIT_FAILED':
      console.log('Deposit transaction failed');
      break;
    case 'COMPLIANCE_REJECTED':
      console.log('Compliance check failed');
      break;
    default:
      console.log('Deposit error:', error.message);
  }
}
```

## UI Example

```typescript theme={null}
import { useState } from 'react';

function ShieldForm() {
  const [amount, setAmount] = useState('');
  const [step, setStep] = useState('');
  const [loading, setLoading] = useState(false);

  const handleShield = async () => {
    setLoading(true);
    try {
      const decimals = 18; // token decimals (fetch via sdk.vault.getToken)
      const parsedAmount = parseAmount(amount, decimals);

      await sdk.vault.shield({
        tokenAddress,
        amount: parsedAmount,
        onProgress: ({ step, message }) => {
          setStep(message);
        },
      });

      alert('Deposit successful!');
    } catch (error) {
      alert(`Deposit failed: ${error.message}`);
    } finally {
      setLoading(false);
      setStep('');
    }
  };

  return (
    <div>
      <input
        type="text"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
        placeholder="Amount"
        disabled={loading}
      />
      <button onClick={handleShield} disabled={loading}>
        {loading ? step || 'Processing...' : 'Deposit'}
      </button>
    </div>
  );
}
```

## Best Practices

### 1. Always Validate Amounts

```typescript theme={null}
function validateDepositAmount(amount: bigint, walletBalance: bigint) {
  if (amount <= 0n) {
    throw new Error('Amount must be positive');
  }
  if (amount > walletBalance) {
    throw new Error('Insufficient wallet balance');
  }
}
```

### 2. Check Token Support

```typescript theme={null}
const supportedTokens = await sdk.vault.getTokens();
const isSupported = supportedTokens.some(
  (t) => t.address.toLowerCase() === tokenAddress.toLowerCase()
);

if (!isSupported) {
  throw new Error('Token not supported');
}
```

### 3. Handle Large Deposits

For large deposits, consider splitting into multiple transactions:

```typescript theme={null}
async function depositInChunks(tokenAddress: Hex, totalAmount: bigint) {
  const CHUNK_SIZE = 10000000000000000000n; // 10 tokens
  let remaining = totalAmount;

  while (remaining > 0n) {
    const chunk = remaining > CHUNK_SIZE ? CHUNK_SIZE : remaining;
    await sdk.vault.shield({ tokenAddress, amount: chunk });
    remaining -= chunk;
  }
}
```

## 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.

```typescript theme={null}
import { finalizeShield } from '@sunnyside-io/privacy-boost';

// 1. Build the calldata (no wallet, nothing submitted)
const prepared = await sdk.vault.prepareShield({
  tokenAddress: '0x...',
  amount: 1000000000000000000n,
  // recipient: '0x04...',  // omit to shield to yourself
});

// prepared.wrap? / prepared.approve? / prepared.shield are { to, value, data }
// calls — relay them in order (wrap?, approve?, shield).
const calls = [prepared.wrap, prepared.approve, prepared.shield].filter(Boolean);
const receipt = await myRelayer.submit(calls);

// 2. Finalize from the mined receipt to recover the request id. finalizeShield
// is a stateless top-level export — it parses the receipt and needs no session.
const result = await finalizeShield({
  receipt,
  shieldContract: prepared.finalize.shieldContract,
  commitment: prepared.commitment,
});
console.log('Request ID:', result.requestId);
```

Each call is `{ to, value, data }`, so the array 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 stateless `buildShieldPayload` — no signed-in session and
no private keys:

```typescript theme={null}
import { buildShieldPayload } from '@sunnyside-io/privacy-boost';

const prepared = await buildShieldPayload({
  tokenAddress: '0x...',
  amount: 1000000000000000000n,
  recipient: userPrivacyAddress,
  tokenId,                 // from the token catalog
  shieldContractAddress,
  teePublicKey,
  minShieldAmount,         // optional: reject below the per-token minimum
});
```

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)
