The Earn component provides a simple interface for earning interest on your crypto via Morpho vaults on Base.

Quickstart

To use Earn, you’ll need to provide a vaultAddress prop. A vault address can be obtained from Morpho’s Vaults page.
import { Earn } from '@coinbase/onchainkit/earn'; 

<Earn vaultAddress="0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A" />

Customization

Custom components and layouts

Earn accepts a children prop that can be used to render Earn subcomponents or custom components. For instance, you can render the EarnDeposit component by itself within the Earn component, along with any other custom components you’d like to render.
import { Earn, EarnDeposit } from '@coinbase/onchainkit/earn';

<Earn vaultAddress="0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A">
  <div>Custom header</div>
  <EarnDeposit />
  <div>Custom footer</div>
</Earn>
For more granular control, you can also render lower level subcomponents within EarnDeposit and EarnWithdraw. These subcomponents accept className props to customize their styles.
import { Earn,
        EarnDeposit,
        EarnDetails,
        DepositBalance,
        DepositAmountInput,
        DepositButton } from '@coinbase/onchainkit/earn';

<Earn vaultAddress="0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A">
  <EarnDeposit>
    <EarnDetails />
    <DepositBalance />
    <DepositAmountInput className="border-2 border-green-400" />
    <DepositButton />
  </EarnDeposit>
</Earn>

Customizing styles

The Earn component is best styled via a OnchainKit theme. You can also override individual component styles using className.

Advanced Usage

If you’d like to implement your own custom components, you can use useEarnContext within an Earn component to access context and build your own components. You can find the full interface for EarnContextType on the Types page. Below, we use useEarnContext to implement a custom deposit interface by using useEarnContext to access the depositAmount and setDepositAmount context values.
import { Earn, useEarnContext } from '@coinbase/onchainkit/earn';
import { CustomDepositButtons } from '@/custom-deposit-buttons';

  <Earn vaultAddress="0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A">
      <CustomDepositButtons />
  </Earn>

Taking advantage of the data and methods provided by useEarnContext, developers can implement fully custom deposit and withdraw interfaces, modifying everything from UI elements to input behavior.

Examples

Sponsoring transactions

To sponsor transactions, you can use the isSponsored prop.
<Earn vaultAddress="0x7BfA7C4f149E7415b73bdeDfe609237e29CBF34A" isSponsored />
Ensure that your OnchainKitProvider has a paymaster configured:
<OnchainKitProvider
  config={{ 
    paymaster: process.env.PAYMASTER_ENDPOINT, 
  }}
>
If you have a contract allowlist set on Coinbase Developer Platform, you’ll need to ensure that the following contract functions are allowed:
  • deposit on the Morpho vault
  • redeem on the Morpho vault
  • approve on the token being deposited

Components

The Earn component includes the following subcomponents:
  • <Earn /> - A fully built Withdraw and Deposit component. Also includes a children prop to render custom components and provides useEarnContext to access the context values.
  • <EarnProvider /> - A headless provider that provides the useEarnContext hook to the Earn component.
  • <EarnDeposit /> - A fully built deposit card.
  • <EarnWithdraw /> - A fully built withdraw card.
  • <EarnDetails /> - A component that displays the details of the vault.
  • <DepositAmountInput /> - A component that handles the deposit amount input.
  • <DepositBalance /> - A component that displays the balance underlying asset in the user’s wallet.
  • <DepositButton /> - A component that triggers the deposit transaction.
  • <WithdrawAmountInput /> - A component that handles the withdraw amount input.
  • <WithdrawBalance /> - A component that displays how much the user can withdraw from a vault.
  • <WithdrawButton /> - A component that triggers the withdraw transaction.
  • <YieldDetails /> - A component that displays the yield details of the vault.
  • <VaultDetails /> - A component that displays the vault details.

Hooks

Props

EarnProps
type EarnProps = {
  children?: React.ReactNode;
  className?: string;
  vaultAddress: Address;
  isSponsored?: boolean;
  /** An optional callback function that handles errors within the provider. */
  onError?: (error: TransactionError) => void;
  /** An optional callback function that exposes the component lifecycle state */
  onStatus?: (lifecycleStatus: LifecycleStatus) => void;
  /** An optional callback function that exposes the transaction receipt */
  onSuccess?: (transactionReceipt?: TransactionReceipt) => void;
};
EarnProviderProps
type EarnProviderProps = {
  children: React.ReactNode;
  vaultAddress: Address;
  isSponsored?: boolean;
  /** An optional callback function that handles errors within the provider. */
  onError?: (error: TransactionError) => void;
  /** An optional callback function that exposes the component lifecycle state */
  onStatus?: (lifecycleStatus: LifecycleStatus) => void;
  /** An optional callback function that exposes the transaction receipt */
  onSuccess?: (transactionReceipt?: TransactionReceipt) => void;
};
EarnContextType
type EarnContextType = {
  /** Warns users if vault address is invalid */
  error: Error | null;
  recipientAddress?: Address;
  /** Balance of the underlying asset in the user's wallet, converted to the asset's decimals */
  walletBalance?: string;
  /** Status of the wallet balance fetch */
  walletBalanceStatus: 'pending' | 'success' | 'error';
  refetchWalletBalance: () => void;
  vaultAddress: Address;
  /** The token that is being deposited or withdrawn (the underlying asset of the vault) */
  vaultToken: Token | undefined;
  vaultName: string | undefined;
  /** Total deposits in the vault */
  deposits: string | undefined;
  /** Total liquidity (available to borrow) in the vault */
  liquidity: string | undefined;
  /** Total APY of the vault, including rewards */
  apy: number | undefined;
  nativeApy: UseMorphoVaultReturnType['nativeApy'];
  vaultFee: UseMorphoVaultReturnType['vaultFee'];
  /** Rewards earned by the user in the vault */
  rewards: UseMorphoVaultReturnType['rewards'];
  /** The amount of underlying asset that has been deposited in the vault by the connected user */
  depositedBalance?: string;
  /** Whether the deposited balance is being fetched */
  depositedBalanceStatus: 'pending' | 'success' | 'error';
  refetchDepositedBalance: () => void;
  /** Interest earned by the user in the vault */
  interestEarned?: string;
  /** Amount that the user has typed into the deposit amount input */
  depositAmount: string;
  setDepositAmount: (amount: string) => void;
  depositAmountError: string | null;
  depositCalls: Call[];
  /** Amount that the user has typed into the withdraw amount input */
  withdrawAmount: string;
  setWithdrawAmount: (amount: string) => void;
  withdrawAmountError: string | null;
  withdrawCalls: Call[];
  lifecycleStatus: LifecycleStatus;
  updateLifecycleStatus: (
    status: LifecycleStatusUpdate<LifecycleStatus>,
  ) => void;
};
LifecycleStatus
type LifecycleStatus =
  | {
      statusName: 'init';
      statusData: null;
    }
  | {
      statusName: 'amountChange';
      statusData: {
        amount: string;
        token: Token;
      };
    }
  | Extract<
      TransactionLifecycleStatus,
      {
        statusName:
          | 'transactionPending'
          | 'transactionLegacyExecuted'
          | 'success'
          | 'error';
      }
    >;
Additional component props:
// Input components
type DepositAmountInputProps = { className?: string; };
type WithdrawAmountInputProps = { className?: string; };

// Balance components  
type DepositBalanceProps = { className?: string; };
type WithdrawBalanceProps = { className?: string; };

// Container components
type EarnDepositProps = { children?: React.ReactNode; className?: string; };
type EarnWithdrawProps = { children?: React.ReactNode; className?: string; };

// Action components
type DepositButtonProps = { className?: string; };
type WithdrawButtonProps = { className?: string; };
type EarnDetailsProps = { className?: string; };