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:
- 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.
- 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).
- Create a CDP Account: Sign up on the Coinbase Developer Platform.
- Create a Project: Create a new project and get your
projectId
. - Set up a Paymaster: Navigate to the "Paymaster" section, create a new paymaster for the Base network, and copy the Paymaster URL.
- Fund Your Paymaster: Add funds to your CDP account to cover the gas fees for your users.
- 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
andredeem
functions. - The underlying token (e.g., USDC, WETH) address: allow the
approve
function.
- The Morpho Vault address: allow the
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
Issue | Solution |
---|---|
Transaction fails silently | Check the browser console for detailed error messages. Common causes include an incorrect vaultAddress or network mismatch. |
Paymaster / Gas Sponsorship Fails | Verify 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 data | Ensure 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:
OnchainKit Earn Docs
Dive deeper into the <Earn />
component, its subcomponents, and the useEarnContext
hook.
Read Docs →
OnchainKit Quickstart
Review the setup process and explore other powerful components for identity, transactions, and more.
Start Building →
Morpho Earn Concepts
Understand the mechanics behind Morpho Vaults, including how yield is generated and risks are managed.
Learn More →
Community Support
Join the Base Discord to ask questions and get help from the community and the OnchainKit team.
Join Discord →