Skip to content

Commit 1c1e8f2

Browse files
committed
refactor: format and organize imports, improve code readability in Morpho smart accounts
1 parent 8b42cd8 commit 1c1e8f2

4 files changed

Lines changed: 156 additions & 69 deletions

File tree

examples/developer-hub-javascript/smart-accounts/morpho/borrow.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { encodeFunctionData, formatUnits } from "viem";
22
import { Client, Wallet } from "xrpl";
33
import { abi as MorphoMarketShimAbi } from "../abis/MorphoMarketShim";
44
import { account, publicClient } from "../utils/client";
5-
import { getPersonalAccountAddress, sendMemoFieldInstruction } from "../utils/smart-accounts";
5+
import {
6+
getPersonalAccountAddress,
7+
sendMemoFieldInstruction,
8+
} from "../utils/smart-accounts";
69
import { computeDirectMintingPaymentAmountXrp } from "../utils/fassets";
710
import {
811
LLTV,
@@ -35,12 +38,17 @@ async function main() {
3538
const xrplWallet = Wallet.fromSeed(process.env.XRPL_SEED!);
3639

3740
// 2. Resolve the personal account, memo-only XRP fee, market decimals, and oracle price in parallel.
38-
const [personalAccount, memoOnlyAmountXrp, marketDecimals, oraclePrice] = await Promise.all([
39-
getPersonalAccountAddress(xrplWallet.address),
40-
computeDirectMintingPaymentAmountXrp({ netMintAmountXrp: 0 }),
41-
fetchMarketDecimals(),
42-
publicClient.readContract({ address: ORACLE_ADDRESS, abi: ORACLE_ABI, functionName: "price" }),
43-
]);
41+
const [personalAccount, memoOnlyAmountXrp, marketDecimals, oraclePrice] =
42+
await Promise.all([
43+
getPersonalAccountAddress(xrplWallet.address),
44+
computeDirectMintingPaymentAmountXrp({ netMintAmountXrp: 0 }),
45+
fetchMarketDecimals(),
46+
publicClient.readContract({
47+
address: ORACLE_ADDRESS,
48+
abi: ORACLE_ABI,
49+
functionName: "price",
50+
}),
51+
]);
4452
const { loanDecimals, collateralDecimals, oraclePriceScale } = marketDecimals;
4553

4654
// 3. Fix collateral supply at 100 whole units (scaled by token decimals).
@@ -56,15 +64,16 @@ async function main() {
5664
await getAndLogState("Before borrow", personalAccount, marketDecimals);
5765

5866
// 6. Compute max borrow off-chain via Morpho Blue's health formula, then borrow 99% for a safety margin.
59-
const maxBorrowAssets = (collateralAssets * oraclePrice * LLTV) / (oraclePriceScale * WAD);
67+
const maxBorrowAssets =
68+
(collateralAssets * oraclePrice * LLTV) / (oraclePriceScale * WAD);
6069
const borrowAssets = (maxBorrowAssets * 99n) / 100n;
6170
console.log("Oracle price:", oraclePrice.toString());
6271
console.log(
6372
"Max borrowable:",
6473
formatUnits(maxBorrowAssets, loanDecimals),
6574
"→ borrowing:",
6675
formatUnits(borrowAssets, loanDecimals),
67-
"\n"
76+
"\n",
6877
);
6978

7079
// 7. Supply collateral and borrow loan tokens in one XRPL memo via the shim.

examples/developer-hub-javascript/smart-accounts/morpho/repay.ts

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@ import { encodeFunctionData } from "viem";
22
import { Client, Wallet } from "xrpl";
33
import { abi as MorphoMarketShimAbi } from "../abis/MorphoMarketShim";
44
import { account } from "../utils/client";
5-
import { getPersonalAccountAddress, sendMemoFieldInstruction } from "../utils/smart-accounts";
5+
import {
6+
getPersonalAccountAddress,
7+
sendMemoFieldInstruction,
8+
} from "../utils/smart-accounts";
69
import { computeDirectMintingPaymentAmountXrp } from "../utils/fassets";
7-
import { MORPHO_MARKET_SHIM_ADDRESS, fetchMarketDecimals, getAndLogState, marketId } from "./utils";
10+
import {
11+
MORPHO_MARKET_SHIM_ADDRESS,
12+
fetchMarketDecimals,
13+
getAndLogState,
14+
marketId,
15+
} from "./utils";
816

917
// NOTE:(Nik) Run after src/morpho/borrow.ts has opened a position. Assumes
1018
// src/morpho/setup.ts has already funded the smart account and configured
@@ -18,11 +26,12 @@ async function main() {
1826
const xrplWallet = Wallet.fromSeed(process.env.XRPL_SEED!);
1927

2028
// 2. Resolve on-chain inputs in parallel — personal account, XRPL memo fee, Morpho token decimals for logging.
21-
const [personalAccount, memoOnlyAmountXrp, marketDecimals] = await Promise.all([
22-
getPersonalAccountAddress(xrplWallet.address),
23-
computeDirectMintingPaymentAmountXrp({ netMintAmountXrp: 0 }),
24-
fetchMarketDecimals(),
25-
]);
29+
const [personalAccount, memoOnlyAmountXrp, marketDecimals] =
30+
await Promise.all([
31+
getPersonalAccountAddress(xrplWallet.address),
32+
computeDirectMintingPaymentAmountXrp({ netMintAmountXrp: 0 }),
33+
fetchMarketDecimals(),
34+
]);
2635

2736
// 3. Log addresses for this run.
2837
console.log("Personal account:", personalAccount, "\n");
@@ -31,14 +40,22 @@ async function main() {
3140
console.log("Shim address: ", MORPHO_MARKET_SHIM_ADDRESS, "\n");
3241

3342
// 4. Snapshot before repay — exit early if there is no open position.
34-
const { borrowShares, collateral } = await getAndLogState("Before repay", personalAccount, marketDecimals);
43+
const { borrowShares, collateral } = await getAndLogState(
44+
"Before repay",
45+
personalAccount,
46+
marketDecimals,
47+
);
3548

3649
if (borrowShares === 0n && collateral === 0n) {
3750
console.log("Nothing to repay or withdraw. Exiting.");
3851
return;
3952
}
4053

41-
console.log("Repaying full position, borrowShares:", borrowShares.toString(), "\n");
54+
console.log(
55+
"Repaying full position, borrowShares:",
56+
borrowShares.toString(),
57+
"\n",
58+
);
4259
// 5. Send repay-and-withdraw memo — shim repays full borrow shares, withdraws all collateral atomically (receiver = smart account).
4360
await sendMemoFieldInstruction({
4461
label: "repay-and-withdraw",
@@ -60,7 +77,11 @@ async function main() {
6077
});
6178

6279
// 6. Snapshot after repay — expect borrow shares and position collateral near zero (loan-token balance reflects interest paid).
63-
await getAndLogState("After repay + withdraw", personalAccount, marketDecimals);
80+
await getAndLogState(
81+
"After repay + withdraw",
82+
personalAccount,
83+
marketDecimals,
84+
);
6485
}
6586

6687
void main()

examples/developer-hub-javascript/smart-accounts/morpho/setup.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ async function main() {
2727
const xrplWallet = Wallet.fromSeed(process.env.XRPL_SEED!);
2828

2929
// 3. Resolve the personal account, memo-only XRP fee, and market decimals in parallel.
30-
const [personalAccount, memoOnlyAmountXrp, marketDecimals] = await Promise.all([
31-
getPersonalAccountAddress(xrplWallet.address),
32-
computeDirectMintingPaymentAmountXrp({ netMintAmountXrp: 0 }),
33-
fetchMarketDecimals(),
34-
]);
30+
const [personalAccount, memoOnlyAmountXrp, marketDecimals] =
31+
await Promise.all([
32+
getPersonalAccountAddress(xrplWallet.address),
33+
computeDirectMintingPaymentAmountXrp({ netMintAmountXrp: 0 }),
34+
fetchMarketDecimals(),
35+
]);
3536

3637
// 4. Log the addresses involved in this run.
3738
console.log("Personal account:", personalAccount, "\n");
@@ -46,13 +47,22 @@ async function main() {
4647
await mintMock(
4748
COLLATERAL_TOKEN_ADDRESS,
4849
personalAccount,
49-
collateralFundingUnits * 10n ** BigInt(marketDecimals.collateralDecimals)
50+
collateralFundingUnits * 10n ** BigInt(marketDecimals.collateralDecimals),
51+
);
52+
await mintMock(
53+
LOAN_TOKEN_ADDRESS,
54+
personalAccount,
55+
loanFundingUnits * 10n ** BigInt(marketDecimals.loanDecimals),
5056
);
51-
await mintMock(LOAN_TOKEN_ADDRESS, personalAccount, loanFundingUnits * 10n ** BigInt(marketDecimals.loanDecimals));
5257
console.log("Funded smart account with collateral and loan tokens.\n");
5358

5459
// 7. Approve the shim for both tokens and authorize it on Morpho Blue (XRPL memos).
55-
await ensureShimSetup({ personalAccount, xrplClient, xrplWallet, amountXrp: memoOnlyAmountXrp });
60+
await ensureShimSetup({
61+
personalAccount,
62+
xrplClient,
63+
xrplWallet,
64+
amountXrp: memoOnlyAmountXrp,
65+
});
5666

5767
// 8. Snapshot state again to confirm balances and that setup memos succeeded.
5868
await getAndLogState("After setup", personalAccount, marketDecimals);

examples/developer-hub-javascript/smart-accounts/morpho/utils.ts

Lines changed: 89 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,43 @@
1-
import { encodeAbiParameters, encodeFunctionData, formatUnits, keccak256, parseAbi, type Address } from "viem";
2-
import { Client, Wallet } from "xrpl";
1+
import {
2+
encodeAbiParameters,
3+
encodeFunctionData,
4+
formatUnits,
5+
keccak256,
6+
parseAbi,
7+
type Address,
8+
} from "viem";
9+
import type { Client, Wallet } from "xrpl";
310
import { abi as ERC20Abi } from "../abis/ERC20";
411
import { abi as MorphoBlueAbi } from "../abis/MorphoBlue";
512
import { account, publicClient, walletClient } from "../utils/client";
613
import { sendMemoFieldInstruction } from "../utils/smart-accounts";
714

815
// Coston2 Morpho Blue test stack (mock tokens, mock oracle, mock IRM).
9-
export const MORPHO_BLUE_ADDRESS = "0x8aE0b3CE90F16E88063516f2d88C8ac2ab552d95" as Address;
10-
export const LOAN_TOKEN_ADDRESS = "0x4984B127c3065f4348858fAFdBa020f2c8633905" as Address;
11-
export const COLLATERAL_TOKEN_ADDRESS = "0x98bf2F2fF322d5eb61D6aE04Df50856525a85D16" as Address;
12-
export const ORACLE_ADDRESS = "0x1e80830e9903c839Db803442c976DD2360D47FE0" as Address;
13-
export const IRM_ADDRESS = "0xDC275701300865D882D44ffe7cb1153535636d1a" as Address;
16+
export const MORPHO_BLUE_ADDRESS =
17+
"0x8aE0b3CE90F16E88063516f2d88C8ac2ab552d95" as Address;
18+
export const LOAN_TOKEN_ADDRESS =
19+
"0x4984B127c3065f4348858fAFdBa020f2c8633905" as Address;
20+
export const COLLATERAL_TOKEN_ADDRESS =
21+
"0x98bf2F2fF322d5eb61D6aE04Df50856525a85D16" as Address;
22+
export const ORACLE_ADDRESS =
23+
"0x1e80830e9903c839Db803442c976DD2360D47FE0" as Address;
24+
export const IRM_ADDRESS =
25+
"0xDC275701300865D882D44ffe7cb1153535636d1a" as Address;
1426
export const LLTV = 860000000000000000n; // 86 %
1527

1628
// MorphoMarketShim deployed on Coston2 — see flare-hardhat-starter/scripts/morpho/deploys.ts.
17-
export const MORPHO_MARKET_SHIM_ADDRESS = "0x33d81a1d7986bB3AbAB4F67Ad6117233ADd6F87A" as Address;
29+
export const MORPHO_MARKET_SHIM_ADDRESS =
30+
"0x33d81a1d7986bB3AbAB4F67Ad6117233ADd6F87A" as Address;
1831

1932
export const WAD = 10n ** 18n;
2033
export const MAX_UINT256 = 2n ** 256n - 1n;
2134
// Allowance >= this counts as "approved unlimited" for setup-skip purposes.
2235
const APPROVAL_THRESHOLD = 2n ** 255n;
2336

2437
// The mock collateral and loan tokens expose an unauthenticated setBalance(account, amount).
25-
export const MOCK_ERC20_ABI = parseAbi(["function setBalance(address account, uint256 amount)"]);
38+
export const MOCK_ERC20_ABI = parseAbi([
39+
"function setBalance(address account, uint256 amount)",
40+
]);
2641
export const ORACLE_ABI = parseAbi(["function price() view returns (uint256)"]);
2742
export const POSITION_ABI = parseAbi([
2843
"function position(bytes32 id, address user) view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral)",
@@ -51,8 +66,8 @@ export const marketId = keccak256(
5166
],
5267
},
5368
],
54-
[marketParams]
55-
)
69+
[marketParams],
70+
),
5671
);
5772

5873
// Reads the loan and collateral token decimals and derives the oracle's price
@@ -76,11 +91,16 @@ export async function fetchMarketDecimals() {
7691
readDecimalsOrDefault(LOAN_TOKEN_ADDRESS),
7792
readDecimalsOrDefault(COLLATERAL_TOKEN_ADDRESS),
7893
]);
79-
const oraclePriceScale = 10n ** (36n + BigInt(loanDecimals) - BigInt(collateralDecimals));
94+
const oraclePriceScale =
95+
10n ** (36n + BigInt(loanDecimals) - BigInt(collateralDecimals));
8096
return { loanDecimals, collateralDecimals, oraclePriceScale };
8197
}
8298

83-
export async function mintMock(tokenAddress: Address, recipient: Address, amount: bigint) {
99+
export async function mintMock(
100+
tokenAddress: Address,
101+
recipient: Address,
102+
amount: bigint,
103+
) {
84104
const { request } = await publicClient.simulateContract({
85105
account,
86106
address: tokenAddress,
@@ -108,33 +128,45 @@ export async function ensureShimSetup({
108128
xrplWallet: Wallet;
109129
amountXrp: number;
110130
}) {
111-
const [collateralAllowance, loanAllowance, morphoAuthorized] = (await Promise.all([
112-
publicClient.readContract({
113-
address: COLLATERAL_TOKEN_ADDRESS,
114-
abi: ERC20Abi,
115-
functionName: "allowance",
116-
args: [personalAccount, MORPHO_MARKET_SHIM_ADDRESS],
117-
}),
118-
publicClient.readContract({
119-
address: LOAN_TOKEN_ADDRESS,
120-
abi: ERC20Abi,
121-
functionName: "allowance",
122-
args: [personalAccount, MORPHO_MARKET_SHIM_ADDRESS],
123-
}),
124-
publicClient.readContract({
125-
address: MORPHO_BLUE_ADDRESS,
126-
abi: MorphoBlueAbi,
127-
functionName: "isAuthorized",
128-
args: [personalAccount, MORPHO_MARKET_SHIM_ADDRESS],
129-
}),
130-
])) as [bigint, bigint, boolean];
131+
const [collateralAllowance, loanAllowance, morphoAuthorized] =
132+
(await Promise.all([
133+
publicClient.readContract({
134+
address: COLLATERAL_TOKEN_ADDRESS,
135+
abi: ERC20Abi,
136+
functionName: "allowance",
137+
args: [personalAccount, MORPHO_MARKET_SHIM_ADDRESS],
138+
}),
139+
publicClient.readContract({
140+
address: LOAN_TOKEN_ADDRESS,
141+
abi: ERC20Abi,
142+
functionName: "allowance",
143+
args: [personalAccount, MORPHO_MARKET_SHIM_ADDRESS],
144+
}),
145+
publicClient.readContract({
146+
address: MORPHO_BLUE_ADDRESS,
147+
abi: MorphoBlueAbi,
148+
functionName: "isAuthorized",
149+
args: [personalAccount, MORPHO_MARKET_SHIM_ADDRESS],
150+
}),
151+
])) as [bigint, bigint, boolean];
131152

132-
if (collateralAllowance >= APPROVAL_THRESHOLD && loanAllowance >= APPROVAL_THRESHOLD && morphoAuthorized) {
133-
console.log("Smart account → shim setup already complete — skipping setup memos.\n");
153+
if (
154+
collateralAllowance >= APPROVAL_THRESHOLD &&
155+
loanAllowance >= APPROVAL_THRESHOLD &&
156+
morphoAuthorized
157+
) {
158+
console.log(
159+
"Smart account → shim setup already complete — skipping setup memos.\n",
160+
);
134161
return;
135162
}
136163

137-
const sharedMemoFields = { amountXrp, personalAccount, xrplClient, xrplWallet };
164+
const sharedMemoFields = {
165+
amountXrp,
166+
personalAccount,
167+
xrplClient,
168+
xrplWallet,
169+
};
138170

139171
if (collateralAllowance < APPROVAL_THRESHOLD) {
140172
await sendMemoFieldInstruction({
@@ -192,7 +224,7 @@ export async function ensureShimSetup({
192224
export async function getAndLogState(
193225
label: string,
194226
smartAccount: Address,
195-
marketDecimals: { loanDecimals: number; collateralDecimals: number }
227+
marketDecimals: { loanDecimals: number; collateralDecimals: number },
196228
) {
197229
const [position, collateralBalance, loanBalance] = await Promise.all([
198230
publicClient.readContract({
@@ -221,11 +253,26 @@ export async function getAndLogState(
221253
// (loanDecimals + 6). The conversion drifts slightly as interest accrues.
222254
const sharesScale = marketDecimals.loanDecimals + 6;
223255
console.log(`=== ${label} ===`);
224-
console.log(" position supply (≈loan tokens): ", formatUnits(supplyShares, sharesScale));
225-
console.log(" position borrow (≈loan tokens): ", formatUnits(borrowShares, sharesScale));
226-
console.log(" position collateral: ", formatUnits(collateral, marketDecimals.collateralDecimals));
227-
console.log(" smart-account collateral balance:", formatUnits(collateralBalance, marketDecimals.collateralDecimals));
228-
console.log(" smart-account loan-token balance:", formatUnits(loanBalance, marketDecimals.loanDecimals));
256+
console.log(
257+
" position supply (≈loan tokens): ",
258+
formatUnits(supplyShares, sharesScale),
259+
);
260+
console.log(
261+
" position borrow (≈loan tokens): ",
262+
formatUnits(borrowShares, sharesScale),
263+
);
264+
console.log(
265+
" position collateral: ",
266+
formatUnits(collateral, marketDecimals.collateralDecimals),
267+
);
268+
console.log(
269+
" smart-account collateral balance:",
270+
formatUnits(collateralBalance, marketDecimals.collateralDecimals),
271+
);
272+
console.log(
273+
" smart-account loan-token balance:",
274+
formatUnits(loanBalance, marketDecimals.loanDecimals),
275+
);
229276
console.log("");
230277

231278
return { supplyShares, borrowShares, collateral };

0 commit comments

Comments
 (0)