@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 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 transferTx = await colToken.transfer(acctAddr, weiAmount);
891
- await transferTx.wait();
892
- this._refreshSigner();
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 transferTx = await colToken.transfer(acctAddr, colWei);
968
- await transferTx.wait();
969
- this._refreshSigner();
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 transferTx = await colToken.transfer(acctAddr, weiAmount);
1140
- await transferTx.wait();
1141
- this._refreshSigner();
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 transferTx = await colToken.transfer(acctAddr, colWei);
1217
- await transferTx.wait();
1218
- this._refreshSigner();
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 transferTx = await colToken.transfer(acctAddr, weiAmount);
1068
- await transferTx.wait();
1069
- this._refreshSigner();
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 transferTx = await colToken.transfer(acctAddr, colWei);
1145
- await transferTx.wait();
1146
- this._refreshSigner();
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
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agether/sdk",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "TypeScript SDK for Agether - autonomous credit for AI agents on Base",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",