|
| 1 | +--- |
| 2 | +title: "Integrate Borrowing" |
| 3 | +description: "Let users borrow USDC against WETH collateral with Morpho, Moonwell, or Aave on Base." |
| 4 | +keywords: ["integrate borrowing Base", "borrow USDC against WETH", "Morpho borrow", "Moonwell borrow", "Aave borrow Base"] |
| 5 | +--- |
| 6 | + |
| 7 | +import { DeFiDemo } from "/snippets/DeFiDemo.jsx" |
| 8 | + |
| 9 | +Open a collateralized loan through a third-party protocol on Base. A safe integration shows collateral value, debt, liquidation parameters, and health before and after every user-signed action. |
| 10 | + |
| 11 | +#### Demo |
| 12 | + |
| 13 | +<DeFiDemo flow="borrow" /> |
| 14 | + |
| 15 | +## Supply collateral and borrow |
| 16 | + |
| 17 | +These examples use `viem@2.55.11`, `@morpho-org/morpho-sdk@5.4.1`, `@moonwell-fi/moonwell-sdk@0.22.0`, and `@aave-dao/aave-address-book@4.65.5`. |
| 18 | + |
| 19 | +<Tabs> |
| 20 | + <Tab title="Morpho"> |
| 21 | + Prepare one Morpho bundle that supplies WETH collateral and borrows USDC from the WETH/USDC market. |
| 22 | + |
| 23 | + ```ts borrow-morpho.ts lines wrap highlight={15-23,33-35} |
| 24 | + import { type MarketId } from '@morpho-org/blue-sdk'; |
| 25 | + import { fetchMarketParams } from '@morpho-org/blue-sdk-viem'; |
| 26 | + import { |
| 27 | + isRequirementSignature, |
| 28 | + morphoViemExtension, |
| 29 | + } from '@morpho-org/morpho-sdk'; |
| 30 | + import { parseUnits } from 'viem'; |
| 31 | + import { base } from 'viem/chains'; |
| 32 | + |
| 33 | + const marketId = |
| 34 | + '0x8793cf302b8ffd655ab97bd1c695dbd967807e8367a65cb2f4edaf1380ba1bda' as MarketId; |
| 35 | + const user = walletClient.account.address; |
| 36 | + const client = publicClient.extend(morphoViemExtension()); |
| 37 | + const params = await fetchMarketParams(marketId, publicClient); |
| 38 | + const market = client.morpho.blue(params, base.id); |
| 39 | + const positionData = await market.getPositionData(user); |
| 40 | + const action = market.supplyCollateralBorrow({ |
| 41 | + amount: parseUnits('2', 18), |
| 42 | + borrowAmount: parseUnits('2000', 6), |
| 43 | + userAddress: user, |
| 44 | + positionData, |
| 45 | + }); |
| 46 | + |
| 47 | + const signatures = []; |
| 48 | + for (const requirement of await action.getRequirements()) { |
| 49 | + if (isRequirementSignature(requirement)) { |
| 50 | + signatures.push(await requirement.sign(walletClient, user)); |
| 51 | + } else { |
| 52 | + const hash = await walletClient.sendTransaction(requirement); |
| 53 | + await publicClient.waitForTransactionReceipt({ hash }); |
| 54 | + } |
| 55 | + } |
| 56 | + const request = action.buildTx(signatures); |
| 57 | + await publicClient.call({ account: user, ...request }); |
| 58 | + const hash = await walletClient.sendTransaction(request); |
| 59 | + await publicClient.waitForTransactionReceipt({ hash }); |
| 60 | + ``` |
| 61 | + </Tab> |
| 62 | + |
| 63 | + <Tab title="Moonwell"> |
| 64 | + Supply WETH to its market, enter that market as collateral, then borrow from the USDC market. |
| 65 | + |
| 66 | + ```ts borrow-moonwell.ts lines wrap highlight={19-25,27-34} |
| 67 | + import { createMoonwellClient } from '@moonwell-fi/moonwell-sdk'; |
| 68 | + import { parseAbi, parseUnits } from 'viem'; |
| 69 | + |
| 70 | + const env = createMoonwellClient({ |
| 71 | + networks: { base: { rpcUrls: ['https://mainnet.base.org'] } }, |
| 72 | + }).environments.base; |
| 73 | + const weth = env.tokens.WETH.address; |
| 74 | + const mWeth = env.tokens.MOONWELL_ETH.address; |
| 75 | + const mUsdc = env.tokens.MOONWELL_USDC.address; |
| 76 | + const comptroller = env.contracts.comptroller.address; |
| 77 | + const user = walletClient.account; |
| 78 | + |
| 79 | + const erc20Abi = parseAbi(['function approve(address,uint256) returns (bool)']); |
| 80 | + const marketAbi = parseAbi([ |
| 81 | + 'function mint(uint256) returns (uint256)', |
| 82 | + 'function borrow(uint256) returns (uint256)', |
| 83 | + ]); |
| 84 | + const comptrollerAbi = parseAbi([ |
| 85 | + 'function enterMarkets(address[]) returns (uint256[])', |
| 86 | + ]); |
| 87 | + |
| 88 | + const approval = await publicClient.simulateContract({ |
| 89 | + account: user, address: weth, abi: erc20Abi, functionName: 'approve', |
| 90 | + args: [mWeth, parseUnits('2', 18)], |
| 91 | + }); |
| 92 | + await publicClient.waitForTransactionReceipt({ |
| 93 | + hash: await walletClient.writeContract(approval.request), |
| 94 | + }); |
| 95 | + |
| 96 | + const supplied = await publicClient.simulateContract({ |
| 97 | + account: user, address: mWeth, abi: marketAbi, functionName: 'mint', |
| 98 | + args: [parseUnits('2', 18)], |
| 99 | + }); |
| 100 | + if (supplied.result !== 0n) throw new Error(`Moonwell error ${supplied.result}`); |
| 101 | + await publicClient.waitForTransactionReceipt({ |
| 102 | + hash: await walletClient.writeContract(supplied.request), |
| 103 | + }); |
| 104 | + |
| 105 | + const entered = await publicClient.simulateContract({ |
| 106 | + account: user, address: comptroller, abi: comptrollerAbi, |
| 107 | + functionName: 'enterMarkets', args: [[mWeth]], |
| 108 | + }); |
| 109 | + if (entered.result.some((code) => code !== 0n)) throw new Error('enterMarkets failed'); |
| 110 | + await publicClient.waitForTransactionReceipt({ |
| 111 | + hash: await walletClient.writeContract(entered.request), |
| 112 | + }); |
| 113 | + |
| 114 | + const loan = await publicClient.simulateContract({ |
| 115 | + account: user, address: mUsdc, abi: marketAbi, functionName: 'borrow', |
| 116 | + args: [parseUnits('2000', 6)], |
| 117 | + }); |
| 118 | + if (loan.result !== 0n) throw new Error(`Moonwell error ${loan.result}`); |
| 119 | + await publicClient.waitForTransactionReceipt({ |
| 120 | + hash: await walletClient.writeContract(loan.request), |
| 121 | + }); |
| 122 | + ``` |
| 123 | + </Tab> |
| 124 | + |
| 125 | + <Tab title="Aave"> |
| 126 | + Use Aave's official address book with the Base Aave V3 Pool. Supply WETH, explicitly enable it as collateral, then borrow USDC at the variable rate. |
| 127 | + |
| 128 | + ```ts borrow-aave.ts lines wrap highlight={13-19,31-37} |
| 129 | + import { AaveV3Base } from '@aave-dao/aave-address-book'; |
| 130 | + import { parseAbi, parseUnits } from 'viem'; |
| 131 | + |
| 132 | + const user = walletClient.account; |
| 133 | + const weth = AaveV3Base.ASSETS.WETH.UNDERLYING; |
| 134 | + const usdc = AaveV3Base.ASSETS.USDC.UNDERLYING; |
| 135 | + const collateral = parseUnits('2', 18); |
| 136 | + const erc20Abi = parseAbi(['function approve(address,uint256) returns (bool)']); |
| 137 | + const poolAbi = parseAbi([ |
| 138 | + 'function supply(address,uint256,address,uint16)', |
| 139 | + 'function setUserUseReserveAsCollateral(address,bool)', |
| 140 | + 'function borrow(address,uint256,uint256,uint16,address)', |
| 141 | + ]); |
| 142 | + |
| 143 | + const approval = await publicClient.simulateContract({ |
| 144 | + account: user, address: weth, abi: erc20Abi, functionName: 'approve', |
| 145 | + args: [AaveV3Base.POOL, collateral], |
| 146 | + }); |
| 147 | + await publicClient.waitForTransactionReceipt({ |
| 148 | + hash: await walletClient.writeContract(approval.request), |
| 149 | + }); |
| 150 | + |
| 151 | + const supplied = await publicClient.simulateContract({ |
| 152 | + account: user, address: AaveV3Base.POOL, abi: poolAbi, |
| 153 | + functionName: 'supply', args: [weth, collateral, user.address, 0], |
| 154 | + }); |
| 155 | + await publicClient.waitForTransactionReceipt({ |
| 156 | + hash: await walletClient.writeContract(supplied.request), |
| 157 | + }); |
| 158 | + |
| 159 | + const enabled = await publicClient.simulateContract({ |
| 160 | + account: user, address: AaveV3Base.POOL, abi: poolAbi, |
| 161 | + functionName: 'setUserUseReserveAsCollateral', args: [weth, true], |
| 162 | + }); |
| 163 | + await publicClient.waitForTransactionReceipt({ |
| 164 | + hash: await walletClient.writeContract(enabled.request), |
| 165 | + }); |
| 166 | + |
| 167 | + const loan = await publicClient.simulateContract({ |
| 168 | + account: user, address: AaveV3Base.POOL, abi: poolAbi, |
| 169 | + functionName: 'borrow', |
| 170 | + args: [usdc, parseUnits('2000', 6), 2n, 0, user.address], |
| 171 | + }); |
| 172 | + await publicClient.waitForTransactionReceipt({ |
| 173 | + hash: await walletClient.writeContract(loan.request), |
| 174 | + }); |
| 175 | + ``` |
| 176 | + </Tab> |
| 177 | +</Tabs> |
| 178 | + |
| 179 | +<Warning> |
| 180 | +Collateral prices, oracle updates, interest, and protocol parameters can move a position toward liquidation. Re-fetch and display health immediately before signing, and warn clearly before a transaction creates unsafe debt. |
| 181 | +</Warning> |
| 182 | + |
| 183 | +## See also |
| 184 | + |
| 185 | +- [Integrate lending](/build-on-base/integrate-defi/integrate-lending) · [Integrate an earn product](/build-on-base/integrate-defi/integrate-earn-product) |
| 186 | +- [Integrate DeFi overview](/get-started/integrate-defi) |
0 commit comments