Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
starchild-ai-agent avatar

Woofi Yield

  • 1 installs
  • 1 repo stars
  • Updated July 29, 2026
  • starchild-ai-agent/community-skills

Reference for deposit/withdraw flows, contract interfaces, pool registries, and GraphQL APIs across Aave V3/V4, Spark, and Morpho lending vaults on 10+ chains.

About

Provides contract interfaces, pool/vault address registries, operation flows, and GraphQL APR queries for Aave V3/V4, Spark (Savings + SparkLend), and Morpho V2 across 10+ chains. A developer uses it to build deposit/withdraw flows into DeFi lending and ERC-4626 yield vaults.

  • Covers Aave V3 Pool and V4 Hub-and-Spoke plus reserveId lookups
  • ERC-4626 deposit/withdraw/redeem flows for Spark and Morpho vaults

Woofi Yield by the numbers

  • 1 all-time installs (skills.sh)
  • Ranked #426 of 479 Web3 & Blockchain skills by installs in the Skillselion catalog
  • Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/starchild-ai-agent/community-skills --skill woofi-yield

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs1
repo stars1
Last updatedJuly 29, 2026
Repositorystarchild-ai-agent/community-skills

What it does

Reference for deposit/withdraw flows, contract interfaces, pool registries, and GraphQL APIs across Aave V3/V4, Spark, and Morpho lending vaults on 10+ chains.

Files

SKILL.mdMarkdownGitHub ↗

Woofi Yield Skill — Aave, Spark & Morpho Deposit / Withdraw

Aave

Aave V3

1. Overview

Aave V3 is the largest decentralized lending protocol. Users supply an underlying asset (e.g. USDC) and receive an aToken in return. aTokens are minted 1:1 with the supplied asset; their balance grows automatically over time to reflect accrued interest.

Key points:

  • V3 uses supply() (renamed from V2's deposit())
  • aToken balances rebase — no need to claim rewards separately
  • supplyWithPermit() is available for gasless approvals on EIP-2612 tokens

Supported chains:

ChainchainId
Ethereum1
Arbitrum42161
Optimism10
Polygon137
Avalanche43114
Base8453
BNB Chain56
Gnosis100
Scroll534352
Linea59144
Sonic146
2. Pool & Token Registry
Pool Addresses
ChainchainIdPool Address
Ethereum10x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2
Arbitrum421610x794a61358D6845594F94dc1DB02A252b5b4814aD
Optimism100x794a61358D6845594F94dc1DB02A252b5b4814aD
Polygon1370x794a61358D6845594F94dc1DB02A252b5b4814aD
Avalanche431140x794a61358D6845594F94dc1DB02A252b5b4814aD
Base84530xA238Dd80C259a72e81d7e4664a9801593F98d1c5
BNB Chain560x6807dc923806fE8Fd134338EABCA509979a7e0cB
Gnosis1000xb50201558B00496A145fE76f7424749556E326D8
Scroll5343520x11fCfe756c05AD438e312a7fd934381537D3cFfe
Linea591440xc47b8C00b0f69a36fa203Ffeac0334874574a8Ac
USDC Reserves
ChainUnderlyingaToken
Ethereum0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB480x98C23E9d8f34FEFb1B7BD6a91B7FF122F4e16F5c
Arbitrum0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC80x625E7708f30cA75bfd92586e17077590C60eb4cD
Optimism0x7F5c764cBc14f9669B88837ca1490cCa17c316070x625E7708f30cA75bfd92586e17077590C60eb4cD
Polygon0x2791Bca1f2de4661ED88A30C99A7a9449Aa841740x625E7708f30cA75bfd92586e17077590C60eb4cD
Avalanche0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E0x625E7708f30cA75bfd92586e17077590C60eb4cD
Base0x833589fCD6eDb6E08f4c7C32D4f71b54bdA029130x4e65fE4DbA92790696d040ac24Aa414708F5c0AB
BNB Chain0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d0x00901a076785e0906d1028c7d6372d247bec7d61
Gnosis0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A830xc6B7AcA6DE8a6044E0e32d0c841a89244A10D284
Scroll0x06eFdBFf2a14a7c8E15944D1F4A48F9F95F663A40x1D738a3436A8C49CefFbaB7fbF04B660fb528CbD
Linea0x176211869cA2b568f2A7D4EE941E073a821EE1ff0x374D7860c4f2f604De0191298dD393703Cce84f3
Sonic0x29219dd400f2Bf60E5a23d13Be72B486D40388940x578Ee1ca3a8E1b54554Da1Bf7C583506C4CD11c6
USDT Reserves

Sonic, Scroll, and Gnosis do not have USDT reserves on Aave V3.

ChainUnderlyingaToken
Ethereum0xdAC17F958D2ee523a2206206994597C13D831ec70x23878914EFE38d27C4D67Ab83ed1b93A74D4086a
Arbitrum0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb90x6ab707Aca953eDAeFBc4fD23bA73294241490620
Optimism0x94b008aA00579c1307B0EF2c499aD98a8ce58e580x6ab707Aca953eDAeFBc4fD23bA73294241490620
Polygon0xc2132D05D31c914a87C6611C10748AEb04B58e8F0x6ab707Aca953eDAeFBc4fD23bA73294241490620
Avalanche0x9702230A8Ea53601f5cD2dc00fDBc13d4dF4A8c70x6ab707Aca953eDAeFBc4fD23bA73294241490620
BNB Chain0x55d398326f99059fF775485246999027B31979550xa9251ca9DE909CB71783723713B21E4233fbf1B1
Linea0xA219439258ca9da29E9Cc4cE5596924745e12B930x88231dfEC71D4FF5c1e466D08C321944A7adC673
WETH Reserves
ChainUnderlyingaTokenNote
Ethereum0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc20x4d5F47FA6A74757f35C14fD3a6Ef8E3C9BC514E8
Arbitrum0x82aF49447D8a07e3bd95BD0d56f35241523fBab10xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8
Optimism0x42000000000000000000000000000000000000060xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8
Polygon0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f6190xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8
Avalanche0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8WETH.e
Base0x42000000000000000000000000000000000000060xD4a0e0b9149BCee3C920d2E00b5dE09138fd8bb7
BNB Chain0x2170Ed0880ac9A755fd29B2688956BD959F933F80x2E94171493fAbE316b6205f1585779C887771E2FETH
Gnosis0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e10xa818F1B57c201E092C4A2017A91815034326Efd1
Scroll0x53000000000000000000000000000000000000040xf301805bE1Df81102C957f6d4Ce29d2B8c056B2a
Linea0xe5D7C2a44FfDDf6b295A15c148167daaAf5Cf34f0x787897dF92703BB3Fc4d9Ee98e15C0b8130Bf163
Sonic0x50c42dEAcD8Fc9773493ED674b675bE577f2634b0xe18Ab82c81E7Eecff32B8A82B1b7d2d23F1EcE96
WBTC / BTC Reserves

Sonic, Scroll, and Gnosis do not have WBTC reserves on Aave V3.

ChainUnderlyingaTokenNote
Ethereum0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C5990x5Ee5bf7ae06D1Be5997A1A72006FE6C607eC6DE8WBTC
Arbitrum0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f0x078f358208685046a11C85e8ad32895DED33A249WBTC
Optimism0x68f180fcCe6836688e9084f035309E29Bf0A20950x078f358208685046a11C85e8ad32895DED33A249WBTC
Polygon0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD60x078f358208685046a11C85e8ad32895DED33A249WBTC
Avalanche0x50b7545627a5162F82A992c33b87aDc75187B2180x078f358208685046a11C85e8ad32895DED33A249WBTC.e
Base0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf0xBdb9300b7CDE636d9cD4AFF00f6F009fFBBc8EE6cbBTC
BNB Chain0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c0x56a7ddc4e848EbF43845854205ad71D5D5F72d3DBTCB
Linea0x3aAB2285ddcDdaD8edf438C1bAB47e1a9D05a9b40x37f7E06359F98162615e016d0008023D910bB576WBTC
3. Pool Contract Interface
Write Functions
// Supply `amount` of `asset` to the pool on behalf of `onBehalfOf`
// referralCode is deprecated — pass 0
function supply(
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode
) external;

// Withdraw `amount` of `asset` from the pool, sending to `to`
// Pass type(uint256).max to withdraw the entire balance
function withdraw(
    address asset,
    uint256 amount,
    address to
) external returns (uint256);

// Supply with EIP-2612 permit (gasless approval)
function supplyWithPermit(
    address asset,
    uint256 amount,
    address onBehalfOf,
    uint16 referralCode,
    uint256 deadline,
    uint8 permitV,
    bytes32 permitR,
    bytes32 permitS
) external;
ERC-20 Approve (prerequisite for supply)
// Approve the Pool to spend underlying tokens on behalf of msg.sender
IERC20(underlying).approve(poolAddress, amount);
4. Operation Flows
Deposit Flow
1. Resolve the Pool address and underlying token address for the target chain/asset.

2. Check user balance:
   balance_raw = IERC20(underlying).balanceOf(user)

3. Approve Pool to spend underlying:
   IERC20(underlying).approve(pool_address, amount_raw)

4. Execute supply:
   pool.supply(underlying, amount_raw, user, 0)
   → User receives aTokens automatically (1:1 with supplied amount)
Withdraw Flow
1. Resolve the Pool address and underlying token address.

2. Check aToken balance (= withdrawable amount):
   aBalance_raw = IERC20(aToken).balanceOf(user)

3. Execute withdraw:
   pool.withdraw(underlying, amount_raw, user)
   → aTokens are burned, underlying tokens sent to `user`

   For full withdrawal, pass type(uint256).max as amount:
   pool.withdraw(underlying, type(uint256).max, user)
Native ETH Handling

Aave V3 does not accept native ETH directly. To supply/withdraw native ETH, use the WrappedTokenGatewayV3 contract:

// Deposit native ETH (wraps to WETH internally)
function depositETH(address pool, address onBehalfOf, uint16 referralCode) external payable;

// Withdraw to native ETH (unwraps WETH internally)
// User must approve aWETH to the gateway first
function withdrawETH(address pool, uint256 amount, address to) external;
5. Aave V3 GraphQL API

Endpoint: https://api.v3.aave.com/graphql

Query Reserve APY
query {
  reserves {
    chain { name chainId }
    asset { underlying { symbol address } }
    summary { supplyApy { normalized } borrowApy { normalized } }
    canBorrow
    canSupply
  }
}

The supplyApy.normalized field returns the annualized supply rate as a decimal (e.g. 0.035 = 3.5% APY).

Aave V4

1. Overview

Aave V4 launched on Ethereum mainnet on 2026-03-30. It introduces a Hub-and-Spoke architecture: users interact with Spoke contracts, and funds flow to a shared Liquidity Hub. This replaces V3's per-chain isolated Pool model.

Key points:

  • Users call supply() / withdraw() on a Spoke contract (not a Pool)
  • Reserves are identified by reserveId (uint256) instead of asset address
  • No referralCode parameter
  • Three Hubs: Core, Prime, Plus — each Spoke is connected to one Hub
  • Currently Ethereum only

Supported chains:

ChainchainId
Ethereum1
2. Hub & Spoke Registry
Hub Addresses (Ethereum)
HubAddress
Core0xCca852Bc40e560adC3b1Cc58CA5b55638ce826c9
Plus0x06002e9c4412CB7814a791eA3666D905871E536A
Prime0x943827DCA022D0F354a8a8c332dA1e5Eb9f9F931
Spoke Addresses (Ethereum)
SpokeAddress
Main0x94e7A5dCbE816e498b89aB752661904E2F56c485
Bluechip0x973a023A77420ba610f06b3858aD991Df6d85A08
Lido E0xe1900480ac69f0B296841Cd01cC37546d92F35Cd
EtherFi E0xbF10BDfE177dE0336aFD7fcCF80A904E15386219
Kelp E0x3131FE68C4722e726fe6B2819ED68e514395B9a4
Ethena Correlated0x58131E79531caB1d52301228d1f7b842F26B9649
Ethena Ecosystem0xba1B3D55D249692b669A164024A838309B7508AF
Lombard BTC0x7EC68b5695e803e98a21a9A05d744F28b0a7753D
Forex0xD8B93635b8C6d0fF98CbE90b5988E3F2d1Cd9da1
Gold0x65407b940966954b23dfA3caA5C0702bB42984DC
Peripheral Contracts (Ethereum)
ContractAddress
Native Token Gateway0xe68ab4F90Fe026B9873F5F276eD2d7efBbbE42Be
Signature Gateway0xfbC184337Dc6595D8bf62968Bda46e7De7AF9c3d
Token Addresses (Ethereum)
TokenAddress
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
USDT0xdAC17F958D2ee523a2206206994597C13D831ec7
WETH0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
WBTC0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599
3. Spoke Contract Interface
Write Functions
// Supply `amount` of the reserve identified by `reserveId` on behalf of `onBehalfOf`
// Returns (suppliedAmount, mintedShares)
function supply(
    uint256 reserveId,
    uint256 amount,
    address onBehalfOf
) external returns (uint256, uint256);

// Withdraw `amount` of the reserve identified by `reserveId` on behalf of `onBehalfOf`
// Returns (withdrawnAmount, burnedShares)
function withdraw(
    uint256 reserveId,
    uint256 amount,
    address onBehalfOf
) external returns (uint256, uint256);
ERC-20 Approve (prerequisite for supply)
// Approve the Spoke contract to spend underlying tokens on behalf of msg.sender
IERC20(underlying).approve(spokeAddress, amount);
4. Operation Flows
Deposit Flow
1. Identify the target Spoke address and look up the reserveId for the asset (see Section 5).

2. Check user balance:
   balance_raw = IERC20(underlying).balanceOf(user)

3. Approve Spoke to spend underlying:
   IERC20(underlying).approve(spoke_address, amount_raw)

4. Execute supply:
   (suppliedAmount, mintedShares) = spoke.supply(reserveId, amount_raw, user)
Withdraw Flow
1. Identify the target Spoke address and look up the reserveId for the asset (see Section 5).

2. Execute withdraw:
   (withdrawnAmount, burnedShares) = spoke.withdraw(reserveId, amount_raw, user)
   → Underlying tokens are sent to `user`
Native ETH Handling

Aave V4 provides a Native Token Gateway for supplying/withdrawing native ETH without manual wrapping:

Gateway address: 0xe68ab4F90Fe026B9873F5F276eD2d7efBbbE42Be
5. Reserve ID Lookup

Aave V4 identifies reserves by reserveId (uint256), not by asset address. To obtain the reserveId for a given asset, perform a two-step on-chain lookup:

Function Signatures
// Step 1 — on the Hub contract
// Returns the Hub-internal asset ID for the given underlying token address
function getAssetId(address underlying) external view returns (uint256);

// Step 2 — on the Spoke contract
// Maps (hub, assetId) → reserveId used in supply()/withdraw()
function getReserveId(address hub, uint256 assetId) external view returns (uint256);
Lookup Example
hub_address   = <Hub address from Section 2>       // e.g. Core Hub
spoke_address = <Spoke address from Section 2>     // e.g. Main Spoke

// Step 1: get the Hub-internal asset ID
assetId   = hub.getAssetId(USDC_ADDRESS)

// Step 2: convert to Spoke-specific reserveId
reserveId = spoke.getReserveId(hub_address, assetId)

// Now use reserveId in supply()/withdraw()
spoke.supply(reserveId, amount, user)

Note: reserveId values are per-Spoke — the same underlying asset can have different reserveId values across different Spokes. Always look up the reserveId for the specific Spoke you intend to interact with.

Additional View Functions on Spoke
FunctionReturnsDescription
getReserve(uint256 reserveId)Reserve structFull reserve details: underlying address, hub, assetId, decimals, collateralRisk, flags
getReserveCount()uint256Total number of reserves in the Spoke
getReserveConfig(uint256 reserveId)ReserveConfigCollateral risk parameters, paused/frozen status
6. Aave V4 GraphQL API

Endpoint: https://api.v4.aave.com/graphql

Query Reserve APY
query {
  reserves {
    chain { name chainId }
    asset { underlying { symbol address } }
    summary { supplyApy { normalized } borrowApy { normalized } }
    canBorrow
    canSupply
  }
}

The supplyApy.normalized field returns the annualized supply rate as a decimal (e.g. 0.035 = 3.5% APY).

7. Key Differences from V3
AspectAave V3Aave V4
ArchitectureIsolated Pool per chainHub-and-Spoke, shared Liquidity Hub
Entry pointPool.supply(asset, ...)Spoke.supply(reserveId, ...)
Reserve identifieraddress assetuint256 reserveId
Referral codeuint16 referralCode (pass 0)Removed
Return valuesNone (supply) / uint256 (withdraw)(uint256, uint256) tuple for both
Chains10+ chainsEthereum only (as of 2026-04)
Liquidity sharingNone — isolated per deploymentCross-Spoke via shared Hub
Native ETH gatewayWrappedTokenGatewayV3Native Token Gateway

Spark

1. Overview

Spark is a DeFi platform within the Sky (formerly MakerDAO) ecosystem, comprising two product lines:

  • Spark Savings — ERC-4626 vaults (spUSDC, spUSDT, spETH, etc.) that optimize yield via the Spark Liquidity Layer
  • SparkLend (Markets) — Aave V3 fork lending pool; supply assets to receive spTokens

Savings products span 6 chains; SparkLend is deployed only on Ethereum and Gnosis.

Supported chains:

ChainchainIdSavingsSparkLend
Ethereum1YesYes
Base8453YesNo
Arbitrum42161YesNo
Optimism10YesNo
Unichain130YesNo
Gnosis100YesYes

---

2. Spark Savings — Vault Registry

Product categories:

  • V2 Vaults (spUSDC, spUSDT, spETH) — Spark-native ERC-4626 vaults deploying strategies via the Liquidity Layer
  • Legacy (sUSDS, sDAI) — Sky protocol Savings Rate wrappers (not listed here)

Only USDC / USDT / ETH / BTC related vaults are listed below.

spUSDC (Spark USDC Vault)

ChainVault AddressUnderlying (USDC)
Ethereum0x28B3a8fb53B741A8Fd78c0fb9A6B2393d896a43d0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Avalanche0x28B3a8fb53B741A8Fd78c0fb9A6B2393d896a43d0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E
Note: Avalanche has spUSDC in the address registry. Base / Arbitrum / Optimism / Unichain have a different savings product (sUSDC) that is a USDS-backed wrapper, not a direct USDC vault.

sUSDC (Savings USDC — USDS-backed)

ChainVault AddressUnderlying (USDC)
Base0x3128a0F7f0ea68E7B7c9B00AFa7E41045828e8580x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Arbitrum0x940098b108fB7D0a7E374f6eDED77607874646090xaf88d065e77c8cC2239327C5EDb3A432268e5831
Optimism0xCF9326e24EBfFBEF22ce1050007A43A3c0B6DB550x0b2C639c533813f4Aa9D7837CAf62653d097Ff85
Unichain0x14d9143BEcC348920b68D123687045db49a016C60x078D782b760474a361dDA0AF3839290b0EF57AD6

spUSDT (Spark USDT Vault — Ethereum only)

ChainVault AddressUnderlying (USDT)
Ethereum0xe2e7a17dFf93280dec073C995595155283e3C3720xdAC17F958D2ee523a2206206994597C13D831ec7

spETH (Spark ETH Vault — Ethereum only)

ChainVault AddressUnderlying (WETH)
Ethereum0xfE6eb3b609a7C8352A241f7F3A21CEA4e9209B8f0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2

BTC

No Spark Savings vault exists for BTC.

---

3. Spark Savings — ERC-4626 Contract Interface

All Spark Savings vaults (spUSDC, spUSDT, spETH, sUSDC) implement the standard ERC-4626 interface.

Read Functions (view)

// Returns the address of the underlying ERC-20 asset
function asset() external view returns (address);

// Total amount of the underlying asset managed by the vault
function totalAssets() external view returns (uint256);

// Vault share balance of `account`
function balanceOf(address account) external view returns (uint256);

// Convert `shares` to asset amount (no fees, no slippage)
function convertToAssets(uint256 shares) external view returns (uint256);

// Convert `assets` to share amount (no fees, no slippage)
function convertToShares(uint256 assets) external view returns (uint256);

// Preview how many shares a deposit of `assets` would yield
function previewDeposit(uint256 assets) external view returns (uint256);

// Preview how many assets a redeem of `shares` would yield
function previewRedeem(uint256 shares) external view returns (uint256);

Write Functions (state-changing)

// Deposit `assets` of underlying token, mint shares to `receiver`
function deposit(uint256 assets, address receiver) external returns (uint256 shares);

// Withdraw exactly `assets`, burning shares from `owner`, sending assets to `receiver`
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

// Redeem exactly `shares`, sending resulting assets to `receiver` (preferred for full withdrawal)
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);

ERC-20 Approve (prerequisite for deposit)

// Approve the vault to spend underlying tokens on behalf of msg.sender
IERC20(underlying).approve(vaultAddress, amount);

---

4. Spark Savings — Operation Flows

Deposit Flow

1. Query underlying token address:
   underlying = vault.asset()

2. Check user balance:
   balance_raw = IERC20(underlying).balanceOf(user)

3. Approve vault to spend underlying:
   IERC20(underlying).approve(vault_address, amount_raw)

4. Preview expected shares:
   expected_shares = vault.previewDeposit(amount_raw)

5. Execute deposit:
   shares = vault.deposit(amount_raw, receiver)

Withdraw Flow (prefer redeem)

Using redeem is preferred over withdraw because it operates on shares (which the user holds) rather than a specific asset amount, avoiding rounding issues.

1. Check user share balance:
   shares = vault.balanceOf(user)

2. Preview expected assets:
   expected_assets = vault.previewRedeem(shares)

3. Execute redeem:
   assets = vault.redeem(shares, receiver, owner)

Full Withdrawal

vault.redeem(vault.balanceOf(user), user, user)

Liquidity Notes

  • No lock-up period
  • V2 vaults retain ~10% idle liquidity for instant withdrawals
  • Large withdrawals are handled via Savings Liquidity Intents (typically fulfilled within minutes)

---

5. SparkLend (Markets) — Pool & Token Registry

SparkLend is an Aave V3 fork deployed only on Ethereum and Gnosis.

Pool Addresses

ChainchainIdPool AddressWETH Gateway
Ethereum10xC13e21B648A5Ee794902342038FF3aDAB66BE9870xBD7D6a9ad7865463DE44B05F04559f65e3B11704
Gnosis1000x2Dae5307c5E3FD1CF5A72Cb6F698f915860607e00xBD7D6a9ad7865463DE44B05F04559f65e3B11704

Ethereum Token Reserves

AssetUnderlyingspToken
USDC0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB480x377C3bd93f2a2984E1E7bE6A5C22c525eD4A4815
USDT0xdAC17F958D2ee523a2206206994597C13D831ec70xe7dF13b8e3d6740fe17CBE928C7334243d86c92f
WETH0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc20x59cD1C87501baa753d0B5B5Ab5D8416A45cD71DB
WBTC0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C5990x4197ba364AE6698015AE5c1468f54087602715b2

Gnosis Token Reserves

AssetUnderlyingspToken
USDC0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A830x5850D127a04ed0B4F1FCDFb051b3409FB9Fe6B90
USDT0x4ECaBa5870353805a9F068101A40E0f32ed605C60x08B0cAebE352c3613302774Cd9B82D08afd7bDC4
WETH0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e10x629D562E92fED431122e865Cc650Bc6bdE6B96b0
Note: Gnosis does not have a WBTC reserve.

---

6. SparkLend — Pool Contract Interface

SparkLend uses the same interface as Aave V3 (it is an Aave V3 fork).

Read Functions (view)

// Returns reserve data including aToken address, liquidity index, current rates
function getReserveData(address asset) external view returns (DataTypes.ReserveData memory);

// Returns user account data (total collateral, total debt, available borrows, etc.)
function getUserAccountData(address user) external view returns (
    uint256 totalCollateralBase,
    uint256 totalDebtBase,
    uint256 availableBorrowsBase,
    uint256 currentLiquidationThreshold,
    uint256 ltv,
    uint256 healthFactor
);

Write Functions (state-changing)

// Supply `amount` of `asset` on behalf of `onBehalfOf`; referralCode is currently inactive (pass 0)
function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;

// Supply with ERC-2612 permit (single tx approve + supply)
function supplyWithPermit(
    address asset, uint256 amount, address onBehalfOf, uint16 referralCode,
    uint256 deadline, uint8 permitV, bytes32 permitR, bytes32 permitS
) external;

// Withdraw `amount` of `asset` to `to`; pass type(uint256).max for full withdrawal
function withdraw(address asset, uint256 amount, address to) external returns (uint256);

ERC-20 Approve (prerequisite for supply)

// Approve the Pool to spend underlying tokens on behalf of msg.sender
IERC20(underlying).approve(poolAddress, amount);

---

7. SparkLend — Operation Flows

Deposit (Supply) Flow

1. Check user balance:
   balance_raw = IERC20(underlying).balanceOf(user)

2. Approve Pool to spend underlying:
   IERC20(underlying).approve(pool_address, amount_raw)

3. Execute supply:
   pool.supply(underlying, amount_raw, user, 0)
   // referralCode = 0 (inactive)

Withdraw Flow

1. Check user spToken balance:
   sp_balance = IERC20(spToken).balanceOf(user)

2. Execute withdraw:
   pool.withdraw(underlying, amount_raw, user)

Full Withdrawal

pool.withdraw(underlying, type(uint256).max, user)

Native ETH via WETH Gateway

For native ETH deposits/withdrawals, use the WETH Gateway contract instead of the Pool directly:

// Deposit native ETH
IWETHGateway(gateway).depositETH{value: amount}(pool_address, onBehalfOf, 0);

// Withdraw to native ETH (requires spWETH approval to gateway)
IWETHGateway(gateway).withdrawETH(pool_address, amount, to);

---

8. Spark API

  • No dedicated REST/GraphQL API (unlike Aave or Morpho)
  • Rate queries: via on-chain getReserveData() or The Graph subgraph
  • Savings rates: via ERC-4626 convertToAssets() method or SSR Oracle

---

9. Data Sources

All contract addresses sourced from:

  • github.com/sparkdotfi/spark-address-registry (Ethereum.sol, Base.sol, Arbitrum.sol, Optimism.sol, Unichain.sol, Gnosis.sol)
  • github.com/marsfoundation/sparklend-deployments (primary-latest.json for chain 1 and 100)
  • Etherscan / GnosisScan verification

Morpho

1. Overview

Morpho V2 vaults are ERC-4626 standard tokenized vaults. Users deposit an underlying asset (e.g. USDC) and receive vault shares representing their proportional claim on the vault's total assets.

Supported chains:

ChainchainId
Ethereum1
Monad143

---

2. Vault Registry

All vaults below are Morpho V2; V1 is excluded.

USDC Vaults
Vault NameChainVault AddressUnderlyingURL
Steakhouse Reservoir USDCEthereum0xBEeFF047C03714965a54b671A37C18beF6b96210USDChttps://app.morpho.org/ethereum/vault/0xBEeFF047C03714965a54b671A37C18beF6b96210/steakhouse-reservoir-usdc
SkyMoney USDC Risk CapitalEthereum0x56bfa6f53669B836D1E0Dfa5e99706b12c373ecfUSDChttps://app.morpho.org/ethereum/vault/0x56bfa6f53669B836D1E0Dfa5e99706b12c373ecf/skymoney-usdc-risk-capital
Re Ecosystem VaultEthereum0xD1E9242e075Db4bdd3f3c721D7d5fd4180A94A7eUSDChttps://app.morpho.org/ethereum/vault/0xD1E9242e075Db4bdd3f3c721D7d5fd4180A94A7e/re-ecosystem-vault
USDT Vaults
Vault NameChainVault AddressUnderlyingURL
SkyMoney USDT SavingsEthereum0x23f5E9c35820f4baB695Ac1F19c203cC3f8e1e11USDThttps://app.morpho.org/ethereum/vault/0x23f5E9c35820f4baB695Ac1F19c203cC3f8e1e11/skymoney-usdt-savings
Steakhouse High Yield InstantEthereum0xbeeff07d991C04CD640DE9F15C08ba59c4FEDEb7USDThttps://app.morpho.org/ethereum/vault/0xbeeff07d991C04CD640DE9F15C08ba59c4FEDEb7/steakhouse-high-yield-instant
Steakhouse High Yield USDT0Monad0xbeeff300E9A9caeC7beEA740ab8758D33b777509USDT0https://app.morpho.org/monad/vault/0xbeeff300E9A9caeC7beEA740ab8758D33b777509/steakhouse-high-yield-usdt0
ETH Vaults
Vault NameChainVault AddressUnderlyingURL
Steakhouse Prime ETHMonad0xbeef04b01e0275D4ac2e2986256BB14E3Ff6ef42ETHhttps://app.morpho.org/monad/vault/0xbeef04b01e0275D4ac2e2986256BB14E3Ff6ef42/steakhouse-prime-eth
Steakhouse Prime InstantEthereum0xbeef0046fcab1dE47E41fB75BB3dC4Dfc94108E3ETHhttps://app.morpho.org/ethereum/vault/0xbeef0046fcab1dE47E41fB75BB3dC4Dfc94108E3/steakhouse-prime-instant
KPK ETH Prime V2Ethereum0xBb50A5341368751024ddf33385BA8cf61fE65FF9ETHhttps://app.morpho.org/ethereum/vault/0xBb50A5341368751024ddf33385BA8cf61fE65FF9/kpk-eth-prime-v2
BTC Vaults
Vault NameChainVault AddressUnderlyingURL
Hyperithm cbBTC ApexMonad0xe09A93786275546690247d70f1767cF0b69e8Ea0cbBTChttps://app.morpho.org/monad/vault/0xe09A93786275546690247d70f1767cF0b69e8Ea0/hyperithm-cbbtc-apex

---

3. ERC-4626 Contract Interface

All Morpho V2 vaults implement the ERC-4626 standard. Below are the core function signatures.

Read Functions (view)
// Returns the address of the underlying ERC-20 asset
function asset() external view returns (address);

// Total amount of the underlying asset managed by the vault
function totalAssets() external view returns (uint256);

// Vault share balance of `account`
function balanceOf(address account) external view returns (uint256);

// Convert `shares` to asset amount (no fees, no slippage)
function convertToAssets(uint256 shares) external view returns (uint256);

// Convert `assets` to share amount (no fees, no slippage)
function convertToShares(uint256 assets) external view returns (uint256);

// Preview how many shares a deposit of `assets` would yield
function previewDeposit(uint256 assets) external view returns (uint256);

// Preview how many assets a redeem of `shares` would yield
function previewRedeem(uint256 shares) external view returns (uint256);
Write Functions (state-changing)
// Deposit `assets` of underlying token, mint shares to `receiver`
function deposit(uint256 assets, address receiver) external returns (uint256 shares);

// Withdraw exactly `assets`, burning shares from `owner`, sending assets to `receiver`
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

// Redeem exactly `shares`, sending resulting assets to `receiver` (preferred for full withdrawal)
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
ERC-20 Approve (prerequisite for deposit)
// Approve the vault to spend underlying tokens on behalf of msg.sender
IERC20(underlying).approve(vaultAddress, amount);

---

4. Operation Flows

Deposit Flow
1. Query underlying token address:
   underlying = vault.asset()

2. Check user balance:
   balance_raw = IERC20(underlying).balanceOf(user)

3. Approve vault to spend underlying:
   IERC20(underlying).approve(vault_address, amount_raw)

4. Preview expected shares:
   expected_shares = vault.previewDeposit(amount_raw)

5. Execute deposit:
   shares = vault.deposit(amount_raw, receiver)
Withdraw Flow (prefer redeem)

Using redeem is preferred over withdraw because it operates on shares (which the user holds) rather than a specific asset amount, avoiding rounding issues.

1. Check user share balance:
   shares = vault.balanceOf(user)

2. Preview expected assets:
   expected_assets = vault.previewRedeem(shares)

3. Execute redeem:
   assets = vault.redeem(shares, receiver, owner)
Full Withdrawal
vault.redeem(vault.balanceOf(user), user, user)

---

5. Morpho GraphQL API

Endpoint: https://api.morpho.org/graphql

Query Vault APY
query {
  vaultByAddress(address: "0xBEeFF047C03714965a54b671A37C18beF6b96210", chainId: 1) {
    address
    symbol
    asset {
      symbol
      decimals
      address
    }
    state {
      totalAssets
      apy
      netApy
    }
  }
}
Batch Query (all tracked vaults)

Use multiple vaultByAddress calls with GraphQL aliases:

query {
  steakhouseUSDC: vaultByAddress(address: "0xBEeFF047C03714965a54b671A37C18beF6b96210", chainId: 1) {
    address
    symbol
    state { apy netApy totalAssets }
  }
  skymoneyUSDC: vaultByAddress(address: "0x56bfa6f53669B836D1E0Dfa5e99706b12c373ecf", chainId: 1) {
    address
    symbol
    state { apy netApy totalAssets }
  }
  # ... add remaining vaults
}

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.