@agether/sdk 1.6.1 → 1.6.3
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.d.ts +25 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +351 -3
- package/dist/clients/AgentIdentityClient.d.ts +188 -0
- package/dist/clients/AgentIdentityClient.d.ts.map +1 -0
- package/dist/clients/AgentIdentityClient.js +333 -0
- package/dist/clients/AgetherClient.d.ts +63 -0
- package/dist/clients/AgetherClient.d.ts.map +1 -0
- package/dist/clients/AgetherClient.js +171 -0
- package/dist/clients/MorphoClient.d.ts +287 -0
- package/dist/clients/MorphoClient.d.ts.map +1 -0
- package/dist/clients/MorphoClient.js +951 -0
- package/dist/clients/ScoringClient.d.ts +89 -0
- package/dist/clients/ScoringClient.d.ts.map +1 -0
- package/dist/clients/ScoringClient.js +93 -0
- package/dist/clients/X402Client.d.ts +130 -0
- package/dist/clients/X402Client.d.ts.map +1 -0
- package/dist/clients/X402Client.js +301 -0
- package/dist/index.d.mts +142 -1
- package/dist/index.d.ts +142 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +351 -3
- package/dist/index.mjs +351 -3
- package/dist/types/index.d.ts +121 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +43 -0
- package/dist/utils/abis.d.ts +18 -0
- package/dist/utils/abis.d.ts.map +1 -0
- package/dist/utils/abis.js +93 -0
- package/dist/utils/config.d.ts +34 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +115 -0
- package/dist/utils/format.d.ts +44 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +75 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -199,9 +199,9 @@ var CONTRACT_ADDRESSES = {
|
|
|
199
199
|
morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
|
|
200
200
|
},
|
|
201
201
|
[8453 /* Base */]: {
|
|
202
|
-
accountFactory: "
|
|
203
|
-
validationRegistry: "
|
|
204
|
-
agentReputation: "
|
|
202
|
+
accountFactory: "0x871eb6b07964bc308bf68b18ca5824AFE5Cb0c8b",
|
|
203
|
+
validationRegistry: "0x5a2FF014C68a2498554B5786ED92483E6d56D06f",
|
|
204
|
+
agentReputation: "0x47adEA82a8975a60D81483CD39C377F905988DF1",
|
|
205
205
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
206
206
|
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
|
|
207
207
|
morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
|
|
@@ -760,6 +760,179 @@ var MorphoClient = class {
|
|
|
760
760
|
};
|
|
761
761
|
}
|
|
762
762
|
// ════════════════════════════════════════════════════════
|
|
763
|
+
// Balance & Borrowing Capacity
|
|
764
|
+
// ════════════════════════════════════════════════════════
|
|
765
|
+
/**
|
|
766
|
+
* Get the USDC balance of the AgentAccount.
|
|
767
|
+
* @returns USDC balance in raw units (6 decimals)
|
|
768
|
+
*/
|
|
769
|
+
async getUsdcBalance() {
|
|
770
|
+
const acctAddr = await this.getAccountAddress();
|
|
771
|
+
const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, this.provider);
|
|
772
|
+
return usdc.balanceOf(acctAddr);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Calculate the maximum additional USDC that can be borrowed
|
|
776
|
+
* given the agent's current collateral and debt across all markets.
|
|
777
|
+
*
|
|
778
|
+
* For each market with collateral deposited:
|
|
779
|
+
* maxBorrow = (collateralValue * LLTV) - currentDebt
|
|
780
|
+
*
|
|
781
|
+
* Uses the Morpho oracle to price collateral → loan token.
|
|
782
|
+
*
|
|
783
|
+
* @returns Maximum additional USDC borrowable (6 decimals)
|
|
784
|
+
*/
|
|
785
|
+
async getMaxBorrowable() {
|
|
786
|
+
const acctAddr = await this.getAccountAddress();
|
|
787
|
+
const markets = await this.getMarkets();
|
|
788
|
+
let totalAdditional = 0n;
|
|
789
|
+
const byMarket = [];
|
|
790
|
+
for (const m of markets) {
|
|
791
|
+
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
792
|
+
try {
|
|
793
|
+
const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
|
|
794
|
+
if (pos.collateral === 0n) continue;
|
|
795
|
+
const mktState = await this.morphoBlue.market(m.uniqueKey);
|
|
796
|
+
const totalBorrowShares = BigInt(mktState.totalBorrowShares);
|
|
797
|
+
const totalBorrowAssets = BigInt(mktState.totalBorrowAssets);
|
|
798
|
+
const currentDebt = totalBorrowShares > 0n ? BigInt(pos.borrowShares) * totalBorrowAssets / totalBorrowShares : 0n;
|
|
799
|
+
let collateralValueInLoan;
|
|
800
|
+
try {
|
|
801
|
+
const oracleContract = new import_ethers2.Contract(m.oracle, [
|
|
802
|
+
"function price() view returns (uint256)"
|
|
803
|
+
], this.provider);
|
|
804
|
+
const oraclePrice = await oracleContract.price();
|
|
805
|
+
const ORACLE_PRICE_SCALE = 10n ** 36n;
|
|
806
|
+
collateralValueInLoan = BigInt(pos.collateral) * oraclePrice / ORACLE_PRICE_SCALE;
|
|
807
|
+
} catch {
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
const maxBorrowTotal = collateralValueInLoan * m.lltv / 10n ** 18n;
|
|
811
|
+
const maxAdditional = maxBorrowTotal > currentDebt ? maxBorrowTotal - currentDebt : 0n;
|
|
812
|
+
totalAdditional += maxAdditional;
|
|
813
|
+
byMarket.push({
|
|
814
|
+
collateralToken: m.collateralAsset.symbol,
|
|
815
|
+
maxAdditional,
|
|
816
|
+
currentDebt,
|
|
817
|
+
collateralValue: collateralValueInLoan
|
|
818
|
+
});
|
|
819
|
+
} catch {
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return { total: totalAdditional, byMarket };
|
|
824
|
+
}
|
|
825
|
+
// ════════════════════════════════════════════════════════
|
|
826
|
+
// Market Rates & Yield Estimation
|
|
827
|
+
// ════════════════════════════════════════════════════════
|
|
828
|
+
/**
|
|
829
|
+
* Fetch current supply/borrow APY for a collateral market from Morpho GraphQL API.
|
|
830
|
+
*
|
|
831
|
+
* Note: On Morpho Blue, collateral does NOT earn yield directly. Supply APY
|
|
832
|
+
* is what lenders earn; borrow APY is what borrowers pay.
|
|
833
|
+
*/
|
|
834
|
+
async getMarketRates(collateralSymbolOrAddress) {
|
|
835
|
+
const chainId = this.config.chainId;
|
|
836
|
+
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
837
|
+
let collateralFilter = "";
|
|
838
|
+
if (collateralSymbolOrAddress) {
|
|
839
|
+
const colInfo = BASE_COLLATERALS[collateralSymbolOrAddress];
|
|
840
|
+
const colAddr = (colInfo?.address ?? collateralSymbolOrAddress).toLowerCase();
|
|
841
|
+
collateralFilter = `, collateralAssetAddress_in: ["${colAddr}"]`;
|
|
842
|
+
}
|
|
843
|
+
const query = `{
|
|
844
|
+
markets(
|
|
845
|
+
first: 50
|
|
846
|
+
orderBy: SupplyAssetsUsd
|
|
847
|
+
orderDirection: Desc
|
|
848
|
+
where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"]${collateralFilter} }
|
|
849
|
+
) {
|
|
850
|
+
items {
|
|
851
|
+
uniqueKey
|
|
852
|
+
lltv
|
|
853
|
+
loanAsset { address symbol decimals }
|
|
854
|
+
collateralAsset { address symbol decimals }
|
|
855
|
+
state {
|
|
856
|
+
borrowAssets
|
|
857
|
+
supplyAssets
|
|
858
|
+
utilization
|
|
859
|
+
supplyApy
|
|
860
|
+
borrowApy
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}`;
|
|
865
|
+
try {
|
|
866
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
867
|
+
const items = resp.data?.data?.markets?.items ?? [];
|
|
868
|
+
return items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress).map((m) => ({
|
|
869
|
+
collateralToken: m.collateralAsset.symbol,
|
|
870
|
+
loanToken: m.loanAsset.symbol,
|
|
871
|
+
supplyApy: m.state?.supplyApy ? Number(m.state.supplyApy) : 0,
|
|
872
|
+
borrowApy: m.state?.borrowApy ? Number(m.state.borrowApy) : 0,
|
|
873
|
+
utilization: m.state?.utilization ? Number(m.state.utilization) : 0,
|
|
874
|
+
totalSupplyUsd: m.state?.supplyAssets ? Number(m.state.supplyAssets) / 1e6 : 0,
|
|
875
|
+
totalBorrowUsd: m.state?.borrowAssets ? Number(m.state.borrowAssets) / 1e6 : 0,
|
|
876
|
+
lltv: `${(Number(m.lltv) / 1e16).toFixed(0)}%`,
|
|
877
|
+
marketId: m.uniqueKey
|
|
878
|
+
}));
|
|
879
|
+
} catch {
|
|
880
|
+
return [];
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* Estimate theoretical yield for a given collateral amount over a period.
|
|
885
|
+
*
|
|
886
|
+
* ⚠️ IMPORTANT: On Morpho Blue, collateral does NOT earn yield directly.
|
|
887
|
+
* This estimates what the collateral WOULD earn if it were instead supplied
|
|
888
|
+
* as a lender (not used as collateral). This is a theoretical upper bound
|
|
889
|
+
* useful for setting spending caps.
|
|
890
|
+
*
|
|
891
|
+
* @param collateralSymbol - e.g. 'WETH'
|
|
892
|
+
* @param amount - collateral amount in human-readable (e.g. '1.5')
|
|
893
|
+
* @param periodDays - estimation period in days (default: 1)
|
|
894
|
+
* @param ethPriceUsd - ETH price in USD for value conversion (if not provided, uses oracle)
|
|
895
|
+
* @returns Estimated yield in USD for the period
|
|
896
|
+
*/
|
|
897
|
+
async getYieldEstimate(collateralSymbol, amount, periodDays = 1, ethPriceUsd) {
|
|
898
|
+
const colInfo = BASE_COLLATERALS[collateralSymbol];
|
|
899
|
+
if (!colInfo) throw new AgetherError(`Unknown collateral: ${collateralSymbol}`, "UNKNOWN_COLLATERAL");
|
|
900
|
+
const rates = await this.getMarketRates(collateralSymbol);
|
|
901
|
+
if (rates.length === 0) {
|
|
902
|
+
throw new AgetherError(`No market found for ${collateralSymbol}`, "MARKET_NOT_FOUND");
|
|
903
|
+
}
|
|
904
|
+
const market = rates[0];
|
|
905
|
+
const supplyApy = market.supplyApy;
|
|
906
|
+
let collateralValueUsd;
|
|
907
|
+
if (ethPriceUsd) {
|
|
908
|
+
collateralValueUsd = parseFloat(amount) * ethPriceUsd;
|
|
909
|
+
} else {
|
|
910
|
+
try {
|
|
911
|
+
const params = await this.findMarketForCollateral(collateralSymbol);
|
|
912
|
+
const oracleContract = new import_ethers2.Contract(params.oracle, [
|
|
913
|
+
"function price() view returns (uint256)"
|
|
914
|
+
], this.provider);
|
|
915
|
+
const oraclePrice = await oracleContract.price();
|
|
916
|
+
const ORACLE_PRICE_SCALE = 10n ** 36n;
|
|
917
|
+
const amountWei = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
|
|
918
|
+
const valueInUsdc = amountWei * oraclePrice / ORACLE_PRICE_SCALE;
|
|
919
|
+
collateralValueUsd = Number(valueInUsdc) / 1e6;
|
|
920
|
+
} catch {
|
|
921
|
+
throw new AgetherError("Cannot determine collateral value. Provide ethPriceUsd.", "PRICE_UNAVAILABLE");
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
const estimatedYieldUsd = collateralValueUsd * supplyApy * (periodDays / 365);
|
|
925
|
+
return {
|
|
926
|
+
collateralToken: collateralSymbol,
|
|
927
|
+
amount,
|
|
928
|
+
periodDays,
|
|
929
|
+
theoreticalSupplyApy: supplyApy,
|
|
930
|
+
estimatedYieldUsd,
|
|
931
|
+
collateralValueUsd,
|
|
932
|
+
disclaimer: "Collateral on Morpho Blue does NOT earn yield directly. This estimates what it WOULD earn if supplied as a lender instead. Use as a theoretical spending cap."
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
// ════════════════════════════════════════════════════════
|
|
763
936
|
// Lending Operations (all via AgentAccount.executeBatch)
|
|
764
937
|
// ════════════════════════════════════════════════════════
|
|
765
938
|
/**
|
|
@@ -1152,6 +1325,9 @@ var X402Client = class {
|
|
|
1152
1325
|
const client = new import_client.x402Client();
|
|
1153
1326
|
(0, import_client2.registerExactEvmScheme)(client, { signer });
|
|
1154
1327
|
this.paidFetch = (0, import_fetch.wrapFetchWithPayment)(fetch, client);
|
|
1328
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1329
|
+
const dailyLimit = config.dailySpendLimitUsdc ? BigInt(Math.round(parseFloat(config.dailySpendLimitUsdc) * 1e6)) : 0n;
|
|
1330
|
+
this._spendingTracker = { date: today, totalBorrowed: 0n, dailyLimit };
|
|
1155
1331
|
}
|
|
1156
1332
|
async get(url, opts) {
|
|
1157
1333
|
return this.request(url, { ...opts, method: "GET" });
|
|
@@ -1167,6 +1343,82 @@ var X402Client = class {
|
|
|
1167
1343
|
getAddress() {
|
|
1168
1344
|
return this.address;
|
|
1169
1345
|
}
|
|
1346
|
+
/** Get the current spending tracker state */
|
|
1347
|
+
getSpendingTracker() {
|
|
1348
|
+
this._resetTrackerIfNewDay();
|
|
1349
|
+
return { ...this._spendingTracker };
|
|
1350
|
+
}
|
|
1351
|
+
/** Get remaining daily spending allowance in USDC (human-readable) */
|
|
1352
|
+
getRemainingDailyAllowance() {
|
|
1353
|
+
this._resetTrackerIfNewDay();
|
|
1354
|
+
if (this._spendingTracker.dailyLimit === 0n) return "unlimited";
|
|
1355
|
+
const remaining = this._spendingTracker.dailyLimit - this._spendingTracker.totalBorrowed;
|
|
1356
|
+
return (Number(remaining > 0n ? remaining : 0n) / 1e6).toFixed(2);
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Pay with auto-draw: Make an x402 request with automatic Morpho borrowing.
|
|
1360
|
+
*
|
|
1361
|
+
* Flow:
|
|
1362
|
+
* 1. Check USDC balance on AgentAccount
|
|
1363
|
+
* 2. Probe the URL to discover payment amount (if 402)
|
|
1364
|
+
* 3. If insufficient USDC, calculate deficit
|
|
1365
|
+
* 4. Check spending limit
|
|
1366
|
+
* 5. Borrow from Morpho via MorphoClient
|
|
1367
|
+
* 6. Proceed with x402 payment
|
|
1368
|
+
*/
|
|
1369
|
+
async payWithAutoDraw(url, opts) {
|
|
1370
|
+
const { morphoClient, ...fetchOpts } = opts || {};
|
|
1371
|
+
if (!this.config.autoDraw || !morphoClient) {
|
|
1372
|
+
return this.request(url, fetchOpts);
|
|
1373
|
+
}
|
|
1374
|
+
try {
|
|
1375
|
+
const usdcBalance = await morphoClient.getUsdcBalance();
|
|
1376
|
+
console.log(` [auto-draw] AgentAccount USDC balance: ${(Number(usdcBalance) / 1e6).toFixed(2)}`);
|
|
1377
|
+
const paymentAmount = await this._probePaymentAmount(url, fetchOpts);
|
|
1378
|
+
if (paymentAmount !== null) {
|
|
1379
|
+
console.log(` [auto-draw] Payment required: ${(Number(paymentAmount) / 1e6).toFixed(6)} USDC`);
|
|
1380
|
+
const bufferStr = this.config.autoDrawBuffer || "0.5";
|
|
1381
|
+
const buffer = BigInt(Math.round(parseFloat(bufferStr) * 1e6));
|
|
1382
|
+
const needed = paymentAmount + buffer;
|
|
1383
|
+
if (usdcBalance < needed) {
|
|
1384
|
+
const deficit = needed - usdcBalance;
|
|
1385
|
+
console.log(` [auto-draw] Insufficient balance. Need to borrow ${(Number(deficit) / 1e6).toFixed(2)} USDC`);
|
|
1386
|
+
const limitCheck = await this._checkSpendingLimit(deficit, morphoClient);
|
|
1387
|
+
if (!limitCheck.allowed) {
|
|
1388
|
+
return {
|
|
1389
|
+
success: false,
|
|
1390
|
+
error: `Auto-draw blocked: ${limitCheck.reason}`
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
const maxBorrowable = await morphoClient.getMaxBorrowable();
|
|
1394
|
+
if (maxBorrowable.total < deficit) {
|
|
1395
|
+
return {
|
|
1396
|
+
success: false,
|
|
1397
|
+
error: `Auto-draw failed: insufficient collateral. Need ${(Number(deficit) / 1e6).toFixed(2)} USDC but can only borrow ${(Number(maxBorrowable.total) / 1e6).toFixed(2)} USDC more.`
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
const borrowAmount = (Number(deficit) / 1e6).toFixed(6);
|
|
1401
|
+
console.log(` [auto-draw] Borrowing ${borrowAmount} USDC from Morpho...`);
|
|
1402
|
+
const borrowResult = await morphoClient.borrow(borrowAmount);
|
|
1403
|
+
console.log(` [auto-draw] Borrow tx: ${borrowResult.tx}`);
|
|
1404
|
+
this._trackSpending(deficit);
|
|
1405
|
+
const result = await this.request(url, fetchOpts);
|
|
1406
|
+
return {
|
|
1407
|
+
...result,
|
|
1408
|
+
autoDrawInfo: {
|
|
1409
|
+
borrowed: borrowAmount,
|
|
1410
|
+
borrowTx: borrowResult.tx,
|
|
1411
|
+
reason: `USDC balance insufficient (had ${(Number(usdcBalance) / 1e6).toFixed(2)}, needed ${(Number(needed) / 1e6).toFixed(2)})`
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
return this.request(url, fetchOpts);
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
console.log(` [auto-draw] Auto-draw check failed: ${error instanceof Error ? error.message : String(error)}. Proceeding with normal request.`);
|
|
1419
|
+
return this.request(url, fetchOpts);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1170
1422
|
// ──────────── Core request — @x402/fetch handles 402 automatically ────────────
|
|
1171
1423
|
async request(url, options) {
|
|
1172
1424
|
try {
|
|
@@ -1216,6 +1468,102 @@ var X402Client = class {
|
|
|
1216
1468
|
};
|
|
1217
1469
|
}
|
|
1218
1470
|
}
|
|
1471
|
+
// ──────────── Auto-Draw Helpers ────────────
|
|
1472
|
+
/**
|
|
1473
|
+
* Probe a URL to discover payment requirements without paying.
|
|
1474
|
+
* Makes a request and parses the 402 PAYMENT-REQUIRED header.
|
|
1475
|
+
* @returns Payment amount in raw USDC units (6 decimals), or null if not a 402.
|
|
1476
|
+
*/
|
|
1477
|
+
async _probePaymentAmount(url, options) {
|
|
1478
|
+
try {
|
|
1479
|
+
const response = await fetch(url, {
|
|
1480
|
+
...options,
|
|
1481
|
+
headers: {
|
|
1482
|
+
...options?.headers,
|
|
1483
|
+
"X-Agent-Id": this.config.agentId || ""
|
|
1484
|
+
}
|
|
1485
|
+
});
|
|
1486
|
+
if (response.status !== 402) return null;
|
|
1487
|
+
const paymentHeader = response.headers.get("X-PAYMENT") || response.headers.get("PAYMENT-REQUIRED");
|
|
1488
|
+
if (!paymentHeader) return null;
|
|
1489
|
+
try {
|
|
1490
|
+
const decoded = JSON.parse(Buffer.from(paymentHeader, "base64").toString("utf-8"));
|
|
1491
|
+
const requirements = Array.isArray(decoded) ? decoded : decoded.accepts || [decoded];
|
|
1492
|
+
if (requirements.length > 0) {
|
|
1493
|
+
const amount = requirements[0].maxAmountRequired || requirements[0].amount;
|
|
1494
|
+
if (amount) {
|
|
1495
|
+
return BigInt(amount);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
} catch {
|
|
1499
|
+
}
|
|
1500
|
+
return null;
|
|
1501
|
+
} catch {
|
|
1502
|
+
return null;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
/** Reset spending tracker if it's a new day */
|
|
1506
|
+
_resetTrackerIfNewDay() {
|
|
1507
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1508
|
+
if (this._spendingTracker.date !== today) {
|
|
1509
|
+
this._spendingTracker = {
|
|
1510
|
+
date: today,
|
|
1511
|
+
totalBorrowed: 0n,
|
|
1512
|
+
dailyLimit: this._spendingTracker.dailyLimit
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
/** Track a new spending amount */
|
|
1517
|
+
_trackSpending(amount) {
|
|
1518
|
+
this._resetTrackerIfNewDay();
|
|
1519
|
+
this._spendingTracker.totalBorrowed += amount;
|
|
1520
|
+
}
|
|
1521
|
+
/**
|
|
1522
|
+
* Check if a borrow amount is within spending limits.
|
|
1523
|
+
* Considers both fixed daily limits and yield-limited spending.
|
|
1524
|
+
*/
|
|
1525
|
+
async _checkSpendingLimit(amount, morphoClient) {
|
|
1526
|
+
this._resetTrackerIfNewDay();
|
|
1527
|
+
if (this.config.yieldLimitedSpending) {
|
|
1528
|
+
try {
|
|
1529
|
+
const status = await morphoClient.getStatus();
|
|
1530
|
+
let totalDailyYieldUsdc = 0;
|
|
1531
|
+
for (const pos of status.positions) {
|
|
1532
|
+
if (parseFloat(pos.collateral) > 0) {
|
|
1533
|
+
try {
|
|
1534
|
+
const estimate = await morphoClient.getYieldEstimate(
|
|
1535
|
+
pos.collateralToken,
|
|
1536
|
+
pos.collateral,
|
|
1537
|
+
1
|
|
1538
|
+
// 1 day
|
|
1539
|
+
);
|
|
1540
|
+
totalDailyYieldUsdc += estimate.estimatedYieldUsd;
|
|
1541
|
+
} catch {
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
const yieldLimit = BigInt(Math.round(totalDailyYieldUsdc * 1e6));
|
|
1546
|
+
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
1547
|
+
if (yieldLimit > 0n && newTotal > yieldLimit) {
|
|
1548
|
+
return {
|
|
1549
|
+
allowed: false,
|
|
1550
|
+
reason: `Yield-limited spending exceeded. Daily yield cap: $${(Number(yieldLimit) / 1e6).toFixed(2)}, already spent: $${(Number(this._spendingTracker.totalBorrowed) / 1e6).toFixed(2)}, requested: $${(Number(amount) / 1e6).toFixed(2)}`
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
} catch {
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
if (this._spendingTracker.dailyLimit > 0n) {
|
|
1557
|
+
const newTotal = this._spendingTracker.totalBorrowed + amount;
|
|
1558
|
+
if (newTotal > this._spendingTracker.dailyLimit) {
|
|
1559
|
+
return {
|
|
1560
|
+
allowed: false,
|
|
1561
|
+
reason: `Daily spending limit exceeded. Limit: $${(Number(this._spendingTracker.dailyLimit) / 1e6).toFixed(2)}, already spent: $${(Number(this._spendingTracker.totalBorrowed) / 1e6).toFixed(2)}, requested: $${(Number(amount) / 1e6).toFixed(2)}`
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return { allowed: true };
|
|
1566
|
+
}
|
|
1219
1567
|
};
|
|
1220
1568
|
|
|
1221
1569
|
// src/clients/ScoringClient.ts
|