<!-- tempo-docs-context -->
> Source: [/docs/earn/integrate](https://tempo.xyz/developers/docs/earn/integrate.md)
> Section: [Earn](https://tempo.xyz/developers/docs/earn.md). Choose a vault, deposit stablecoins, read positions, and withdraw. Earn is in beta; see the introduction for access.
> [Documentation index](https://tempo.xyz/developers/llms.txt) · [Docs skill](https://tempo.xyz/developers/SKILL.md)

> Tempo MCP: Use `search`, `find_pages`, `read_page`, and `code` at `https://mcp.tempo.xyz` for Tempo and related documentation.
> If an indexed page is missing or reflects an older site, read this site’s [documentation index](https://tempo.xyz/developers/llms.txt) and Markdown pages directly.

<span id="deposit-funds" />

# Deposit

Deposit stablecoins from a funded Tempo account into an Earn vault, then read the resulting position.

A [vault](https://tempo.xyz/developers/docs/earn/how-it-works) is a contract that accepts one stablecoin and issues vault shares, units that represent the account's position. A share isn't necessarily worth one stablecoin, so the position's asset value is read separately from its share count.

<span id="select-a-vault" />

<span id="connect-deposits" />

<span id="display-the-position" />

<span id="handle-withdrawals" />

<span id="build-the-earning-balance" />

## Try a deposit

Create a passkey test account, get faucet funds, and deposit into a pathUSD test vault on [Moderato](https://tempo.xyz/developers/docs/quickstart/connection-details), Tempo's testnet. [pathUSD](https://tempo.xyz/developers/docs/protocol/exchange/quote-tokens#pathusd) is a Tempo stablecoin, and the vault is verified, meaning it's in Tempo's curated Earn registry. The amount starts at 1 pathUSD. The demo checks that the vault supports deposits and redemptions before enabling them. You can withdraw the test position afterward.

In the interactive web page, create a testnet passkey account, get test pathUSD from the faucet, choose a deposit amount (1 pathUSD by default), approve it for the verified vault, and confirm a deposit. Inspect the resulting shares and receipt, then withdraw the test position. Every transaction requires your confirmation and uses Moderato testnet.

## Deposit into a vault

Each tab deposits 1 pathUSD (`1000000` base units) into the Moderato test vault. The tabs use values defined in the [Earn testnet example](https://tempo.xyz/developers/docs/earn/example), which sets up each tool:

* **CLI (Cast).** Shell variables for the keystore (`KEYSTORE`), RPC URL (`TEMPO_RPC_URL`), `VAULT`, `ASSET_TOKEN`, `ASSET_AMOUNT`, and `ACCOUNT`, funded from the faucet.
* **Viem.** A `client` created with `createClient` from `viem/tempo` on `tempoModerato`, with a funded account, plus `vault`, `assetToken`, and `assetAmount`.
* **Wagmi.** A React component connected to a funded Moderato account (`address`), with the same constants and hook imports from `wagmi` and `wagmi/tempo`.

Keep the signer and vault on the same network.

::::steps
### Check the asset and access requirements

Read the vault with the [Earn API](https://tempo.xyz/developers/docs/api/earn) `GET /v1/earn/vaults/{vaultId}` endpoint, adding `include=access,capabilities`. Save the vault address and network together. Before enabling the deposit action, check these fields:

* **`assetToken`.** Matches the stablecoin the account holds.
* **`access.status`.** `open` accepts any holder; `allowlisted` accepts only approved holders.
* **`capabilities.deposit`.** Is `true`.
* **`state.depositsPaused`.** Is `false`.

<span id="prepare-the-deposit" />

### Approve funds

The vault pulls the stablecoin from the account, so the deposit simulation that follows needs an approval for the amount. Show the asset, amount, and vault before asking the account to sign. Cast pays the network fee in the vault's asset, pathUSD here, with `--tempo.fee-token`.

:::code-group
```ts [Viem]
await client.token.approveSync({
  token: assetToken,
  spender: vault,
  amount: assetAmount,
})
```

```bash [CLI (Cast)]
cast erc20 approve "$ASSET_TOKEN" "$VAULT" "$ASSET_AMOUNT" \
  --rpc-url "$TEMPO_RPC_URL" --account "$KEYSTORE" \
  --tempo.fee-token "$ASSET_TOKEN"
```

```tsx [Wagmi]
const approval = Hooks.token.useApproveSync({
  mutation: { onSuccess: () => setApproved(true) },
})
const approve = () => approval.mutateAsync({
  token: assetToken, spender: vault, amount: assetAmount,
})
```
:::

`shareAmountMin` is the fewest shares the vault may mint before it rejects the deposit, which protects the account if the share price moves before the deposit lands. After approval confirms, simulate the deposit with `shareAmountMin` set to 1, which only reads the quoted share amount. Then set `shareAmountMin` 0.5% below the quote: `EarnShares.minimumOutput(quotedShares, 50)` subtracts 50 basis points, and the Cast tab defines a `minimum_output` helper that does the same in integer math. `Actions`, `Abis`, and `EarnShares` come from `viem/tempo`.

:::code-group
```ts [Viem]
const { result: quotedShares } = await Actions.earn.deposit.simulate(client, {
  vault,
  assetAmount,
  shareAmountMin: 1n,
})
const shareAmountMin = EarnShares.minimumOutput(quotedShares, 50)
```

```bash [CLI (Cast)]
minimum_output() {
  python3 - "$1" <<'PYTHON'
import sys
amount = int(sys.argv[1])
if amount <= 0:
    raise ValueError("The quote must be positive")
print(max(1, amount * 9950 // 10000))
PYTHON
}

QUOTED_SHARES=$(cast call "$VAULT" \
  'deposit(uint256,address,uint256)(uint256)' \
  "$ASSET_AMOUNT" "$ACCOUNT" 1 --from "$ACCOUNT" \
  --rpc-url "$TEMPO_RPC_URL" --json | \
  python3 -c 'import json,sys; print(json.load(sys.stdin)[0])')
MIN_SHARES=$(minimum_output "$QUOTED_SHARES")
```

```tsx [Wagmi]
const quote = useSimulateContract({
  abi: Abis.earnVault, address: vault, account: address,
  functionName: 'deposit', args: [assetAmount, address, 1n],
  query: { enabled: approved },
})
const shareAmountMin = quote.data
  ? EarnShares.minimumOutput(quote.data.result, 50)
  : undefined
```
:::

Refresh the simulation if the customer waits before signing. In Wagmi, call `quote.refetch()` to refresh and keep the deposit button disabled until a positive quote is available.

A deposit is a contract call; a direct transfer to the vault address doesn't create a position. The Earn API only reads data and doesn't submit deposits or withdrawals.

<span id="submit-and-verify-the-position" />

### Make a deposit

Viem's `depositSync` and Wagmi's `Actions.earn.deposit.calls` send an approval and the deposit together as one atomic transaction, so in those tabs the earlier approval only enables the simulation. Cast sends the deposit alone and uses the earlier approval. The account receives shares, and the vault rejects the deposit if it would mint fewer than `shareAmountMin`. In Wagmi, `forceAtomic: true` requires the wallet to run both calls in one transaction.

:::code-group
```ts [Viem]
const { receipt, shareAmount } = await client.earn.depositSync({
  vault,
  assetAmount,
  shareAmountMin,
})
```

```bash [CLI (Cast)]
cast send "$VAULT" 'deposit(uint256,address,uint256)' \
  "$ASSET_AMOUNT" "$ACCOUNT" "$MIN_SHARES" \
  --rpc-url "$TEMPO_RPC_URL" --account "$KEYSTORE" \
  --tempo.fee-token "$ASSET_TOKEN"
```

```tsx [Wagmi]
const deposit = useSendCallsSync()
const submitDeposit = async () => {
  if (!shareAmountMin) return
  const calls = Actions.earn.deposit.calls({
    vault, assetToken, assetAmount,
    recipient: address, shareAmountMin,
  })
  return deposit.mutateAsync({ calls, forceAtomic: true })
}
```
:::

Confirm the receipt succeeded. Save its transaction hash, the deposited amount, and the shares received, so you can match them against your own ledger later.

### Confirm your position

Read the position with the Earn API `GET /v1/earn/vaults/{vaultId}/positions/{address}` endpoint. The request builder starts with the Moderato test vault as `vaultId` and `chainId` set to `testnet`. Set the `address` path parameter to your test account’s address; use a matching vault and network for other deposits. Use **Try** to read the position, or switch the request’s code language to copy an example.

`GET /v1/earn/vaults/{vaultId}/positions/{address}`

Get account position

[API reference](https://tempo.xyz/developers/docs/api/earn#getearnvaultposition) — request parameters, authentication, and response schema. The examples below use editable sample values.

**cURL**

```bash
curl 'https://api.tempo.xyz/v1/earn/vaults/0x20147491b5701dea880263241c335caca9be326d/positions/0xbe058e1c4df8a4366a387bf595b284246a93039e?chainId=testnet'
```

**TypeScript**

```ts
fetch('https://api.tempo.xyz/v1/earn/vaults/0x20147491b5701dea880263241c335caca9be326d/positions/0xbe058e1c4df8a4366a387bf595b284246a93039e?chainId=testnet')
```

In the response, read `shareBalance` for the shares held and `value` for their current value in the vault's stablecoin. Don't treat the share count as the same amount of stablecoins.
::::

<span id="show-rates-and-rewards-accurately" />

## Display the earning balance

Follow [Balances and earnings](https://tempo.xyz/developers/docs/earn/balances) to show position value, earnings, and rate measurement windows. Make [withdrawal behavior](https://tempo.xyz/developers/docs/earn/withdraw) visible before accepting the deposit.

## Track private Earn activity

A [Zone](https://tempo.xyz/developers/docs/zones) is a private stablecoin ledger connected to Tempo. A vault supports Zone positions when `capabilities.privateRouting` is `true`; request `include=zones` to read its Zone routes. Funds in a Zone reach a vault on Tempo through a [Zone withdrawal](https://tempo.xyz/developers/docs/zones/withdraw), so Zone-routed positions start from a withdrawal, not from the approval and deposit steps on this page. Tempo runs the Earn operation, then deposits the result back into the Zone.

Each Zone withdrawal emits a `senderTag`, a 32-byte tag shared by withdrawals from one sender in one Zone transaction. Pass it to [`GET /v1/earn/transactions/{senderTag}`](https://tempo.xyz/developers/docs/api/earn#getearntransaction). The response reports the Earn execution on Tempo in `l1Execution` and the return deposit into the Zone in `zoneDeposit`. Show returned funds as available once `zoneDeposit.status` is `completed`.

The Earn API's account position listing includes Zone positions, but it returns no earnings data for them.

<span id="earn-api-operations" />

## Read Earn data

The [Earn API](https://tempo.xyz/developers/docs/api/earn) covers vault discovery, positions, share prices, and earnings. Keep the network consistent across discovery, transactions, and reads. The API's `chainId` parameter takes `mainnet`, `testnet`, or a numeric chain ID.
