# Quickstart (/onramp/earn/quickstart)

> For the complete documentation index, see [llms.txt](/llms.txt)



<Callout type="warn" title="Beta">
  ZeroDev Earn is an experimental product offered in beta. It supports your
  integration with third-party protocols. ZeroDev does not provide these
  protocols or the services they offer, does not custody assets, and does not
  execute or control transactions; any transactions are solely between you (or
  your end users) and the applicable third-party protocol. Any APY, rewards,
  or other amounts are provided by such protocols, not ZeroDev, and all data
  is supplied by third parties for informational purposes only. Use involves
  significant risk, including smart contract failures, market volatility,
  illiquidity, lockup, and loss of principal. ZeroDev is not liable for any
  losses arising from use of the product or the third-party protocols it
  surfaces. You are responsible for ensuring your end users understand these
  risks and for any disclosures required by applicable law. Do your own
  independent research and proceed at your own risk.
</Callout>

This guide deposits USDC from Base into a Morpho vault on Arbitrum using an EOA.

<Callout type="warn" title="Mainnet example">
  This flow uses mainnet assets. Start with a small amount. The account needs
  USDC and ETH for gas on Base.
</Callout>

For complete EOA, Kernel, Aave, withdrawal, and recovery scripts, see the [`earn` examples in the ZeroDev examples repository](https://github.com/zerodevapp/zerodev-examples).

## Prerequisites [#prerequisites]

* Node.js 18 or later
* A [ZeroDev project](https://dashboard.zerodev.app/) with the caller's origin or IP address allowlisted
* An EOA funded with USDC and ETH on Base

## Install [#install]

<Tabs items="[&#x22;npm&#x22;,&#x22;pnpm&#x22;,&#x22;yarn&#x22;,&#x22;bun&#x22;]">
  <Tab value="npm">
    ```bash
    npm i @zerodev/earn@beta viem
    ```
  </Tab>

  <Tab value="pnpm">
    ```bash
    pnpm add @zerodev/earn@beta viem
    ```
  </Tab>

  <Tab value="yarn">
    ```bash
    yarn add @zerodev/earn@beta viem
    ```
  </Tab>

  <Tab value="bun">
    ```bash
    bun add @zerodev/earn@beta viem
    ```
  </Tab>
</Tabs>

Pin an exact beta version before deploying to production.

## Create the clients [#create-the-clients]

```ts
import { createEarnClient, TOKENS, type Vault } from "@zerodev/earn";
import {
  createPublicClient,
  createWalletClient,
  http,
  parseUnits,
  type Hex,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { arbitrum, base } from "viem/chains";

const account = privateKeyToAccount("<PRIVATE_KEY>" as Hex);

const earn = createEarnClient({
  projectId: "<ZERODEV_PROJECT_ID>",
});

const walletClient = createWalletClient({
  account,
  chain: base,
  transport: http(),
});

const publicClient = createPublicClient({
  chain: base,
  transport: http(),
});
```

`createEarnClient` is synchronous. It does not create a signer, RPC client, or bundler client.

## Select a vault [#select-a-vault]

Use discovery to find Morpho USDC vaults on Arbitrum. Check the current on-chain deposit state before requesting a quote.

```ts
const amount = "1";

const { vaults } = await earn.listVaults({
  asset: TOKENS.USDC,
  chains: [arbitrum.id],
  protocol: "morpho",
});

let vault: Vault | undefined;

for (const candidate of vaults.slice(0, 5)) {
  const check = await earn.preflight({
    owner: account.address,
    vaultId: candidate.address,
    destChainId: candidate.chainId,
    // preflight uses base units
    amount: parseUnits(amount, candidate.asset.decimals).toString(),
  });

  if (!check.depositsDisabled) {
    vault = candidate;
    break;
  }
}

if (!vault) {
  throw new Error("No Morpho USDC vault is currently accepting deposits");
}
```

`listVaults` returns listed vaults. `preflight` verifies the target and reads its current deposit capacity. See [Supported protocols and vaults](/onramp/earn/supported-vaults) for discovery and raw-address constraints.

## Request a quote [#request-a-quote]

```ts
const quote = await earn.morpho.deposit({
  owner: account.address,
  amount,
  token: TOKENS.USDC,
  srcChainId: base.id,
  into: vault,
  slippage: 100, // 1%
});

if (!quote.sra) {
  throw new Error("Deposit quote did not include an SRA");
}

console.log("SRA:", quote.sra);
console.log("Estimated shares:", quote.estimatedShares);
```

Passing a `Vault` object allows the SDK to derive `destChainId` from `vault.chainId`. Request the quote when the user is ready to submit; estimates are valid for approximately 60 seconds.

## Submit the source-chain calls [#submit-the-source-chain-calls]

```ts
for (const call of quote.transaction.calls) {
  const hash = await walletClient.sendTransaction({
    to: call.to,
    data: call.data,
    value: BigInt(call.value),
  });

  await publicClient.waitForTransactionReceipt({ hash });
}
```

Execute every call in order. Direct-token deposit routes currently contain the SRA funding transfer. The array shape also supports routes that require more than one source-chain call.

For a smart account, submit `quote.userOp.callData` as one ERC-4337 user operation. See [Using a smart account](/onramp/earn/usage#using-a-smart-account).

## Track the deposit [#track-the-deposit]

Save `quote.sra` before submitting the transaction. Status and recovery methods use the SRA address.

```ts
const watcher = earn.watchStatus(quote.sra, {
  onStatusChange(status) {
    console.log(status.state);
  },
  onError(error) {
    console.error(error);
  },
});

await watcher.done;
```

The normal cross-chain sequence is `PENDING`, `BRIDGING`, `EXECUTING`, then `COMPLETED`. If execution fails after funding, use the [recovery flow](/onramp/earn/usage#recover-funds-from-an-sra).
