@agether/sdk 2.14.0 → 2.15.0
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 +373 -42
- package/dist/index.d.mts +60 -6
- package/dist/index.d.ts +60 -6
- package/dist/index.js +381 -50
- package/dist/index.mjs +381 -50
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -75,6 +75,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
75
75
|
|
|
76
76
|
// src/clients/AgetherClient.ts
|
|
77
77
|
var import_ethers = require("ethers");
|
|
78
|
+
var import_axios = __toESM(require("axios"));
|
|
78
79
|
|
|
79
80
|
// src/types/index.ts
|
|
80
81
|
var ChainId = /* @__PURE__ */ ((ChainId3) => {
|
|
@@ -371,6 +372,7 @@ function getContractAddresses(chainId) {
|
|
|
371
372
|
// src/clients/AgetherClient.ts
|
|
372
373
|
var MODE_SINGLE = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
373
374
|
var erc20Iface = new import_ethers.ethers.Interface(ERC20_ABI);
|
|
375
|
+
var MORPHO_API_URL = "https://api.morpho.org/graphql";
|
|
374
376
|
var KNOWN_TOKENS = {
|
|
375
377
|
[8453 /* Base */]: {
|
|
376
378
|
WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
|
|
@@ -385,6 +387,17 @@ var KNOWN_TOKENS = {
|
|
|
385
387
|
};
|
|
386
388
|
var AgetherClient = class _AgetherClient {
|
|
387
389
|
constructor(options) {
|
|
390
|
+
/**
|
|
391
|
+
* Resolve a token symbol or address to { address, symbol, decimals }.
|
|
392
|
+
*
|
|
393
|
+
* Resolution order:
|
|
394
|
+
* 1. `'USDC'` → from chain config (instant)
|
|
395
|
+
* 2. Well-known symbols (`'WETH'`, `'wstETH'`, `'cbETH'`) → built-in per-chain registry (instant)
|
|
396
|
+
* 3. Dynamic cache populated by previous Morpho API lookups (instant)
|
|
397
|
+
* 4. `'0x...'` address → reads decimals and symbol onchain
|
|
398
|
+
* 5. Morpho GraphQL `search` API → discovers any token across all Morpho markets
|
|
399
|
+
*/
|
|
400
|
+
this._dynamicTokenCache = /* @__PURE__ */ new Map();
|
|
388
401
|
this.config = options.config;
|
|
389
402
|
this.signer = options.signer;
|
|
390
403
|
this.agentId = options.agentId;
|
|
@@ -669,21 +682,10 @@ var AgetherClient = class _AgetherClient {
|
|
|
669
682
|
}
|
|
670
683
|
/**
|
|
671
684
|
* Fund the Safe account with USDC from EOA.
|
|
672
|
-
*
|
|
685
|
+
* @deprecated Use `fundAccountToken('USDC', amount)` instead.
|
|
673
686
|
*/
|
|
674
687
|
async fundAccount(usdcAmount) {
|
|
675
|
-
|
|
676
|
-
const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, this.signer);
|
|
677
|
-
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
678
|
-
const tx = await usdc.transfer(acctAddr, amount);
|
|
679
|
-
const receipt = await tx.wait();
|
|
680
|
-
this._refreshSigner();
|
|
681
|
-
return {
|
|
682
|
-
txHash: receipt.hash,
|
|
683
|
-
blockNumber: receipt.blockNumber,
|
|
684
|
-
status: receipt.status === 1 ? "success" : "failed",
|
|
685
|
-
gasUsed: receipt.gasUsed
|
|
686
|
-
};
|
|
688
|
+
return this.fundAccountToken("USDC", usdcAmount);
|
|
687
689
|
}
|
|
688
690
|
// ════════════════════════════════════════════════════════
|
|
689
691
|
// Withdrawals (Safe → EOA via UserOps)
|
|
@@ -735,6 +737,138 @@ var AgetherClient = class _AgetherClient {
|
|
|
735
737
|
return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: eoaAddr };
|
|
736
738
|
}
|
|
737
739
|
// ════════════════════════════════════════════════════════
|
|
740
|
+
// Token Transfers & Approvals (AgentAccount → any address)
|
|
741
|
+
// ════════════════════════════════════════════════════════
|
|
742
|
+
/**
|
|
743
|
+
* Fund the AgentAccount with any ERC-20 token from EOA.
|
|
744
|
+
* Simple ERC-20 transfer (does NOT require a UserOp).
|
|
745
|
+
*
|
|
746
|
+
* @param tokenSymbol - Token symbol (e.g. 'USDC', 'WETH') or 0x address
|
|
747
|
+
* @param amount - Amount to send (human-readable, e.g. '100')
|
|
748
|
+
*/
|
|
749
|
+
async fundAccountToken(tokenSymbol, amount) {
|
|
750
|
+
const acctAddr = await this.getAccountAddress();
|
|
751
|
+
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
752
|
+
const tokenContract = new import_ethers.Contract(tokenInfo.address, ERC20_ABI, this.signer);
|
|
753
|
+
const weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
754
|
+
const eoaBalance = await tokenContract.balanceOf(await this._getSignerAddress());
|
|
755
|
+
if (eoaBalance < weiAmount) {
|
|
756
|
+
throw new AgetherError(
|
|
757
|
+
`Insufficient ${tokenInfo.symbol}. EOA has ${import_ethers.ethers.formatUnits(eoaBalance, tokenInfo.decimals)}, need ${amount}.`,
|
|
758
|
+
"INSUFFICIENT_BALANCE"
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
const tx = await tokenContract.transfer(acctAddr, weiAmount);
|
|
762
|
+
const receipt = await tx.wait();
|
|
763
|
+
this._refreshSigner();
|
|
764
|
+
return {
|
|
765
|
+
txHash: receipt.hash,
|
|
766
|
+
blockNumber: receipt.blockNumber,
|
|
767
|
+
status: receipt.status === 1 ? "success" : "failed",
|
|
768
|
+
gasUsed: receipt.gasUsed
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Transfer any ERC-20 token from AgentAccount to any address or agent.
|
|
773
|
+
* Executes via Safe UserOp (ERC-7579 single execution).
|
|
774
|
+
*
|
|
775
|
+
* @param tokenSymbol - Token symbol (e.g. 'USDC', 'WETH') or 0x address
|
|
776
|
+
* @param amount - Amount to send (e.g. '100' or 'all')
|
|
777
|
+
* @param to - Destination: `{ address: '0x...' }` or `{ agentId: '42' }`
|
|
778
|
+
*/
|
|
779
|
+
async transferToken(tokenSymbol, amount, to) {
|
|
780
|
+
const acctAddr = await this.getAccountAddress();
|
|
781
|
+
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
782
|
+
const tokenContract = new import_ethers.Contract(tokenInfo.address, ERC20_ABI, this.signer.provider);
|
|
783
|
+
let toAddr;
|
|
784
|
+
if (to.address) {
|
|
785
|
+
toAddr = to.address;
|
|
786
|
+
} else if (to.agentId) {
|
|
787
|
+
toAddr = await this.agether4337Factory.getAccount(BigInt(to.agentId));
|
|
788
|
+
if (toAddr === import_ethers.ethers.ZeroAddress) {
|
|
789
|
+
throw new AgetherError(`Agent ${to.agentId} has no account`, "NO_ACCOUNT");
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
throw new AgetherError("Provide address or agentId as destination", "INVALID_TARGET");
|
|
793
|
+
}
|
|
794
|
+
let weiAmount;
|
|
795
|
+
if (amount === "all") {
|
|
796
|
+
weiAmount = await tokenContract.balanceOf(acctAddr);
|
|
797
|
+
if (weiAmount === 0n) {
|
|
798
|
+
throw new AgetherError(`No ${tokenInfo.symbol} in AgentAccount`, "INSUFFICIENT_BALANCE");
|
|
799
|
+
}
|
|
800
|
+
} else {
|
|
801
|
+
weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
802
|
+
}
|
|
803
|
+
const data = erc20Iface.encodeFunctionData("transfer", [toAddr, weiAmount]);
|
|
804
|
+
const receipt = await this._exec(tokenInfo.address, data);
|
|
805
|
+
const actualAmount = amount === "all" ? import_ethers.ethers.formatUnits(weiAmount, tokenInfo.decimals) : amount;
|
|
806
|
+
return { tx: receipt.hash, token: tokenInfo.symbol, amount: actualAmount, destination: toAddr };
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Transfer ETH from AgentAccount to any address or agent.
|
|
810
|
+
* Executes via Safe UserOp.
|
|
811
|
+
*
|
|
812
|
+
* @param amount - ETH amount (e.g. '0.01' or 'all')
|
|
813
|
+
* @param to - Destination: `{ address: '0x...' }` or `{ agentId: '42' }`
|
|
814
|
+
*/
|
|
815
|
+
async transferEth(amount, to) {
|
|
816
|
+
const acctAddr = await this.getAccountAddress();
|
|
817
|
+
let toAddr;
|
|
818
|
+
if (to.address) {
|
|
819
|
+
toAddr = to.address;
|
|
820
|
+
} else if (to.agentId) {
|
|
821
|
+
toAddr = await this.agether4337Factory.getAccount(BigInt(to.agentId));
|
|
822
|
+
if (toAddr === import_ethers.ethers.ZeroAddress) {
|
|
823
|
+
throw new AgetherError(`Agent ${to.agentId} has no account`, "NO_ACCOUNT");
|
|
824
|
+
}
|
|
825
|
+
} else {
|
|
826
|
+
throw new AgetherError("Provide address or agentId as destination", "INVALID_TARGET");
|
|
827
|
+
}
|
|
828
|
+
let weiAmount;
|
|
829
|
+
if (amount === "all") {
|
|
830
|
+
weiAmount = await this.signer.provider.getBalance(acctAddr);
|
|
831
|
+
if (weiAmount === 0n) throw new AgetherError("No ETH in AgentAccount", "INSUFFICIENT_BALANCE");
|
|
832
|
+
} else {
|
|
833
|
+
weiAmount = import_ethers.ethers.parseEther(amount);
|
|
834
|
+
}
|
|
835
|
+
const receipt = await this._exec(toAddr, "0x", weiAmount);
|
|
836
|
+
const actualAmount = amount === "all" ? import_ethers.ethers.formatEther(weiAmount) : amount;
|
|
837
|
+
return { tx: receipt.hash, token: "ETH", amount: actualAmount, destination: toAddr };
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* Approve a spender to use ERC-20 tokens from the AgentAccount.
|
|
841
|
+
* Executes via Safe UserOp (ERC-7579 single execution).
|
|
842
|
+
*
|
|
843
|
+
* @param tokenSymbol - Token symbol (e.g. 'USDC', 'WETH') or 0x address
|
|
844
|
+
* @param amount - Allowance amount (e.g. '1000' or 'max' for uint256 max)
|
|
845
|
+
* @param spender - Spender: `{ address: '0x...' }` or `{ agentId: '42' }`
|
|
846
|
+
*/
|
|
847
|
+
async approveToken(tokenSymbol, amount, spender) {
|
|
848
|
+
const tokenInfo = await this._resolveToken(tokenSymbol);
|
|
849
|
+
let spenderAddr;
|
|
850
|
+
if (spender.address) {
|
|
851
|
+
spenderAddr = spender.address;
|
|
852
|
+
} else if (spender.agentId) {
|
|
853
|
+
spenderAddr = await this.agether4337Factory.getAccount(BigInt(spender.agentId));
|
|
854
|
+
if (spenderAddr === import_ethers.ethers.ZeroAddress) {
|
|
855
|
+
throw new AgetherError(`Agent ${spender.agentId} has no account`, "NO_ACCOUNT");
|
|
856
|
+
}
|
|
857
|
+
} else {
|
|
858
|
+
throw new AgetherError("Provide address or agentId as spender", "INVALID_TARGET");
|
|
859
|
+
}
|
|
860
|
+
let weiAmount;
|
|
861
|
+
if (amount === "max") {
|
|
862
|
+
weiAmount = import_ethers.ethers.MaxUint256;
|
|
863
|
+
} else {
|
|
864
|
+
weiAmount = import_ethers.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
865
|
+
}
|
|
866
|
+
const data = erc20Iface.encodeFunctionData("approve", [spenderAddr, weiAmount]);
|
|
867
|
+
const receipt = await this._exec(tokenInfo.address, data);
|
|
868
|
+
const actualAmount = amount === "max" ? "unlimited" : amount;
|
|
869
|
+
return { tx: receipt.hash, token: tokenInfo.symbol, amount: actualAmount, spender: spenderAddr };
|
|
870
|
+
}
|
|
871
|
+
// ════════════════════════════════════════════════════════
|
|
738
872
|
// Sponsorship
|
|
739
873
|
// ════════════════════════════════════════════════════════
|
|
740
874
|
/**
|
|
@@ -863,14 +997,6 @@ var AgetherClient = class _AgetherClient {
|
|
|
863
997
|
}
|
|
864
998
|
return this._eoaAddress;
|
|
865
999
|
}
|
|
866
|
-
/**
|
|
867
|
-
* Resolve a token symbol or address to { address, symbol, decimals }.
|
|
868
|
-
*
|
|
869
|
-
* Supports:
|
|
870
|
-
* - `'USDC'` → from chain config
|
|
871
|
-
* - Well-known symbols (`'WETH'`, `'wstETH'`, `'cbETH'`) → built-in per-chain registry
|
|
872
|
-
* - `'0x...'` address → reads decimals and symbol onchain
|
|
873
|
-
*/
|
|
874
1000
|
async _resolveToken(symbolOrAddress) {
|
|
875
1001
|
if (symbolOrAddress.toUpperCase() === "USDC") {
|
|
876
1002
|
return { address: this.config.contracts.usdc, symbol: "USDC", decimals: 6 };
|
|
@@ -878,6 +1004,9 @@ var AgetherClient = class _AgetherClient {
|
|
|
878
1004
|
const chainTokens = KNOWN_TOKENS[this.config.chainId] ?? {};
|
|
879
1005
|
const bySymbol = chainTokens[symbolOrAddress] || chainTokens[symbolOrAddress.toUpperCase()];
|
|
880
1006
|
if (bySymbol) return bySymbol;
|
|
1007
|
+
const cacheKey = symbolOrAddress.startsWith("0x") ? symbolOrAddress.toLowerCase() : symbolOrAddress.toUpperCase();
|
|
1008
|
+
const cached = this._dynamicTokenCache.get(cacheKey);
|
|
1009
|
+
if (cached) return cached;
|
|
881
1010
|
if (symbolOrAddress.startsWith("0x") && symbolOrAddress.length === 42) {
|
|
882
1011
|
try {
|
|
883
1012
|
const token = new import_ethers.Contract(
|
|
@@ -886,7 +1015,10 @@ var AgetherClient = class _AgetherClient {
|
|
|
886
1015
|
this.signer.provider
|
|
887
1016
|
);
|
|
888
1017
|
const [decimals, symbol] = await Promise.all([token.decimals(), token.symbol()]);
|
|
889
|
-
|
|
1018
|
+
const info = { address: symbolOrAddress, symbol, decimals: Number(decimals) };
|
|
1019
|
+
this._dynamicTokenCache.set(symbolOrAddress.toLowerCase(), info);
|
|
1020
|
+
this._dynamicTokenCache.set(symbol.toUpperCase(), info);
|
|
1021
|
+
return info;
|
|
890
1022
|
} catch (e) {
|
|
891
1023
|
throw new AgetherError(
|
|
892
1024
|
`Failed to read token at ${symbolOrAddress}: ${e instanceof Error ? e.message : e}`,
|
|
@@ -894,8 +1026,53 @@ var AgetherClient = class _AgetherClient {
|
|
|
894
1026
|
);
|
|
895
1027
|
}
|
|
896
1028
|
}
|
|
1029
|
+
try {
|
|
1030
|
+
const chainId = this.config.chainId;
|
|
1031
|
+
const query = `{
|
|
1032
|
+
markets(
|
|
1033
|
+
first: 20
|
|
1034
|
+
where: { chainId_in: [${chainId}], search: "${symbolOrAddress}" }
|
|
1035
|
+
) {
|
|
1036
|
+
items {
|
|
1037
|
+
loanAsset { address symbol decimals }
|
|
1038
|
+
collateralAsset { address symbol decimals }
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
}`;
|
|
1042
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
1043
|
+
const items = resp.data?.data?.markets?.items ?? [];
|
|
1044
|
+
const sym = symbolOrAddress.toUpperCase();
|
|
1045
|
+
for (const m of items) {
|
|
1046
|
+
if (m.loanAsset?.symbol?.toUpperCase() === sym) {
|
|
1047
|
+
const info = { address: m.loanAsset.address, symbol: m.loanAsset.symbol, decimals: m.loanAsset.decimals };
|
|
1048
|
+
this._dynamicTokenCache.set(sym, info);
|
|
1049
|
+
this._dynamicTokenCache.set(m.loanAsset.address.toLowerCase(), info);
|
|
1050
|
+
return info;
|
|
1051
|
+
}
|
|
1052
|
+
if (m.collateralAsset?.symbol?.toUpperCase() === sym) {
|
|
1053
|
+
const info = { address: m.collateralAsset.address, symbol: m.collateralAsset.symbol, decimals: m.collateralAsset.decimals };
|
|
1054
|
+
this._dynamicTokenCache.set(sym, info);
|
|
1055
|
+
this._dynamicTokenCache.set(m.collateralAsset.address.toLowerCase(), info);
|
|
1056
|
+
return info;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
for (const m of items) {
|
|
1060
|
+
if (m.loanAsset?.symbol) {
|
|
1061
|
+
const info = { address: m.loanAsset.address, symbol: m.loanAsset.symbol, decimals: m.loanAsset.decimals };
|
|
1062
|
+
this._dynamicTokenCache.set(m.loanAsset.symbol.toUpperCase(), info);
|
|
1063
|
+
this._dynamicTokenCache.set(m.loanAsset.address.toLowerCase(), info);
|
|
1064
|
+
}
|
|
1065
|
+
if (m.collateralAsset?.symbol) {
|
|
1066
|
+
const info = { address: m.collateralAsset.address, symbol: m.collateralAsset.symbol, decimals: m.collateralAsset.decimals };
|
|
1067
|
+
this._dynamicTokenCache.set(m.collateralAsset.symbol.toUpperCase(), info);
|
|
1068
|
+
this._dynamicTokenCache.set(m.collateralAsset.address.toLowerCase(), info);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
} catch (e) {
|
|
1072
|
+
console.warn("[agether] Morpho token search failed:", e instanceof Error ? e.message : e);
|
|
1073
|
+
}
|
|
897
1074
|
throw new AgetherError(
|
|
898
|
-
`Unknown token: ${symbolOrAddress}.
|
|
1075
|
+
`Unknown token: ${symbolOrAddress}. No Morpho market found with this token.`,
|
|
899
1076
|
"UNKNOWN_TOKEN"
|
|
900
1077
|
);
|
|
901
1078
|
}
|
|
@@ -1029,8 +1206,8 @@ var AgetherClient = class _AgetherClient {
|
|
|
1029
1206
|
|
|
1030
1207
|
// src/clients/MorphoClient.ts
|
|
1031
1208
|
var import_ethers2 = require("ethers");
|
|
1032
|
-
var
|
|
1033
|
-
var
|
|
1209
|
+
var import_axios2 = __toESM(require("axios"));
|
|
1210
|
+
var MORPHO_API_URL2 = "https://api.morpho.org/graphql";
|
|
1034
1211
|
var MODE_SINGLE2 = "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
1035
1212
|
var MODE_BATCH = "0x0100000000000000000000000000000000000000000000000000000000000000";
|
|
1036
1213
|
var morphoIface = new import_ethers2.ethers.Interface(MORPHO_BLUE_ABI);
|
|
@@ -1176,7 +1353,7 @@ var MorphoClient = class {
|
|
|
1176
1353
|
}
|
|
1177
1354
|
}`;
|
|
1178
1355
|
try {
|
|
1179
|
-
const resp = await
|
|
1356
|
+
const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
|
|
1180
1357
|
const items = resp.data?.data?.markets?.items ?? [];
|
|
1181
1358
|
this._discoveredMarkets = items.map((m) => ({
|
|
1182
1359
|
uniqueKey: m.uniqueKey,
|
|
@@ -1261,7 +1438,7 @@ var MorphoClient = class {
|
|
|
1261
1438
|
const resolved = await this._resolveToken(loanTokenSymbolOrAddress);
|
|
1262
1439
|
loanAddr = resolved.address.toLowerCase();
|
|
1263
1440
|
} catch {
|
|
1264
|
-
loanAddr =
|
|
1441
|
+
loanAddr = void 0;
|
|
1265
1442
|
}
|
|
1266
1443
|
}
|
|
1267
1444
|
}
|
|
@@ -1269,6 +1446,7 @@ var MorphoClient = class {
|
|
|
1269
1446
|
for (const m of this._discoveredMarkets ?? []) {
|
|
1270
1447
|
if (m.collateralAsset.address.toLowerCase() !== colAddr) continue;
|
|
1271
1448
|
if (loanAddr && m.loanAsset.address.toLowerCase() !== loanAddr) continue;
|
|
1449
|
+
if (!loanAddr && loanTokenSymbolOrAddress && m.loanAsset.symbol.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
|
|
1272
1450
|
return {
|
|
1273
1451
|
loanToken: m.loanAsset.address,
|
|
1274
1452
|
collateralToken: m.collateralAsset.address,
|
|
@@ -1279,9 +1457,22 @@ var MorphoClient = class {
|
|
|
1279
1457
|
}
|
|
1280
1458
|
if (!collateralSymbolOrAddress.startsWith("0x")) {
|
|
1281
1459
|
const searched = await this.searchMarkets(collateralSymbolOrAddress, { asCollateral: true });
|
|
1282
|
-
|
|
1460
|
+
const allResults = [...searched];
|
|
1461
|
+
if (loanTokenSymbolOrAddress && !loanTokenSymbolOrAddress.startsWith("0x")) {
|
|
1462
|
+
const loanSearched = await this.searchMarkets(loanTokenSymbolOrAddress, { asLoanToken: true });
|
|
1463
|
+
const seen = new Set(allResults.map((r) => r.marketId));
|
|
1464
|
+
for (const m of loanSearched) {
|
|
1465
|
+
if (!seen.has(m.marketId)) allResults.push(m);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
for (const m of allResults) {
|
|
1469
|
+
if (colAddr.startsWith("0x") && m.collateralAddress.toLowerCase() !== colAddr) {
|
|
1470
|
+
if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) continue;
|
|
1471
|
+
} else if (m.collateralToken.toUpperCase() !== collateralSymbolOrAddress.toUpperCase()) {
|
|
1472
|
+
continue;
|
|
1473
|
+
}
|
|
1283
1474
|
if (loanAddr && m.loanAddress.toLowerCase() !== loanAddr) continue;
|
|
1284
|
-
if (
|
|
1475
|
+
if (!loanAddr && loanTokenSymbolOrAddress && m.loanToken.toUpperCase() !== loanTokenSymbolOrAddress.toUpperCase()) continue;
|
|
1285
1476
|
return this.getMarketParams(m.marketId);
|
|
1286
1477
|
}
|
|
1287
1478
|
}
|
|
@@ -1315,26 +1506,54 @@ var MorphoClient = class {
|
|
|
1315
1506
|
};
|
|
1316
1507
|
}
|
|
1317
1508
|
/**
|
|
1318
|
-
* Full status: positions across all
|
|
1509
|
+
* Full status: positions across all markets the user has interacted with.
|
|
1510
|
+
*
|
|
1511
|
+
* Uses Morpho GraphQL `marketPositions` to find ALL positions (not limited
|
|
1512
|
+
* to the top-500 markets), then reads onchain data for accurate debt.
|
|
1319
1513
|
*/
|
|
1320
1514
|
async getStatus() {
|
|
1321
1515
|
const acctAddr = await this.getAccountAddress();
|
|
1322
|
-
const
|
|
1516
|
+
const chainId = this.config.chainId;
|
|
1323
1517
|
const positions = [];
|
|
1324
1518
|
let totalDebtFloat = 0;
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1519
|
+
try {
|
|
1520
|
+
const posQuery = `{
|
|
1521
|
+
marketPositions(
|
|
1522
|
+
where: {
|
|
1523
|
+
userAddress_in: ["${acctAddr}"]
|
|
1524
|
+
chainId_in: [${chainId}]
|
|
1525
|
+
}
|
|
1526
|
+
first: 100
|
|
1527
|
+
) {
|
|
1528
|
+
items {
|
|
1529
|
+
supplyShares
|
|
1530
|
+
borrowShares
|
|
1531
|
+
collateral
|
|
1532
|
+
market {
|
|
1533
|
+
uniqueKey
|
|
1534
|
+
loanAsset { symbol address decimals }
|
|
1535
|
+
collateralAsset { symbol address decimals }
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
}`;
|
|
1540
|
+
const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: posQuery }, { timeout: 15e3 });
|
|
1541
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
1542
|
+
for (const item of items) {
|
|
1543
|
+
const supplyShares = BigInt(item.supplyShares ?? "0");
|
|
1544
|
+
const borrowShares = BigInt(item.borrowShares ?? "0");
|
|
1545
|
+
const collateral = BigInt(item.collateral ?? "0");
|
|
1546
|
+
if (collateral === 0n && borrowShares === 0n && supplyShares === 0n) continue;
|
|
1547
|
+
const m = item.market;
|
|
1548
|
+
if (!m?.collateralAsset || !m?.loanAsset) continue;
|
|
1330
1549
|
const loanDecimals = m.loanAsset.decimals;
|
|
1331
1550
|
let debt = 0n;
|
|
1332
|
-
if (
|
|
1551
|
+
if (borrowShares > 0n) {
|
|
1333
1552
|
try {
|
|
1334
1553
|
const mkt = await this.morphoBlue.market(m.uniqueKey);
|
|
1335
1554
|
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
1336
1555
|
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
1337
|
-
debt = totalBorrowShares > 0n ? (
|
|
1556
|
+
debt = totalBorrowShares > 0n ? (borrowShares * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1338
1557
|
totalDebtFloat += parseFloat(import_ethers2.ethers.formatUnits(debt, loanDecimals));
|
|
1339
1558
|
} catch (e) {
|
|
1340
1559
|
console.warn(`[agether] debt calc failed for market ${m.uniqueKey}:`, e instanceof Error ? e.message : e);
|
|
@@ -1344,14 +1563,46 @@ var MorphoClient = class {
|
|
|
1344
1563
|
marketId: m.uniqueKey,
|
|
1345
1564
|
collateralToken: m.collateralAsset.symbol,
|
|
1346
1565
|
loanToken: m.loanAsset.symbol,
|
|
1347
|
-
collateral: import_ethers2.ethers.formatUnits(
|
|
1348
|
-
borrowShares:
|
|
1349
|
-
supplyShares:
|
|
1566
|
+
collateral: import_ethers2.ethers.formatUnits(collateral, m.collateralAsset.decimals),
|
|
1567
|
+
borrowShares: borrowShares.toString(),
|
|
1568
|
+
supplyShares: supplyShares.toString(),
|
|
1350
1569
|
debt: import_ethers2.ethers.formatUnits(debt, loanDecimals)
|
|
1351
1570
|
});
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
|
|
1571
|
+
}
|
|
1572
|
+
} catch (e) {
|
|
1573
|
+
console.warn("[agether] marketPositions API failed, falling back to market scan:", e instanceof Error ? e.message : e);
|
|
1574
|
+
const markets = await this.getMarkets();
|
|
1575
|
+
for (const m of markets) {
|
|
1576
|
+
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
1577
|
+
try {
|
|
1578
|
+
const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
|
|
1579
|
+
if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
|
|
1580
|
+
const loanDecimals = m.loanAsset.decimals;
|
|
1581
|
+
let debt = 0n;
|
|
1582
|
+
if (pos.borrowShares > 0n) {
|
|
1583
|
+
try {
|
|
1584
|
+
const mkt = await this.morphoBlue.market(m.uniqueKey);
|
|
1585
|
+
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
1586
|
+
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
1587
|
+
debt = totalBorrowShares > 0n ? (BigInt(pos.borrowShares) * totalBorrowAssets + totalBorrowShares - 1n) / totalBorrowShares : 0n;
|
|
1588
|
+
totalDebtFloat += parseFloat(import_ethers2.ethers.formatUnits(debt, loanDecimals));
|
|
1589
|
+
} catch (e2) {
|
|
1590
|
+
console.warn(`[agether] debt calc failed:`, e2 instanceof Error ? e2.message : e2);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
positions.push({
|
|
1594
|
+
marketId: m.uniqueKey,
|
|
1595
|
+
collateralToken: m.collateralAsset.symbol,
|
|
1596
|
+
loanToken: m.loanAsset.symbol,
|
|
1597
|
+
collateral: import_ethers2.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
|
|
1598
|
+
borrowShares: pos.borrowShares.toString(),
|
|
1599
|
+
supplyShares: pos.supplyShares.toString(),
|
|
1600
|
+
debt: import_ethers2.ethers.formatUnits(debt, loanDecimals)
|
|
1601
|
+
});
|
|
1602
|
+
} catch (e2) {
|
|
1603
|
+
console.warn(`[agether] position read failed:`, e2 instanceof Error ? e2.message : e2);
|
|
1604
|
+
continue;
|
|
1605
|
+
}
|
|
1355
1606
|
}
|
|
1356
1607
|
}
|
|
1357
1608
|
return {
|
|
@@ -1505,7 +1756,7 @@ var MorphoClient = class {
|
|
|
1505
1756
|
}
|
|
1506
1757
|
}`;
|
|
1507
1758
|
try {
|
|
1508
|
-
const resp = await
|
|
1759
|
+
const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
|
|
1509
1760
|
let items = resp.data?.data?.markets?.items ?? [];
|
|
1510
1761
|
if (searchTerm && collateralSymbolOrAddress && !collateralSymbolOrAddress.startsWith("0x")) {
|
|
1511
1762
|
const sym = collateralSymbolOrAddress.toUpperCase();
|
|
@@ -1571,7 +1822,7 @@ var MorphoClient = class {
|
|
|
1571
1822
|
}
|
|
1572
1823
|
}`;
|
|
1573
1824
|
try {
|
|
1574
|
-
const resp = await
|
|
1825
|
+
const resp = await import_axios2.default.post(MORPHO_API_URL2, { query }, { timeout: 1e4 });
|
|
1575
1826
|
let items = resp.data?.data?.markets?.items ?? [];
|
|
1576
1827
|
items = items.filter((m) => m.collateralAsset?.address && m.collateralAsset.address !== import_ethers2.ethers.ZeroAddress);
|
|
1577
1828
|
const searchUpper = search.toUpperCase();
|
|
@@ -1964,7 +2215,7 @@ var MorphoClient = class {
|
|
|
1964
2215
|
}
|
|
1965
2216
|
}
|
|
1966
2217
|
}`;
|
|
1967
|
-
const posResp = await
|
|
2218
|
+
const posResp = await import_axios2.default.post(MORPHO_API_URL2, { query: positionsQuery }, { timeout: 15e3 });
|
|
1968
2219
|
const user = posResp.data?.data?.userByAddress;
|
|
1969
2220
|
if (!user?.marketPositions) return [];
|
|
1970
2221
|
const activePositions = user.marketPositions.filter(
|
|
@@ -2638,6 +2889,46 @@ var MorphoClient = class {
|
|
|
2638
2889
|
/** Find the first market where the agent has collateral deposited. */
|
|
2639
2890
|
async _findActiveMarket() {
|
|
2640
2891
|
const acctAddr = await this.getAccountAddress();
|
|
2892
|
+
const chainId = this.config.chainId;
|
|
2893
|
+
try {
|
|
2894
|
+
const posQuery = `{
|
|
2895
|
+
marketPositions(
|
|
2896
|
+
where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
|
|
2897
|
+
first: 50
|
|
2898
|
+
) {
|
|
2899
|
+
items {
|
|
2900
|
+
collateral
|
|
2901
|
+
market {
|
|
2902
|
+
uniqueKey
|
|
2903
|
+
oracleAddress
|
|
2904
|
+
irmAddress
|
|
2905
|
+
lltv
|
|
2906
|
+
loanAsset { address symbol decimals }
|
|
2907
|
+
collateralAsset { address symbol decimals }
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
}`;
|
|
2912
|
+
const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: posQuery }, { timeout: 1e4 });
|
|
2913
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
2914
|
+
for (const item of items) {
|
|
2915
|
+
if (BigInt(item.collateral ?? "0") > 0n && item.market?.collateralAsset) {
|
|
2916
|
+
const m = item.market;
|
|
2917
|
+
return {
|
|
2918
|
+
params: {
|
|
2919
|
+
loanToken: m.loanAsset.address,
|
|
2920
|
+
collateralToken: m.collateralAsset.address,
|
|
2921
|
+
oracle: m.oracleAddress,
|
|
2922
|
+
irm: m.irmAddress,
|
|
2923
|
+
lltv: BigInt(m.lltv)
|
|
2924
|
+
},
|
|
2925
|
+
symbol: m.collateralAsset.symbol
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
} catch (e) {
|
|
2930
|
+
console.warn("[agether] _findActiveMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
|
|
2931
|
+
}
|
|
2641
2932
|
const markets = await this.getMarkets();
|
|
2642
2933
|
for (const m of markets) {
|
|
2643
2934
|
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
@@ -2666,6 +2957,46 @@ var MorphoClient = class {
|
|
|
2666
2957
|
/** Find the first market where the agent has a supply (lending) position. */
|
|
2667
2958
|
async _findActiveSupplyMarket() {
|
|
2668
2959
|
const acctAddr = await this.getAccountAddress();
|
|
2960
|
+
const chainId = this.config.chainId;
|
|
2961
|
+
try {
|
|
2962
|
+
const posQuery = `{
|
|
2963
|
+
marketPositions(
|
|
2964
|
+
where: { userAddress_in: ["${acctAddr}"], chainId_in: [${chainId}] }
|
|
2965
|
+
first: 50
|
|
2966
|
+
) {
|
|
2967
|
+
items {
|
|
2968
|
+
supplyShares
|
|
2969
|
+
market {
|
|
2970
|
+
uniqueKey
|
|
2971
|
+
oracleAddress
|
|
2972
|
+
irmAddress
|
|
2973
|
+
lltv
|
|
2974
|
+
loanAsset { address symbol decimals }
|
|
2975
|
+
collateralAsset { address symbol decimals }
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
}`;
|
|
2980
|
+
const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: posQuery }, { timeout: 1e4 });
|
|
2981
|
+
const items = resp.data?.data?.marketPositions?.items ?? [];
|
|
2982
|
+
for (const item of items) {
|
|
2983
|
+
if (BigInt(item.supplyShares ?? "0") > 0n && item.market?.collateralAsset) {
|
|
2984
|
+
const m = item.market;
|
|
2985
|
+
return {
|
|
2986
|
+
params: {
|
|
2987
|
+
loanToken: m.loanAsset.address,
|
|
2988
|
+
collateralToken: m.collateralAsset.address,
|
|
2989
|
+
oracle: m.oracleAddress,
|
|
2990
|
+
irm: m.irmAddress,
|
|
2991
|
+
lltv: BigInt(m.lltv)
|
|
2992
|
+
},
|
|
2993
|
+
symbol: m.collateralAsset.symbol
|
|
2994
|
+
};
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
} catch (e) {
|
|
2998
|
+
console.warn("[agether] _findActiveSupplyMarket GraphQL failed, falling back:", e instanceof Error ? e.message : e);
|
|
2999
|
+
}
|
|
2669
3000
|
const markets = await this.getMarkets();
|
|
2670
3001
|
for (const m of markets) {
|
|
2671
3002
|
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
@@ -2798,7 +3129,7 @@ var MorphoClient = class {
|
|
|
2798
3129
|
}
|
|
2799
3130
|
}
|
|
2800
3131
|
}`;
|
|
2801
|
-
const resp = await
|
|
3132
|
+
const resp = await import_axios2.default.post(MORPHO_API_URL2, { query: txQuery }, { timeout: 15e3 });
|
|
2802
3133
|
const txData = resp.data?.data?.transactions;
|
|
2803
3134
|
if (!txData?.items) break;
|
|
2804
3135
|
for (const tx of txData.items) {
|
|
@@ -2823,7 +3154,7 @@ var MorphoClient = class {
|
|
|
2823
3154
|
};
|
|
2824
3155
|
|
|
2825
3156
|
// src/clients/ScoringClient.ts
|
|
2826
|
-
var
|
|
3157
|
+
var import_axios3 = __toESM(require("axios"));
|
|
2827
3158
|
|
|
2828
3159
|
// src/clients/X402Client.ts
|
|
2829
3160
|
var import_fetch = require("@x402/fetch");
|
|
@@ -3185,7 +3516,7 @@ var ScoringClient = class {
|
|
|
3185
3516
|
constructor(config) {
|
|
3186
3517
|
this.endpoint = config.endpoint;
|
|
3187
3518
|
this.defaultChainId = config.chainId;
|
|
3188
|
-
this.client =
|
|
3519
|
+
this.client = import_axios3.default.create({
|
|
3189
3520
|
baseURL: config.endpoint,
|
|
3190
3521
|
headers: { "Content-Type": "application/json" },
|
|
3191
3522
|
timeout: 3e4
|