@agether/sdk 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +128 -7
- package/dist/index.js +128 -7
- package/dist/index.mjs +128 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -133,7 +133,8 @@ var init_abis = __esm({
|
|
|
133
133
|
"function getNonce(address sender, uint192 key) view returns (uint256 nonce)",
|
|
134
134
|
"function balanceOf(address account) view returns (uint256)",
|
|
135
135
|
"function depositTo(address account) payable",
|
|
136
|
-
"event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)"
|
|
136
|
+
"event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)",
|
|
137
|
+
"event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)"
|
|
137
138
|
];
|
|
138
139
|
SAFE7579_ACCOUNT_ABI = [
|
|
139
140
|
// ERC-7579 execution (called via UserOp through Safe7579 fallback)
|
|
@@ -887,9 +888,20 @@ var init_MorphoClient = __esm({
|
|
|
887
888
|
const weiAmount = import_ethers.ethers.parseUnits(amount, colInfo.decimals);
|
|
888
889
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
889
890
|
const colToken = new import_ethers.Contract(colInfo.address, ERC20_ABI, this._signer);
|
|
890
|
-
const
|
|
891
|
-
|
|
892
|
-
|
|
891
|
+
const acctBalance = await colToken.balanceOf(acctAddr);
|
|
892
|
+
if (acctBalance < weiAmount) {
|
|
893
|
+
const shortfall = weiAmount - acctBalance;
|
|
894
|
+
const eoaBalance = await colToken.balanceOf(await this.getSignerAddress());
|
|
895
|
+
if (eoaBalance < shortfall) {
|
|
896
|
+
throw new AgetherError(
|
|
897
|
+
`Insufficient ${tokenSymbol}. Need ${amount}, AgentAccount has ${import_ethers.ethers.formatUnits(acctBalance, colInfo.decimals)}, EOA has ${import_ethers.ethers.formatUnits(eoaBalance, colInfo.decimals)}.`,
|
|
898
|
+
"INSUFFICIENT_BALANCE"
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
const transferTx = await colToken.transfer(acctAddr, shortfall);
|
|
902
|
+
await transferTx.wait();
|
|
903
|
+
this._refreshSigner();
|
|
904
|
+
}
|
|
893
905
|
const targets = [colInfo.address, morphoAddr];
|
|
894
906
|
const values = [0n, 0n];
|
|
895
907
|
const datas = [
|
|
@@ -932,6 +944,41 @@ var init_MorphoClient = __esm({
|
|
|
932
944
|
params = p;
|
|
933
945
|
usedToken = symbol;
|
|
934
946
|
}
|
|
947
|
+
try {
|
|
948
|
+
const marketId = import_ethers.ethers.keccak256(
|
|
949
|
+
import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
950
|
+
["address", "address", "address", "address", "uint256"],
|
|
951
|
+
[params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
|
|
952
|
+
)
|
|
953
|
+
);
|
|
954
|
+
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
955
|
+
if (pos.collateral === 0n) {
|
|
956
|
+
throw new AgetherError(
|
|
957
|
+
`No collateral deposited for ${usedToken}. Deposit collateral first.`,
|
|
958
|
+
"NO_COLLATERAL"
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
const oracleContract = new import_ethers.Contract(params.oracle, ["function price() view returns (uint256)"], this.provider);
|
|
962
|
+
const oraclePrice = await oracleContract.price();
|
|
963
|
+
const collateralValueInLoan = BigInt(pos.collateral) * oraclePrice / 10n ** 36n;
|
|
964
|
+
const maxBorrowTotal = collateralValueInLoan * params.lltv / 10n ** 18n;
|
|
965
|
+
const mktState = await this.morphoBlue.market(marketId);
|
|
966
|
+
const totalBorrowShares = BigInt(mktState.totalBorrowShares);
|
|
967
|
+
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
968
|
+
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
969
|
+
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
970
|
+
if (amount > maxAdditional) {
|
|
971
|
+
const maxUsd = import_ethers.ethers.formatUnits(maxAdditional, 6);
|
|
972
|
+
const colFormatted = import_ethers.ethers.formatUnits(pos.collateral, 18);
|
|
973
|
+
throw new AgetherError(
|
|
974
|
+
`Borrow of $${usdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (collateral: ${colFormatted} ${usedToken}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Deposit more collateral or reduce borrow amount.`,
|
|
975
|
+
"EXCEEDS_MAX_LTV"
|
|
976
|
+
);
|
|
977
|
+
}
|
|
978
|
+
} catch (e) {
|
|
979
|
+
if (e instanceof AgetherError) throw e;
|
|
980
|
+
console.warn("[agether] borrow pre-check failed (proceeding anyway):", e instanceof Error ? e.message : e);
|
|
981
|
+
}
|
|
935
982
|
const data = morphoIface.encodeFunctionData("borrow", [
|
|
936
983
|
this._toTuple(params),
|
|
937
984
|
amount,
|
|
@@ -963,10 +1010,50 @@ var init_MorphoClient = __esm({
|
|
|
963
1010
|
const colWei = import_ethers.ethers.parseUnits(collateralAmount, colInfo.decimals);
|
|
964
1011
|
const borrowWei = import_ethers.ethers.parseUnits(borrowUsdcAmount, 6);
|
|
965
1012
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1013
|
+
try {
|
|
1014
|
+
const marketId = import_ethers.ethers.keccak256(
|
|
1015
|
+
import_ethers.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
1016
|
+
["address", "address", "address", "address", "uint256"],
|
|
1017
|
+
[params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
|
|
1018
|
+
)
|
|
1019
|
+
);
|
|
1020
|
+
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
1021
|
+
const totalCollateral = BigInt(pos.collateral) + colWei;
|
|
1022
|
+
const oracleContract = new import_ethers.Contract(params.oracle, ["function price() view returns (uint256)"], this.provider);
|
|
1023
|
+
const oraclePrice = await oracleContract.price();
|
|
1024
|
+
const collateralValueInLoan = totalCollateral * oraclePrice / 10n ** 36n;
|
|
1025
|
+
const maxBorrowTotal = collateralValueInLoan * params.lltv / 10n ** 18n;
|
|
1026
|
+
const mktState = await this.morphoBlue.market(marketId);
|
|
1027
|
+
const totalBorrowShares = BigInt(mktState.totalBorrowShares);
|
|
1028
|
+
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
1029
|
+
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1030
|
+
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1031
|
+
if (borrowWei > maxAdditional) {
|
|
1032
|
+
const maxUsd = import_ethers.ethers.formatUnits(maxAdditional, 6);
|
|
1033
|
+
throw new AgetherError(
|
|
1034
|
+
`Borrow of $${borrowUsdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (total collateral: ${import_ethers.ethers.formatUnits(totalCollateral, colInfo.decimals)} ${tokenSymbol}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Reduce borrow or increase collateral.`,
|
|
1035
|
+
"EXCEEDS_MAX_LTV"
|
|
1036
|
+
);
|
|
1037
|
+
}
|
|
1038
|
+
} catch (e) {
|
|
1039
|
+
if (e instanceof AgetherError) throw e;
|
|
1040
|
+
console.warn("[agether] depositAndBorrow pre-check failed (proceeding anyway):", e instanceof Error ? e.message : e);
|
|
1041
|
+
}
|
|
966
1042
|
const colToken = new import_ethers.Contract(colInfo.address, ERC20_ABI, this._signer);
|
|
967
|
-
const
|
|
968
|
-
|
|
969
|
-
|
|
1043
|
+
const acctBalance = await colToken.balanceOf(acctAddr);
|
|
1044
|
+
if (acctBalance < colWei) {
|
|
1045
|
+
const shortfall = colWei - acctBalance;
|
|
1046
|
+
const eoaBalance = await colToken.balanceOf(await this.getSignerAddress());
|
|
1047
|
+
if (eoaBalance < shortfall) {
|
|
1048
|
+
throw new AgetherError(
|
|
1049
|
+
`Insufficient ${tokenSymbol}. Need ${collateralAmount}, AgentAccount has ${import_ethers.ethers.formatUnits(acctBalance, colInfo.decimals)}, EOA has ${import_ethers.ethers.formatUnits(eoaBalance, colInfo.decimals)}.`,
|
|
1050
|
+
"INSUFFICIENT_BALANCE"
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
const transferTx = await colToken.transfer(acctAddr, shortfall);
|
|
1054
|
+
await transferTx.wait();
|
|
1055
|
+
this._refreshSigner();
|
|
1056
|
+
}
|
|
970
1057
|
const targets = [colInfo.address, morphoAddr, morphoAddr];
|
|
971
1058
|
const values = [0n, 0n, 0n];
|
|
972
1059
|
const datas = [
|
|
@@ -1321,6 +1408,40 @@ var init_MorphoClient = __esm({
|
|
|
1321
1408
|
const tx = await this.entryPoint.handleOps([userOp], await this.getSignerAddress());
|
|
1322
1409
|
const receipt = await tx.wait();
|
|
1323
1410
|
this._refreshSigner();
|
|
1411
|
+
const epIface = new import_ethers.ethers.Interface(ENTRYPOINT_V07_ABI);
|
|
1412
|
+
for (const log of receipt.logs) {
|
|
1413
|
+
try {
|
|
1414
|
+
const parsed = epIface.parseLog({ topics: log.topics, data: log.data });
|
|
1415
|
+
if (parsed?.name === "UserOperationEvent" && !parsed.args.success) {
|
|
1416
|
+
let revertMsg = "UserOp inner execution reverted";
|
|
1417
|
+
for (const rLog of receipt.logs) {
|
|
1418
|
+
try {
|
|
1419
|
+
const rParsed = epIface.parseLog({ topics: rLog.topics, data: rLog.data });
|
|
1420
|
+
if (rParsed?.name === "UserOperationRevertReason") {
|
|
1421
|
+
const reason = rParsed.args.revertReason;
|
|
1422
|
+
try {
|
|
1423
|
+
if (reason.length >= 10 && reason.slice(0, 10) === "0x08c379a0") {
|
|
1424
|
+
const decoded = import_ethers.ethers.AbiCoder.defaultAbiCoder().decode(["string"], "0x" + reason.slice(10));
|
|
1425
|
+
revertMsg = `UserOp reverted: ${decoded[0]}`;
|
|
1426
|
+
} else {
|
|
1427
|
+
revertMsg = `UserOp reverted with data: ${reason}`;
|
|
1428
|
+
}
|
|
1429
|
+
} catch {
|
|
1430
|
+
revertMsg = `UserOp reverted with data: ${reason}`;
|
|
1431
|
+
}
|
|
1432
|
+
break;
|
|
1433
|
+
}
|
|
1434
|
+
} catch {
|
|
1435
|
+
continue;
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
throw new AgetherError(revertMsg, "USEROP_EXECUTION_FAILED");
|
|
1439
|
+
}
|
|
1440
|
+
} catch (e) {
|
|
1441
|
+
if (e instanceof AgetherError) throw e;
|
|
1442
|
+
continue;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1324
1445
|
return receipt;
|
|
1325
1446
|
}
|
|
1326
1447
|
/**
|
package/dist/index.js
CHANGED
|
@@ -216,7 +216,8 @@ var ENTRYPOINT_V07_ABI = [
|
|
|
216
216
|
"function getNonce(address sender, uint192 key) view returns (uint256 nonce)",
|
|
217
217
|
"function balanceOf(address account) view returns (uint256)",
|
|
218
218
|
"function depositTo(address account) payable",
|
|
219
|
-
"event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)"
|
|
219
|
+
"event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)",
|
|
220
|
+
"event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)"
|
|
220
221
|
];
|
|
221
222
|
var SAFE7579_ACCOUNT_ABI = [
|
|
222
223
|
// ERC-7579 execution (called via UserOp through Safe7579 fallback)
|
|
@@ -1136,9 +1137,20 @@ var MorphoClient = class {
|
|
|
1136
1137
|
const weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
|
|
1137
1138
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1138
1139
|
const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this._signer);
|
|
1139
|
-
const
|
|
1140
|
-
|
|
1141
|
-
|
|
1140
|
+
const acctBalance = await colToken.balanceOf(acctAddr);
|
|
1141
|
+
if (acctBalance < weiAmount) {
|
|
1142
|
+
const shortfall = weiAmount - acctBalance;
|
|
1143
|
+
const eoaBalance = await colToken.balanceOf(await this.getSignerAddress());
|
|
1144
|
+
if (eoaBalance < shortfall) {
|
|
1145
|
+
throw new AgetherError(
|
|
1146
|
+
`Insufficient ${tokenSymbol}. Need ${amount}, AgentAccount has ${import_ethers2.ethers.formatUnits(acctBalance, colInfo.decimals)}, EOA has ${import_ethers2.ethers.formatUnits(eoaBalance, colInfo.decimals)}.`,
|
|
1147
|
+
"INSUFFICIENT_BALANCE"
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
const transferTx = await colToken.transfer(acctAddr, shortfall);
|
|
1151
|
+
await transferTx.wait();
|
|
1152
|
+
this._refreshSigner();
|
|
1153
|
+
}
|
|
1142
1154
|
const targets = [colInfo.address, morphoAddr];
|
|
1143
1155
|
const values = [0n, 0n];
|
|
1144
1156
|
const datas = [
|
|
@@ -1181,6 +1193,41 @@ var MorphoClient = class {
|
|
|
1181
1193
|
params = p;
|
|
1182
1194
|
usedToken = symbol;
|
|
1183
1195
|
}
|
|
1196
|
+
try {
|
|
1197
|
+
const marketId = import_ethers2.ethers.keccak256(
|
|
1198
|
+
import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
1199
|
+
["address", "address", "address", "address", "uint256"],
|
|
1200
|
+
[params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
|
|
1201
|
+
)
|
|
1202
|
+
);
|
|
1203
|
+
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
1204
|
+
if (pos.collateral === 0n) {
|
|
1205
|
+
throw new AgetherError(
|
|
1206
|
+
`No collateral deposited for ${usedToken}. Deposit collateral first.`,
|
|
1207
|
+
"NO_COLLATERAL"
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
const oracleContract = new import_ethers2.Contract(params.oracle, ["function price() view returns (uint256)"], this.provider);
|
|
1211
|
+
const oraclePrice = await oracleContract.price();
|
|
1212
|
+
const collateralValueInLoan = BigInt(pos.collateral) * oraclePrice / 10n ** 36n;
|
|
1213
|
+
const maxBorrowTotal = collateralValueInLoan * params.lltv / 10n ** 18n;
|
|
1214
|
+
const mktState = await this.morphoBlue.market(marketId);
|
|
1215
|
+
const totalBorrowShares = BigInt(mktState.totalBorrowShares);
|
|
1216
|
+
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
1217
|
+
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1218
|
+
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1219
|
+
if (amount > maxAdditional) {
|
|
1220
|
+
const maxUsd = import_ethers2.ethers.formatUnits(maxAdditional, 6);
|
|
1221
|
+
const colFormatted = import_ethers2.ethers.formatUnits(pos.collateral, 18);
|
|
1222
|
+
throw new AgetherError(
|
|
1223
|
+
`Borrow of $${usdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (collateral: ${colFormatted} ${usedToken}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Deposit more collateral or reduce borrow amount.`,
|
|
1224
|
+
"EXCEEDS_MAX_LTV"
|
|
1225
|
+
);
|
|
1226
|
+
}
|
|
1227
|
+
} catch (e) {
|
|
1228
|
+
if (e instanceof AgetherError) throw e;
|
|
1229
|
+
console.warn("[agether] borrow pre-check failed (proceeding anyway):", e instanceof Error ? e.message : e);
|
|
1230
|
+
}
|
|
1184
1231
|
const data = morphoIface.encodeFunctionData("borrow", [
|
|
1185
1232
|
this._toTuple(params),
|
|
1186
1233
|
amount,
|
|
@@ -1212,10 +1259,50 @@ var MorphoClient = class {
|
|
|
1212
1259
|
const colWei = import_ethers2.ethers.parseUnits(collateralAmount, colInfo.decimals);
|
|
1213
1260
|
const borrowWei = import_ethers2.ethers.parseUnits(borrowUsdcAmount, 6);
|
|
1214
1261
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1262
|
+
try {
|
|
1263
|
+
const marketId = import_ethers2.ethers.keccak256(
|
|
1264
|
+
import_ethers2.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
1265
|
+
["address", "address", "address", "address", "uint256"],
|
|
1266
|
+
[params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
|
|
1267
|
+
)
|
|
1268
|
+
);
|
|
1269
|
+
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
1270
|
+
const totalCollateral = BigInt(pos.collateral) + colWei;
|
|
1271
|
+
const oracleContract = new import_ethers2.Contract(params.oracle, ["function price() view returns (uint256)"], this.provider);
|
|
1272
|
+
const oraclePrice = await oracleContract.price();
|
|
1273
|
+
const collateralValueInLoan = totalCollateral * oraclePrice / 10n ** 36n;
|
|
1274
|
+
const maxBorrowTotal = collateralValueInLoan * params.lltv / 10n ** 18n;
|
|
1275
|
+
const mktState = await this.morphoBlue.market(marketId);
|
|
1276
|
+
const totalBorrowShares = BigInt(mktState.totalBorrowShares);
|
|
1277
|
+
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
1278
|
+
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1279
|
+
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1280
|
+
if (borrowWei > maxAdditional) {
|
|
1281
|
+
const maxUsd = import_ethers2.ethers.formatUnits(maxAdditional, 6);
|
|
1282
|
+
throw new AgetherError(
|
|
1283
|
+
`Borrow of $${borrowUsdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (total collateral: ${import_ethers2.ethers.formatUnits(totalCollateral, colInfo.decimals)} ${tokenSymbol}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Reduce borrow or increase collateral.`,
|
|
1284
|
+
"EXCEEDS_MAX_LTV"
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
} catch (e) {
|
|
1288
|
+
if (e instanceof AgetherError) throw e;
|
|
1289
|
+
console.warn("[agether] depositAndBorrow pre-check failed (proceeding anyway):", e instanceof Error ? e.message : e);
|
|
1290
|
+
}
|
|
1215
1291
|
const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this._signer);
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
|
|
1292
|
+
const acctBalance = await colToken.balanceOf(acctAddr);
|
|
1293
|
+
if (acctBalance < colWei) {
|
|
1294
|
+
const shortfall = colWei - acctBalance;
|
|
1295
|
+
const eoaBalance = await colToken.balanceOf(await this.getSignerAddress());
|
|
1296
|
+
if (eoaBalance < shortfall) {
|
|
1297
|
+
throw new AgetherError(
|
|
1298
|
+
`Insufficient ${tokenSymbol}. Need ${collateralAmount}, AgentAccount has ${import_ethers2.ethers.formatUnits(acctBalance, colInfo.decimals)}, EOA has ${import_ethers2.ethers.formatUnits(eoaBalance, colInfo.decimals)}.`,
|
|
1299
|
+
"INSUFFICIENT_BALANCE"
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
const transferTx = await colToken.transfer(acctAddr, shortfall);
|
|
1303
|
+
await transferTx.wait();
|
|
1304
|
+
this._refreshSigner();
|
|
1305
|
+
}
|
|
1219
1306
|
const targets = [colInfo.address, morphoAddr, morphoAddr];
|
|
1220
1307
|
const values = [0n, 0n, 0n];
|
|
1221
1308
|
const datas = [
|
|
@@ -1570,6 +1657,40 @@ var MorphoClient = class {
|
|
|
1570
1657
|
const tx = await this.entryPoint.handleOps([userOp], await this.getSignerAddress());
|
|
1571
1658
|
const receipt = await tx.wait();
|
|
1572
1659
|
this._refreshSigner();
|
|
1660
|
+
const epIface = new import_ethers2.ethers.Interface(ENTRYPOINT_V07_ABI);
|
|
1661
|
+
for (const log of receipt.logs) {
|
|
1662
|
+
try {
|
|
1663
|
+
const parsed = epIface.parseLog({ topics: log.topics, data: log.data });
|
|
1664
|
+
if (parsed?.name === "UserOperationEvent" && !parsed.args.success) {
|
|
1665
|
+
let revertMsg = "UserOp inner execution reverted";
|
|
1666
|
+
for (const rLog of receipt.logs) {
|
|
1667
|
+
try {
|
|
1668
|
+
const rParsed = epIface.parseLog({ topics: rLog.topics, data: rLog.data });
|
|
1669
|
+
if (rParsed?.name === "UserOperationRevertReason") {
|
|
1670
|
+
const reason = rParsed.args.revertReason;
|
|
1671
|
+
try {
|
|
1672
|
+
if (reason.length >= 10 && reason.slice(0, 10) === "0x08c379a0") {
|
|
1673
|
+
const decoded = import_ethers2.ethers.AbiCoder.defaultAbiCoder().decode(["string"], "0x" + reason.slice(10));
|
|
1674
|
+
revertMsg = `UserOp reverted: ${decoded[0]}`;
|
|
1675
|
+
} else {
|
|
1676
|
+
revertMsg = `UserOp reverted with data: ${reason}`;
|
|
1677
|
+
}
|
|
1678
|
+
} catch {
|
|
1679
|
+
revertMsg = `UserOp reverted with data: ${reason}`;
|
|
1680
|
+
}
|
|
1681
|
+
break;
|
|
1682
|
+
}
|
|
1683
|
+
} catch {
|
|
1684
|
+
continue;
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
throw new AgetherError(revertMsg, "USEROP_EXECUTION_FAILED");
|
|
1688
|
+
}
|
|
1689
|
+
} catch (e) {
|
|
1690
|
+
if (e instanceof AgetherError) throw e;
|
|
1691
|
+
continue;
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1573
1694
|
return receipt;
|
|
1574
1695
|
}
|
|
1575
1696
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -144,7 +144,8 @@ var ENTRYPOINT_V07_ABI = [
|
|
|
144
144
|
"function getNonce(address sender, uint192 key) view returns (uint256 nonce)",
|
|
145
145
|
"function balanceOf(address account) view returns (uint256)",
|
|
146
146
|
"function depositTo(address account) payable",
|
|
147
|
-
"event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)"
|
|
147
|
+
"event UserOperationEvent(bytes32 indexed userOpHash, address indexed sender, address indexed paymaster, uint256 nonce, bool success, uint256 actualGasCost, uint256 actualGasUsed)",
|
|
148
|
+
"event UserOperationRevertReason(bytes32 indexed userOpHash, address indexed sender, uint256 nonce, bytes revertReason)"
|
|
148
149
|
];
|
|
149
150
|
var SAFE7579_ACCOUNT_ABI = [
|
|
150
151
|
// ERC-7579 execution (called via UserOp through Safe7579 fallback)
|
|
@@ -1064,9 +1065,20 @@ var MorphoClient = class {
|
|
|
1064
1065
|
const weiAmount = ethers2.parseUnits(amount, colInfo.decimals);
|
|
1065
1066
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1066
1067
|
const colToken = new Contract2(colInfo.address, ERC20_ABI, this._signer);
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1068
|
+
const acctBalance = await colToken.balanceOf(acctAddr);
|
|
1069
|
+
if (acctBalance < weiAmount) {
|
|
1070
|
+
const shortfall = weiAmount - acctBalance;
|
|
1071
|
+
const eoaBalance = await colToken.balanceOf(await this.getSignerAddress());
|
|
1072
|
+
if (eoaBalance < shortfall) {
|
|
1073
|
+
throw new AgetherError(
|
|
1074
|
+
`Insufficient ${tokenSymbol}. Need ${amount}, AgentAccount has ${ethers2.formatUnits(acctBalance, colInfo.decimals)}, EOA has ${ethers2.formatUnits(eoaBalance, colInfo.decimals)}.`,
|
|
1075
|
+
"INSUFFICIENT_BALANCE"
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
const transferTx = await colToken.transfer(acctAddr, shortfall);
|
|
1079
|
+
await transferTx.wait();
|
|
1080
|
+
this._refreshSigner();
|
|
1081
|
+
}
|
|
1070
1082
|
const targets = [colInfo.address, morphoAddr];
|
|
1071
1083
|
const values = [0n, 0n];
|
|
1072
1084
|
const datas = [
|
|
@@ -1109,6 +1121,41 @@ var MorphoClient = class {
|
|
|
1109
1121
|
params = p;
|
|
1110
1122
|
usedToken = symbol;
|
|
1111
1123
|
}
|
|
1124
|
+
try {
|
|
1125
|
+
const marketId = ethers2.keccak256(
|
|
1126
|
+
ethers2.AbiCoder.defaultAbiCoder().encode(
|
|
1127
|
+
["address", "address", "address", "address", "uint256"],
|
|
1128
|
+
[params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
|
|
1129
|
+
)
|
|
1130
|
+
);
|
|
1131
|
+
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
1132
|
+
if (pos.collateral === 0n) {
|
|
1133
|
+
throw new AgetherError(
|
|
1134
|
+
`No collateral deposited for ${usedToken}. Deposit collateral first.`,
|
|
1135
|
+
"NO_COLLATERAL"
|
|
1136
|
+
);
|
|
1137
|
+
}
|
|
1138
|
+
const oracleContract = new Contract2(params.oracle, ["function price() view returns (uint256)"], this.provider);
|
|
1139
|
+
const oraclePrice = await oracleContract.price();
|
|
1140
|
+
const collateralValueInLoan = BigInt(pos.collateral) * oraclePrice / 10n ** 36n;
|
|
1141
|
+
const maxBorrowTotal = collateralValueInLoan * params.lltv / 10n ** 18n;
|
|
1142
|
+
const mktState = await this.morphoBlue.market(marketId);
|
|
1143
|
+
const totalBorrowShares = BigInt(mktState.totalBorrowShares);
|
|
1144
|
+
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
1145
|
+
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1146
|
+
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1147
|
+
if (amount > maxAdditional) {
|
|
1148
|
+
const maxUsd = ethers2.formatUnits(maxAdditional, 6);
|
|
1149
|
+
const colFormatted = ethers2.formatUnits(pos.collateral, 18);
|
|
1150
|
+
throw new AgetherError(
|
|
1151
|
+
`Borrow of $${usdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (collateral: ${colFormatted} ${usedToken}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Deposit more collateral or reduce borrow amount.`,
|
|
1152
|
+
"EXCEEDS_MAX_LTV"
|
|
1153
|
+
);
|
|
1154
|
+
}
|
|
1155
|
+
} catch (e) {
|
|
1156
|
+
if (e instanceof AgetherError) throw e;
|
|
1157
|
+
console.warn("[agether] borrow pre-check failed (proceeding anyway):", e instanceof Error ? e.message : e);
|
|
1158
|
+
}
|
|
1112
1159
|
const data = morphoIface.encodeFunctionData("borrow", [
|
|
1113
1160
|
this._toTuple(params),
|
|
1114
1161
|
amount,
|
|
@@ -1140,10 +1187,50 @@ var MorphoClient = class {
|
|
|
1140
1187
|
const colWei = ethers2.parseUnits(collateralAmount, colInfo.decimals);
|
|
1141
1188
|
const borrowWei = ethers2.parseUnits(borrowUsdcAmount, 6);
|
|
1142
1189
|
const morphoAddr = this.config.contracts.morphoBlue;
|
|
1190
|
+
try {
|
|
1191
|
+
const marketId = ethers2.keccak256(
|
|
1192
|
+
ethers2.AbiCoder.defaultAbiCoder().encode(
|
|
1193
|
+
["address", "address", "address", "address", "uint256"],
|
|
1194
|
+
[params.loanToken, params.collateralToken, params.oracle, params.irm, params.lltv]
|
|
1195
|
+
)
|
|
1196
|
+
);
|
|
1197
|
+
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
1198
|
+
const totalCollateral = BigInt(pos.collateral) + colWei;
|
|
1199
|
+
const oracleContract = new Contract2(params.oracle, ["function price() view returns (uint256)"], this.provider);
|
|
1200
|
+
const oraclePrice = await oracleContract.price();
|
|
1201
|
+
const collateralValueInLoan = totalCollateral * oraclePrice / 10n ** 36n;
|
|
1202
|
+
const maxBorrowTotal = collateralValueInLoan * params.lltv / 10n ** 18n;
|
|
1203
|
+
const mktState = await this.morphoBlue.market(marketId);
|
|
1204
|
+
const totalBorrowShares = BigInt(mktState.totalBorrowShares);
|
|
1205
|
+
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
1206
|
+
const currentDebt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1207
|
+
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
1208
|
+
if (borrowWei > maxAdditional) {
|
|
1209
|
+
const maxUsd = ethers2.formatUnits(maxAdditional, 6);
|
|
1210
|
+
throw new AgetherError(
|
|
1211
|
+
`Borrow of $${borrowUsdcAmount} USDC exceeds max borrowable $${maxUsd} USDC (total collateral: ${ethers2.formatUnits(totalCollateral, colInfo.decimals)} ${tokenSymbol}, LLTV: ${Number(params.lltv) / 1e18 * 100}%). Reduce borrow or increase collateral.`,
|
|
1212
|
+
"EXCEEDS_MAX_LTV"
|
|
1213
|
+
);
|
|
1214
|
+
}
|
|
1215
|
+
} catch (e) {
|
|
1216
|
+
if (e instanceof AgetherError) throw e;
|
|
1217
|
+
console.warn("[agether] depositAndBorrow pre-check failed (proceeding anyway):", e instanceof Error ? e.message : e);
|
|
1218
|
+
}
|
|
1143
1219
|
const colToken = new Contract2(colInfo.address, ERC20_ABI, this._signer);
|
|
1144
|
-
const
|
|
1145
|
-
|
|
1146
|
-
|
|
1220
|
+
const acctBalance = await colToken.balanceOf(acctAddr);
|
|
1221
|
+
if (acctBalance < colWei) {
|
|
1222
|
+
const shortfall = colWei - acctBalance;
|
|
1223
|
+
const eoaBalance = await colToken.balanceOf(await this.getSignerAddress());
|
|
1224
|
+
if (eoaBalance < shortfall) {
|
|
1225
|
+
throw new AgetherError(
|
|
1226
|
+
`Insufficient ${tokenSymbol}. Need ${collateralAmount}, AgentAccount has ${ethers2.formatUnits(acctBalance, colInfo.decimals)}, EOA has ${ethers2.formatUnits(eoaBalance, colInfo.decimals)}.`,
|
|
1227
|
+
"INSUFFICIENT_BALANCE"
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1230
|
+
const transferTx = await colToken.transfer(acctAddr, shortfall);
|
|
1231
|
+
await transferTx.wait();
|
|
1232
|
+
this._refreshSigner();
|
|
1233
|
+
}
|
|
1147
1234
|
const targets = [colInfo.address, morphoAddr, morphoAddr];
|
|
1148
1235
|
const values = [0n, 0n, 0n];
|
|
1149
1236
|
const datas = [
|
|
@@ -1498,6 +1585,40 @@ var MorphoClient = class {
|
|
|
1498
1585
|
const tx = await this.entryPoint.handleOps([userOp], await this.getSignerAddress());
|
|
1499
1586
|
const receipt = await tx.wait();
|
|
1500
1587
|
this._refreshSigner();
|
|
1588
|
+
const epIface = new ethers2.Interface(ENTRYPOINT_V07_ABI);
|
|
1589
|
+
for (const log of receipt.logs) {
|
|
1590
|
+
try {
|
|
1591
|
+
const parsed = epIface.parseLog({ topics: log.topics, data: log.data });
|
|
1592
|
+
if (parsed?.name === "UserOperationEvent" && !parsed.args.success) {
|
|
1593
|
+
let revertMsg = "UserOp inner execution reverted";
|
|
1594
|
+
for (const rLog of receipt.logs) {
|
|
1595
|
+
try {
|
|
1596
|
+
const rParsed = epIface.parseLog({ topics: rLog.topics, data: rLog.data });
|
|
1597
|
+
if (rParsed?.name === "UserOperationRevertReason") {
|
|
1598
|
+
const reason = rParsed.args.revertReason;
|
|
1599
|
+
try {
|
|
1600
|
+
if (reason.length >= 10 && reason.slice(0, 10) === "0x08c379a0") {
|
|
1601
|
+
const decoded = ethers2.AbiCoder.defaultAbiCoder().decode(["string"], "0x" + reason.slice(10));
|
|
1602
|
+
revertMsg = `UserOp reverted: ${decoded[0]}`;
|
|
1603
|
+
} else {
|
|
1604
|
+
revertMsg = `UserOp reverted with data: ${reason}`;
|
|
1605
|
+
}
|
|
1606
|
+
} catch {
|
|
1607
|
+
revertMsg = `UserOp reverted with data: ${reason}`;
|
|
1608
|
+
}
|
|
1609
|
+
break;
|
|
1610
|
+
}
|
|
1611
|
+
} catch {
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
throw new AgetherError(revertMsg, "USEROP_EXECUTION_FAILED");
|
|
1616
|
+
}
|
|
1617
|
+
} catch (e) {
|
|
1618
|
+
if (e instanceof AgetherError) throw e;
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1501
1622
|
return receipt;
|
|
1502
1623
|
}
|
|
1503
1624
|
/**
|