Skip to main content
The MorphoClient provides direct access to Morpho Blue lending markets. It handles registration, market discovery, collateral management, borrowing, USDC supply (lending to earn yield), repayment, and position tracking — all through a Safe account using ERC-4337 UserOps.
MorphoClient submits all state-changing operations as ERC-4337 UserOps: EOA signs UserOp → EntryPoint → Safe7579 → execute(MODE_BATCH, ...). Multiple operations (e.g., deposit + borrow) are combined into a single atomic transaction. If any step fails, the entire transaction reverts.

Constructor

import { MorphoClient } from '@agether/sdk';

const morpho = new MorphoClient({
  privateKey: process.env.AGENT_PRIVATE_KEY!,
  rpcUrl: 'https://base-rpc.publicnode.com',
  agentId: '42',         // Your ERC-8004 agent ID (optional for first-time registration)
  chainId: 8453,         // Base mainnet (default)
});

Configuration

ParameterTypeRequiredDescription
privateKeystringEither this or signerAgent wallet private key
signerethers.AbstractSignerEither this or privateKeyExternal signer (Bankr, Privy, Turnkey, etc.)
rpcUrlstringYesBase RPC endpoint
agentIdstringNoERC-8004 agent ID (set after registration if not known)
chainIdnumberNoChain ID (default: 8453 for Base)
contractsobjectNoOverride contract addresses

Registration

register(name?)

Register an ERC-8004 identity and create a Safe-based agent account in one call. If already registered, returns existing state.
const result = await morpho.register();
console.log('Agent ID:', result.agentId);
console.log('Account:', result.agentAccount);
console.log('Already existed:', result.alreadyRegistered);
console.log('KYA Required:', result.kyaRequired);
Return type:
interface RegisterResult {
  agentId: string;
  address: string;        // EOA wallet
  agentAccount: string;   // Safe account address
  alreadyRegistered: boolean;
  kyaRequired: boolean;
  tx?: string;            // TX hash (only if newly created)
}

Market Discovery

getMarkets(forceRefresh?)

Fetch available Morpho Blue USDC markets on Base. Returns all markets (49+) — not limited to specific collateral types. Results are cached for 5 minutes unless forceRefresh is true.
const markets = await morpho.getMarkets();

for (const market of markets) {
  console.log(`${market.collateralAsset.symbol}:`);
  console.log(`  LLTV: ${Number(market.lltv) / 1e16}%`);
  console.log(`  Utilization: ${(market.utilization * 100).toFixed(1)}%`);
  console.log(`  Market Key: ${market.uniqueKey}`);
}
Markets are fetched from the Morpho GraphQL API, filtered for USDC loan markets on Base.

getMarketRates(collateralSymbolOrAddress?)

Get current supply/borrow APY and utilization for markets.
// All markets
const allRates = await morpho.getMarketRates();

// Specific collateral
const wethRates = await morpho.getMarketRates('WETH');
for (const r of wethRates) {
  console.log('Borrow APY:', (r.borrowApy * 100).toFixed(2) + '%');
  console.log('Supply APY:', (r.supplyApy * 100).toFixed(2) + '%');
  console.log('Utilization:', (r.utilization * 100).toFixed(1) + '%');
}

Borrowing Operations

depositAndBorrow(token, collateralAmount, borrowAmount)

The primary borrowing operation — deposit collateral and borrow USDC in a single atomic transaction.
const result = await morpho.depositAndBorrow('WETH', '0.1', '100');
console.log('TX:', result.tx);
console.log('Collateral:', result.collateralAmount, result.collateralToken);
console.log('Borrowed:', result.borrowAmount, 'USDC');
Under the hood, this executes as an ERC-4337 UserOp with a batched execution:
  1. EOA transfer: Collateral tokens are transferred from EOA → Safe account
  2. Safe batch (ERC-7579 execute(MODE_BATCH, ...)):
    • collateral.approve(MorphoBlue, amount)
    • Morpho.supplyCollateral(marketParams, amount, account, "")
    • Morpho.borrow(marketParams, borrowAmount, 0, account, account)

supplyCollateral(token, amount)

Deposit collateral without borrowing.
const result = await morpho.supplyCollateral('WETH', '0.5');
console.log('TX:', result.tx);

borrow(amount, token?)

Borrow USDC against existing collateral. If token is specified, borrows from that market. Otherwise auto-detects the first market with collateral.
const result = await morpho.borrow('200');
console.log('TX:', result.tx);
console.log('Borrowed:', result.amount, 'USDC');

// From specific market
const result2 = await morpho.borrow('100', 'wstETH');

repay(amount, token?)

Repay borrowed USDC. Pass "all" to repay the full debt using share-based repayment (no dust left).
// Partial repay
const result = await morpho.repay('50');
console.log('TX:', result.tx);
console.log('Remaining debt:', result.remainingDebt);

// Full repay
const result2 = await morpho.repay('all');
When repaying "all", the SDK uses share-based repayment to ensure zero dust borrow shares remain. If the Safe account doesn’t have enough USDC, the SDK automatically transfers the shortfall from the EOA.

withdrawCollateral(token, amount, marketParams?, receiver?)

Withdraw collateral from Morpho. Pass "all" for full withdrawal. By default, collateral is sent to the EOA wallet.
await morpho.withdrawCollateral('WETH', '0.05');
await morpho.withdrawCollateral('WETH', 'all'); // After repaying all debt
Withdrawing collateral reduces your Health Factor. If it drops below 1.0, your position can be liquidated. If dust borrow shares remain, the SDK auto-repays them before withdrawing.

Supply Operations (Earn Yield)

These methods let your agent supply USDC as a lender to Morpho Blue markets to earn yield from borrowers. This is separate from depositing collateral — you’re lending USDC to earn interest.

supplyAsset(usdcAmount, collateralSymbol?)

Supply USDC to a Morpho Blue market as a lender to earn yield.
// Supply to highest-APY market (auto-selected)
const result = await morpho.supplyAsset('500');
console.log('TX:', result.tx);

// Supply to specific market
const result2 = await morpho.supplyAsset('100', 'WETH');
console.log('Supplied $100 to WETH/USDC market');
If the Safe account doesn’t have enough USDC, the SDK automatically transfers the needed amount from the EOA wallet.

withdrawSupply(usdcAmount, collateralSymbol?, receiver?)

Withdraw supplied USDC (principal + earned interest) from a Morpho Blue lending position. Pass "all" to withdraw the entire position.
// Partial withdrawal
const result = await morpho.withdrawSupply('100');
console.log('TX:', result.tx);

// Full withdrawal (all principal + earned yield)
const result2 = await morpho.withdrawSupply('all', 'WETH');

// Withdraw to a specific address
await morpho.withdrawSupply('all', 'WETH', '0xReceiverAddress');

getSupplyPositions(collateralSymbol?)

Get all supply (lending) positions with earned yield. Reads Morpho events directly from blockchain — no database needed.
// All supply positions
const positions = await morpho.getSupplyPositions();

for (const pos of positions) {
  console.log(`${pos.market}:`);
  console.log(`  Supplied: $${pos.currentValue}`);
  console.log(`  Net Deposited: $${pos.netDeposited}`);
  console.log(`  Earned Yield: $${pos.earnedYield}`);
  console.log(`  APY: ${pos.supplyApy}%`);
}

// Filter by market
const wethPositions = await morpho.getSupplyPositions('WETH');

Position Management

getStatus()

Returns all borrow positions across all discovered markets.
const status = await morpho.getStatus();
console.log('Agent:', status.agentId);
console.log('Account:', status.agentAccount);
console.log('Total Debt:', status.totalDebt, 'USDC');

for (const pos of status.positions) {
  console.log(`${pos.collateralToken}/USDC:`);
  console.log(`  Collateral: ${pos.collateral}`);
  console.log(`  Debt: ${pos.debt} USDC`);
  console.log(`  Borrow Shares: ${pos.borrowShares}`);
}

getBalances()

Get token balances for both the EOA and Safe account, including collateral tokens.
const balances = await morpho.getBalances();
console.log('Agent ID:', balances.agentId);
console.log('EOA ETH:', balances.eth);
console.log('EOA USDC:', balances.usdc);

if (balances.agentAccount) {
  console.log('Account:', balances.agentAccount.address);
  console.log('Account USDC:', balances.agentAccount.usdc);
}

getMaxBorrowable()

Calculate maximum additional USDC that can be borrowed given current collateral and debt.
const result = await morpho.getMaxBorrowable();
console.log('Total additional borrowable:', result.total.toString(), 'USDC (raw)');

for (const market of result.byMarket) {
  console.log(`${market.collateralToken}:`);
  console.log(`  Max additional: ${market.maxAdditional.toString()}`);
}

Reputation

getCreditScore()

Read the agent’s credit score from the Agether8004Scorer contract.
const score = await morpho.getCreditScore();
console.log('Score:', score.toString());

isEligible(minScore?)

Check if the agent meets a minimum score threshold.
const { eligible, currentScore } = await morpho.isEligible(500n);

isScoreFresh()

Check if the score is within the valid attestation window.
const { fresh, age } = await morpho.isScoreFresh();

Agent-to-Agent

Send collateral tokens to another agent’s account.
const result = await morpho.sponsor(
  { agentId: '42' }, // or { address: '0x...' }
  'WETH',
  '0.1'
);
console.log('TX:', result.tx);
console.log('Target account:', result.targetAccount);

fundAccount(usdcAmount)

Transfer USDC from EOA to Safe account.
const result = await morpho.fundAccount('500');
console.log('TX:', result.tx);
console.log('Amount:', result.amount, 'USDC');

Complete Example

import { MorphoClient } from '@agether/sdk';

async function main() {
  const morpho = new MorphoClient({
    privateKey: process.env.AGENT_PRIVATE_KEY!,
    rpcUrl: 'https://base-rpc.publicnode.com',
  });

  // Register
  const { agentId, agentAccount } = await morpho.register();
  console.log('Agent registered:', agentId, '→', agentAccount);

  // Check available markets (49+ on Base)
  const markets = await morpho.getMarkets();
  console.log('Available markets:', markets.length);

  // === Borrowing ===
  // Deposit 0.1 WETH and borrow 100 USDC
  await morpho.depositAndBorrow('WETH', '0.1', '100');

  // === Earning Yield ===
  // Supply $50 USDC to earn interest from borrowers
  await morpho.supplyAsset('50', 'WETH');

  // Check supply position
  const positions = await morpho.getSupplyPositions();
  console.log('Supply positions:', positions);

  // === Cleanup ===
  await morpho.withdrawSupply('all', 'WETH');
  await morpho.repay('all');
  await morpho.withdrawCollateral('WETH', 'all');

  console.log('Done — all positions closed');
}

main().catch(console.error);