Skip to content

Guide: Integrating One-Click Yield with OnchainKit

This tutorial provides a complete guide for developers to integrate a simple, high-yield deposit feature into their applications using Morpho's Vaults and Coinbase's OnchainKit. This powerful combination allows you to offer your users a seamless, one-click experience to earn interest on their crypto, complete with gas-sponsored transactions, all powered by Base and Morpho's efficient onchain infrastructure.

We will walk through the entire process, from initial setup to implementing both a plug-and-play component and a fully custom UI.

Final Product: By the end of this guide, you'll be able to integrate a yield component into any Base-related app, similar to the earn features found in leading wallets and dApps.


Architecture Overview

The integration relies on two key components working in concert:

  1. Morpho Protocol (Vaults): Provides the core, permissionless yield infrastructure. Morpho Vaults are ERC4626-compliant smart contracts that automatically allocate user deposits across various Morpho Markets to generate an optimized, passive yield.
  2. OnchainKit: Acts as the developer-friendly SDK and component library for building onchain apps on Base. It handles wallet connections, transaction building, and provides pre-built React components like <Earn /> that abstract away the complexity of interacting with DeFi protocols.

Here is a high-level view of the interaction flow:

┌───────────┐
│ End User  │
└───────────┘

      │ Interacts with

┌──────────────────┐
│ Your Application │
└──────────────────┘

      │ Uses

┌────────────────────┐
│ OnchainKit         │
│ <Earn /> Component │
└────────────────────┘

      │ 1. Connects Wallet
      │ 2. Builds & Sponsors Transaction (via Paymaster)

┌──────────────────┐
│   Base Chain     │
└──────────────────┘

      │ Executes atomic deposit call

┌──────────────────┐
│ Morpho Vault     │
│   (deposit)      │
└──────────────────┘

Part 1: Setup and Configuration

This guide assumes you are building a React application with TypeScript on the Base network.

Step 1: Install Dependencies

We recommend starting with the official OnchainKit template, which sets up the entire environment for you.

npm create onchain@latest

If you are adding OnchainKit to an existing project, install the necessary packages:

npm install @coinbase/onchainkit viem wagmi @tanstack/react-query

Step 2: Configure Paymaster for Gas Sponsorship

To sponsor your users' gas fees, you'll need a Paymaster service. OnchainKit is deeply integrated with the Coinbase Developer Platform (CDP).

  1. Create a CDP Account: Sign up on the Coinbase Developer Platform.
  2. Create a Project: Create a new project and get your projectId.
  3. Set up a Paymaster: Navigate to the "Paymaster" section, create a new paymaster for the Base network, and copy the Paymaster URL.
  4. Fund Your Paymaster: Add funds to your CDP account to cover the gas fees for your users.
  5. Allowlist Contracts (Important!): In your Paymaster policy settings, you must allowlist the contract functions the <Earn /> component will call:
    • The Morpho Vault address: allow the deposit and redeem functions.
    • The underlying token (e.g., USDC, WETH) address: allow the approve function.

Step 3: Define Constants

For a clean implementation, define your vault address in a separate constants file. You can find a list of available Morpho Vaults on the Morpho App.

// constants.ts
import { base } from "viem/chains";
 
// 1. Supported Network
export const SUPPORTED_NETWORK = base;
 
// 2. Morpho Vault Address on Base (Example: USDC Vault)
export const MORPHO_USDC_VAULT_ADDRESS = "0x425314C7836371752a7b8eF41B32997f888A011f";

Step 4: Configure the OnchainKit Provider

Wrap your application's root with the OnchainKitProvider. This makes all of OnchainKit's hooks and components available throughout your app.

// In your main App.tsx or a layout component
import { OnchainKitProvider } from '@coinbase/onchainkit';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { base } from 'viem/chains';
import { WagmiProvider, createConfig, http } from 'wagmi';
 
const queryClient = new QueryClient();
 
// Configure wagmi with the standard Base RPC
const wagmiConfig = createConfig({
  chains: [base],
  transports: {
    [base.id]: http(),
  },
});
 
const App = ({ children }: { children: React.ReactNode }) => {
  return (
    <WagmiProvider config={wagmiConfig}>
      <QueryClientProvider client={queryClient}>
        <OnchainKitProvider
          chain={base}
          // The paymaster URL is configured here to apply globally
          paymaster={process.env.NEXT_PUBLIC_PAYMASTER_URL}
          // The projectId is used by OnchainKit for various services
          projectId={process.env.NEXT_PUBLIC_CDP_PROJECT_ID} 
        >
          {children}
        </OnchainKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  );
};

Part 2: Building the Earn Component

OnchainKit offers incredible flexibility. You can either use the pre-built, plug-and-play <Earn /> component for a quick integration, or build a fully custom UI using its underlying hooks.

Option A: The Plug-and-Play Approach (Easiest)

This is the fastest way to add a yield feature to your app. The <Earn /> component handles the entire UI and transaction logic for you.

// components/SimpleEarnComponent.tsx
import { Earn } from '@coinbase/onchainkit/earn';
import { MORPHO_USDC_VAULT_ADDRESS } from '../constants';
 
export const SimpleEarnComponent = () => {
  return (
    <div style={{ width: '100%', maxWidth: '420px', margin: 'auto' }}>
      <Earn 
        vaultAddress={MORPHO_USDC_VAULT_ADDRESS}
        // This prop enables gas-sponsored transactions via your Paymaster
        isSponsored 
      />
    </div>
  );
};

That's it! You now have a fully functional, gas-sponsored yield component in your application.

Option B: The Custom UI Approach (Advanced)

For full control over the look and feel, you can build your own component using the useEarnContext hook and the declarative <Transaction /> component.

// components/CustomEarnComponent.tsx
import React, { useCallback } from 'react';
import { useAccount } from 'wagmi';
import { 
  Earn,
  useEarnContext, 
  buildDepositToMorphoTx, 
} from '@coinbase/onchainkit/earn';
import { Transaction } from '@coinbase/onchainkit/transaction';
import { parseUnits } from 'viem';
import { MORPHO_USDC_VAULT_ADDRESS } from '../constants';
 
// A custom UI that uses the underlying context from the <Earn> provider
const CustomDepositUI = () => {
  const { address } = useAccount();
 
  // useEarnContext gives you all the data you need about the vault
  const { 
    apy, 
    vaultToken, 
    setDepositAmount, 
    depositAmount 
  } = useEarnContext();
 
  // This function prepares the transaction calls.
  // It's passed to the <Transaction> component, which handles execution.
  const buildCalls = useCallback(async () => {
    if (!address || !depositAmount || parseFloat(depositAmount) === 0) return [];
 
    const parsedAmount = parseUnits(depositAmount, vaultToken.decimals);
    
    // OnchainKit's helper function builds the necessary `approve` and `deposit` calls.
    const calls = await buildDepositToMorphoTx({
      vaultAddress: MORPHO_USDC_VAULT_ADDRESS,
      tokenAddress: vaultToken.address,
      amount: parsedAmount,
      recipientAddress: address,
    });
 
    return calls;
  }, [address, depositAmount, vaultToken]);
 
  return (
    <div>
      <h3>Custom Deposit for {vaultToken.symbol}</h3>
      <p>APY: {(apy * 100).toFixed(2)}%</p>
      <input
        type="text"
        placeholder={`Amount in ${vaultToken.symbol}`}
        onChange={(e) => setDepositAmount(e.target.value)}
      />
      
      {/* 
        The <Transaction> component is a declarative wrapper.
        It takes the `buildCalls` function and handles the button state,
        transaction submission, and waiting states for you.
      */}
      <Transaction
        calls={buildCalls}
        isSponsored
      >
        <button>Deposit Now</button>
      </Transaction>
    </div>
  );
};
 
// You must wrap your custom UI with the <Earn> component to provide the context.
export const CustomEarnComponent = () => {
  return (
    <Earn vaultAddress={MORPHO_USDC_VAULT_ADDRESS}>
      <CustomDepositUI />
    </Earn>
  );
};

Part 3: Production Considerations

Security Best Practices

  • Key Management: Never expose private keys or Paymaster URLs in client-side code. Use secure environment variables.
  • Input Validation: Always validate and sanitize user inputs, especially amounts, to prevent errors and unexpected behavior.
  • Paymaster Monitoring: Regularly monitor your Paymaster balance on the Coinbase Developer Platform to ensure you have sufficient funds to sponsor user transactions. Set up low-balance alerts.

Common Issues & Troubleshooting

IssueSolution
Transaction fails silentlyCheck the browser console for detailed error messages. Common causes include an incorrect vaultAddress or network mismatch.
Paymaster / Gas Sponsorship FailsVerify your Paymaster URL is correct, your account is funded, and that you have allowlisted the vault and token contracts in your CDP policy.
Component not rendering dataEnsure that OnchainKitProvider correctly wraps your entire application or the component tree where <Earn /> is used.
"Cannot estimate gas"This often points to an onchain error. Ensure the user has enough of the asset to deposit or that the vault contract is operational.

Key Resources & Next Steps

You now have the building blocks to integrate a powerful, user-friendly yield product. To learn more, explore these resources: