@agether/sdk 1.4.0 → 1.5.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 +1135 -1799
- package/dist/index.d.mts +435 -840
- package/dist/index.d.ts +435 -840
- package/dist/index.js +1069 -1617
- package/dist/index.mjs +1060 -1601
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -34,24 +34,18 @@ __export(index_exports, {
|
|
|
34
34
|
AGENT_ACCOUNT_ABI: () => AGENT_ACCOUNT_ABI,
|
|
35
35
|
AGENT_REPUTATION_ABI: () => AGENT_REPUTATION_ABI,
|
|
36
36
|
AgentIdentityClient: () => AgentIdentityClient,
|
|
37
|
+
AgentNotApprovedError: () => AgentNotApprovedError,
|
|
37
38
|
AgetherClient: () => AgetherClient,
|
|
38
39
|
AgetherError: () => AgetherError,
|
|
39
|
-
CREDIT_PROVIDER_ABI: () => CREDIT_PROVIDER_ABI,
|
|
40
40
|
ChainId: () => ChainId,
|
|
41
|
-
CreditNotActiveError: () => CreditNotActiveError,
|
|
42
|
-
CreditStatus: () => CreditStatus,
|
|
43
41
|
ERC20_ABI: () => ERC20_ABI,
|
|
44
42
|
IDENTITY_REGISTRY_ABI: () => IDENTITY_REGISTRY_ABI,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
MorphoCreditClient: () => MorphoCreditClient,
|
|
49
|
-
REPUTATION_CREDIT_ABI: () => REPUTATION_CREDIT_ABI,
|
|
43
|
+
InsufficientBalanceError: () => InsufficientBalanceError,
|
|
44
|
+
MORPHO_BLUE_ABI: () => MORPHO_BLUE_ABI,
|
|
45
|
+
MorphoClient: () => MorphoClient,
|
|
50
46
|
ScoringClient: () => ScoringClient,
|
|
51
47
|
ScoringRejectedError: () => ScoringRejectedError,
|
|
52
48
|
VALIDATION_REGISTRY_ABI: () => VALIDATION_REGISTRY_ABI,
|
|
53
|
-
VaultClient: () => VaultClient,
|
|
54
|
-
WalletClient: () => WalletClient,
|
|
55
49
|
X402Client: () => X402Client,
|
|
56
50
|
bpsToRate: () => bpsToRate,
|
|
57
51
|
createConfig: () => createConfig,
|
|
@@ -63,7 +57,6 @@ __export(index_exports, {
|
|
|
63
57
|
formatUSD: () => formatUSD,
|
|
64
58
|
formatUnits: () => formatUnits,
|
|
65
59
|
getDefaultConfig: () => getDefaultConfig,
|
|
66
|
-
getUSDCAddress: () => getUSDCAddress,
|
|
67
60
|
parseUnits: () => parseUnits,
|
|
68
61
|
rateToBps: () => rateToBps
|
|
69
62
|
});
|
|
@@ -73,15 +66,6 @@ module.exports = __toCommonJS(index_exports);
|
|
|
73
66
|
var import_ethers = require("ethers");
|
|
74
67
|
|
|
75
68
|
// src/types/index.ts
|
|
76
|
-
var CreditStatus = /* @__PURE__ */ ((CreditStatus3) => {
|
|
77
|
-
CreditStatus3[CreditStatus3["None"] = 0] = "None";
|
|
78
|
-
CreditStatus3[CreditStatus3["Pending"] = 1] = "Pending";
|
|
79
|
-
CreditStatus3[CreditStatus3["Active"] = 2] = "Active";
|
|
80
|
-
CreditStatus3[CreditStatus3["Frozen"] = 3] = "Frozen";
|
|
81
|
-
CreditStatus3[CreditStatus3["Closed"] = 4] = "Closed";
|
|
82
|
-
CreditStatus3[CreditStatus3["Defaulted"] = 5] = "Defaulted";
|
|
83
|
-
return CreditStatus3;
|
|
84
|
-
})(CreditStatus || {});
|
|
85
69
|
var ChainId = /* @__PURE__ */ ((ChainId3) => {
|
|
86
70
|
ChainId3[ChainId3["Ethereum"] = 1] = "Ethereum";
|
|
87
71
|
ChainId3[ChainId3["Base"] = 8453] = "Base";
|
|
@@ -98,12 +82,12 @@ var AgetherError = class extends Error {
|
|
|
98
82
|
this.name = "AgetherError";
|
|
99
83
|
}
|
|
100
84
|
};
|
|
101
|
-
var
|
|
102
|
-
constructor(available,
|
|
85
|
+
var InsufficientBalanceError = class extends AgetherError {
|
|
86
|
+
constructor(available, required) {
|
|
103
87
|
super(
|
|
104
|
-
`Insufficient
|
|
105
|
-
"
|
|
106
|
-
{ available: available.toString(),
|
|
88
|
+
`Insufficient balance: available ${available}, required ${required}`,
|
|
89
|
+
"INSUFFICIENT_BALANCE",
|
|
90
|
+
{ available: available.toString(), required: required.toString() }
|
|
107
91
|
);
|
|
108
92
|
}
|
|
109
93
|
};
|
|
@@ -116,12 +100,12 @@ var ScoringRejectedError = class extends AgetherError {
|
|
|
116
100
|
);
|
|
117
101
|
}
|
|
118
102
|
};
|
|
119
|
-
var
|
|
120
|
-
constructor(
|
|
103
|
+
var AgentNotApprovedError = class extends AgetherError {
|
|
104
|
+
constructor(agentId) {
|
|
121
105
|
super(
|
|
122
|
-
`
|
|
123
|
-
"
|
|
124
|
-
{
|
|
106
|
+
`Agent ${agentId} is not KYA-approved. Submit code for validation first.`,
|
|
107
|
+
"AGENT_NOT_APPROVED",
|
|
108
|
+
{ agentId: agentId.toString() }
|
|
125
109
|
);
|
|
126
110
|
}
|
|
127
111
|
};
|
|
@@ -147,103 +131,51 @@ var ACCOUNT_FACTORY_ABI = [
|
|
|
147
131
|
var AGENT_ACCOUNT_ABI = [
|
|
148
132
|
"function agentId() view returns (uint256)",
|
|
149
133
|
"function owner() view returns (address)",
|
|
134
|
+
"function factory() view returns (address)",
|
|
135
|
+
"function validationRegistry() view returns (address)",
|
|
136
|
+
"function identityRegistry() view returns (address)",
|
|
150
137
|
"function balanceOf(address token) view returns (uint256)",
|
|
151
138
|
"function ethBalance() view returns (uint256)",
|
|
152
139
|
"function execute(address target, uint256 value, bytes data) payable returns (bytes)",
|
|
153
140
|
"function executeBatch(address[] targets, uint256[] values, bytes[] datas) payable returns (bytes[])",
|
|
154
|
-
"function drawCredit(address creditProvider, uint256 amount)",
|
|
155
|
-
"function repayCredit(address creditProvider, uint256 amount)",
|
|
156
141
|
"function fund(address token, uint256 amount)",
|
|
157
142
|
"function withdraw(address token, uint256 amount, address to)",
|
|
158
|
-
"function
|
|
159
|
-
|
|
160
|
-
var CREDIT_PROVIDER_ABI = [
|
|
161
|
-
"function asset() view returns (address)",
|
|
162
|
-
"function isEligible(address account) view returns (bool)",
|
|
163
|
-
"function getCreditInfo(address account) view returns (tuple(uint256 limit, uint256 used, uint256 available, uint256 accruedInterest, uint256 aprBps, bool isActive, bool requiresCollateral))",
|
|
164
|
-
"function getTotalDebt(address account) view returns (uint256)",
|
|
165
|
-
"function maxDrawable(address account) view returns (uint256)",
|
|
166
|
-
"function draw(address account, uint256 amount)",
|
|
167
|
-
"function repay(address account, uint256 amount)",
|
|
168
|
-
"event CreditDrawn(address indexed account, uint256 amount, uint256 totalUsed)",
|
|
169
|
-
"event CreditRepaid(address indexed account, uint256 amount, uint256 totalUsed)",
|
|
170
|
-
"event CreditLineOpened(address indexed account, uint256 limit, uint256 aprBps)",
|
|
171
|
-
"event CreditLineClosed(address indexed account)"
|
|
172
|
-
];
|
|
173
|
-
var REPUTATION_CREDIT_ABI = [
|
|
174
|
-
...CREDIT_PROVIDER_ABI,
|
|
175
|
-
// Application / Approval flow
|
|
176
|
-
"function applyForCredit(address account, uint256 requestedLimit)",
|
|
177
|
-
"function approveCreditLine(address account, uint256 limit, uint256 aprBps)",
|
|
178
|
-
"function rejectCreditLine(address account, string reason)",
|
|
179
|
-
"function requestLimitIncrease(address account, uint256 newRequestedLimit)",
|
|
180
|
-
"function approveLimitIncrease(address account, uint256 newLimit)",
|
|
181
|
-
"function freezeCreditLine(address account)",
|
|
182
|
-
"function unfreezeCreditLine(address account)",
|
|
183
|
-
// Admin
|
|
184
|
-
"function setCreditLine(address account, uint256 limit, uint256 aprBps)",
|
|
185
|
-
"function openScoredCreditLine(address account)",
|
|
186
|
-
"function closeCreditLine(address account)",
|
|
187
|
-
"function declareDefault(address account)",
|
|
188
|
-
// View
|
|
189
|
-
"function getCreditLineStatus(address account) view returns (uint8)",
|
|
190
|
-
"function getCreditLineDetails(address account) view returns (uint256 agentId, uint256 limit, uint256 used, uint256 aprBps, uint256 accruedInterest, uint256 requestedLimit, uint256 createdAt, uint256 lastActivityAt, uint8 status)",
|
|
191
|
-
"function getAgentAccount(uint256 agentId) view returns (address)",
|
|
192
|
-
"function previewScoredLimit(uint256 agentId) view returns (uint256 limit, uint256 score, bool eligible)",
|
|
193
|
-
"function dailyDrawLimit() view returns (uint256)",
|
|
194
|
-
"function totalBorrowed() view returns (uint256)",
|
|
195
|
-
// Events
|
|
196
|
-
"event CreditApplied(uint256 indexed creditLineId, uint256 indexed agentId, uint256 requestedLimit)",
|
|
197
|
-
"event CreditApproved(uint256 indexed creditLineId, uint256 limit, uint256 aprBps)",
|
|
198
|
-
"event CreditRejected(uint256 indexed creditLineId, string reason)",
|
|
199
|
-
"event CreditFrozen(uint256 indexed creditLineId)",
|
|
200
|
-
"event CreditUnfrozen(uint256 indexed creditLineId)",
|
|
201
|
-
"event DefaultDeclared(address indexed account, uint256 amount)"
|
|
202
|
-
];
|
|
203
|
-
var LP_VAULT_ABI = [
|
|
204
|
-
"function asset() view returns (address)",
|
|
205
|
-
"function totalAssets() view returns (uint256)",
|
|
206
|
-
"function totalSupply() view returns (uint256)",
|
|
207
|
-
"function balanceOf(address account) view returns (uint256)",
|
|
208
|
-
"function convertToShares(uint256 assets) view returns (uint256)",
|
|
209
|
-
"function convertToAssets(uint256 shares) view returns (uint256)",
|
|
210
|
-
"function previewDeposit(uint256 assets) view returns (uint256)",
|
|
211
|
-
"function previewRedeem(uint256 shares) view returns (uint256)",
|
|
212
|
-
"function deposit(uint256 assets, address receiver) returns (uint256)",
|
|
213
|
-
"function withdraw(uint256 assets, address receiver, address owner) returns (uint256)",
|
|
214
|
-
"function redeem(uint256 shares, address receiver, address owner) returns (uint256)",
|
|
215
|
-
"function totalBorrowed() view returns (uint256)",
|
|
216
|
-
"function availableLiquidity() view returns (uint256)",
|
|
217
|
-
"event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares)",
|
|
218
|
-
"event Withdraw(address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares)"
|
|
143
|
+
"function withdrawETH(uint256 amount, address to)",
|
|
144
|
+
"function isValidSignature(bytes32 hash, bytes signature) view returns (bytes4)"
|
|
219
145
|
];
|
|
220
146
|
var AGENT_REPUTATION_ABI = [
|
|
221
|
-
"function getReputation(uint256 agentId) view returns (tuple(uint256 totalBorrowed, uint256 totalRepaid, uint256 totalInterestPaid, uint256 onTimePayments, uint256 latePayments, uint256 missedPayments, uint256 peakUtilization, uint256 avgUtilization, uint256 utilizationSamples, uint256 firstActivityAt, uint256 lastActivityAt, uint256 lastScoreUpdate, uint256 creditScore, uint256 creditLinesOpened, uint256 lastCreditOpenedAt, uint256 currentOutstanding, uint256 currentLimit, uint256 totalDeposits, uint256 totalWithdrawals))",
|
|
222
147
|
"function getCreditScore(uint256 agentId) view returns (uint256)",
|
|
223
|
-
"function
|
|
224
|
-
"function
|
|
148
|
+
"function getAttestation(uint256 agentId) view returns (tuple(uint256 score, uint256 timestamp, address signer))",
|
|
149
|
+
"function isScoreFresh(uint256 agentId) view returns (bool fresh, uint256 age)",
|
|
225
150
|
"function isEligible(uint256 agentId, uint256 minScore) view returns (bool eligible, uint256 currentScore)",
|
|
226
|
-
"function
|
|
227
|
-
"function
|
|
228
|
-
"function
|
|
229
|
-
"
|
|
230
|
-
"function decayPeriod() view returns (uint256)"
|
|
151
|
+
"function oracleSigner() view returns (address)",
|
|
152
|
+
"function submitScore(uint256 agentId, uint256 score_, uint256 timestamp_, bytes signature)",
|
|
153
|
+
"function setOracleSigner(address signer_)",
|
|
154
|
+
"event ScoreUpdated(uint256 indexed agentId, uint256 score, uint256 timestamp, address signer)"
|
|
231
155
|
];
|
|
232
156
|
var VALIDATION_REGISTRY_ABI = [
|
|
233
157
|
"function isAgentCodeApproved(uint256 agentId) view returns (bool)",
|
|
234
158
|
"function isAgentCodeApprovedForTag(uint256 agentId, string tag) view returns (bool)",
|
|
235
|
-
"function getAgentValidations(uint256 agentId) view returns (bytes32[])"
|
|
159
|
+
"function getAgentValidations(uint256 agentId) view returns (bytes32[])",
|
|
160
|
+
"function getValidation(bytes32 requestHash) view returns (tuple(address validatorAddress, uint256 agentId, string requestURI, uint8 response, string responseURI, bytes32 responseHash, string tag, uint256 requestedAt, uint256 respondedAt, bool hasResponse))"
|
|
236
161
|
];
|
|
237
|
-
var
|
|
238
|
-
|
|
239
|
-
"function
|
|
240
|
-
"function
|
|
241
|
-
|
|
242
|
-
"function
|
|
243
|
-
"function
|
|
244
|
-
|
|
245
|
-
"function
|
|
246
|
-
"function
|
|
162
|
+
var MORPHO_BLUE_ABI = [
|
|
163
|
+
// Supply & Withdraw (lending side)
|
|
164
|
+
"function supply(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) returns (uint256 assetsSupplied, uint256 sharesSupplied)",
|
|
165
|
+
"function withdraw(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn)",
|
|
166
|
+
// Collateral
|
|
167
|
+
"function supplyCollateral(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, bytes data)",
|
|
168
|
+
"function withdrawCollateral(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, address onBehalf, address receiver)",
|
|
169
|
+
// Borrow & Repay
|
|
170
|
+
"function borrow(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, address receiver) returns (uint256 assetsBorrowed, uint256 sharesBorrowed)",
|
|
171
|
+
"function repay(tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv) marketParams, uint256 assets, uint256 shares, address onBehalf, bytes data) returns (uint256 assetsRepaid, uint256 sharesRepaid)",
|
|
172
|
+
// Authorization
|
|
173
|
+
"function setAuthorization(address authorized, bool newIsAuthorized)",
|
|
174
|
+
"function isAuthorized(address authorizer, address authorized) view returns (bool)",
|
|
175
|
+
// Views
|
|
176
|
+
"function position(bytes32 id, address user) view returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral)",
|
|
177
|
+
"function market(bytes32 id) view returns (uint128 totalSupplyAssets, uint128 totalSupplyShares, uint128 totalBorrowAssets, uint128 totalBorrowShares, uint128 lastUpdate, uint128 fee)",
|
|
178
|
+
"function idToMarketParams(bytes32 id) view returns (tuple(address loanToken, address collateralToken, address oracle, address irm, uint256 lltv))"
|
|
247
179
|
];
|
|
248
180
|
var ERC20_ABI = [
|
|
249
181
|
"function balanceOf(address account) view returns (uint256)",
|
|
@@ -252,57 +184,51 @@ var ERC20_ABI = [
|
|
|
252
184
|
"function transfer(address to, uint256 amount) returns (bool)",
|
|
253
185
|
"function transferFrom(address from, address to, uint256 amount) returns (bool)",
|
|
254
186
|
"function decimals() view returns (uint8)",
|
|
255
|
-
"function symbol() view returns (string)"
|
|
187
|
+
"function symbol() view returns (string)",
|
|
188
|
+
"function name() view returns (string)"
|
|
256
189
|
];
|
|
257
190
|
|
|
258
191
|
// src/utils/config.ts
|
|
259
192
|
var CONTRACT_ADDRESSES = {
|
|
260
193
|
[1 /* Ethereum */]: {
|
|
261
194
|
accountFactory: "0x0000000000000000000000000000000000000000",
|
|
262
|
-
reputationCredit: "0x0000000000000000000000000000000000000000",
|
|
263
|
-
lpVault: "0x0000000000000000000000000000000000000000",
|
|
264
195
|
validationRegistry: "0x0000000000000000000000000000000000000000",
|
|
196
|
+
agentReputation: "0x0000000000000000000000000000000000000000",
|
|
265
197
|
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
266
198
|
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
|
|
267
|
-
|
|
199
|
+
morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
|
|
268
200
|
},
|
|
269
201
|
[8453 /* Base */]: {
|
|
270
202
|
accountFactory: "0xeB72f248Ad9F4bf4024e8D9da75cf7AAD37B58f5",
|
|
271
|
-
reputationCredit: "0x57B2B4ef3e7B8BE5FC86c6369602125d240F552A",
|
|
272
|
-
lpVault: "0x612A80D6c3175F8283e9C7EE71d5177fE9acc338",
|
|
273
203
|
validationRegistry: "0x8842f2383A86134Dd80c3Ecf6Bbae2e38396A5ec",
|
|
204
|
+
agentReputation: "0xF1bed094D4E33E47CC8C72E086FFFde09e2211b4",
|
|
274
205
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
275
206
|
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
|
|
276
|
-
|
|
277
|
-
morphoCredit: "0x7dFfa40E17471F7f26F5662D0F07a31977F47BeB"
|
|
207
|
+
morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
|
|
278
208
|
},
|
|
279
209
|
[84532 /* BaseSepolia */]: {
|
|
280
210
|
accountFactory: "0x0000000000000000000000000000000000000000",
|
|
281
|
-
reputationCredit: "0x0000000000000000000000000000000000000000",
|
|
282
|
-
lpVault: "0x0000000000000000000000000000000000000000",
|
|
283
211
|
validationRegistry: "0x0000000000000000000000000000000000000000",
|
|
212
|
+
agentReputation: "0x0000000000000000000000000000000000000000",
|
|
284
213
|
usdc: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
|
|
285
214
|
identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
|
|
286
|
-
|
|
215
|
+
morphoBlue: "0x0000000000000000000000000000000000000000"
|
|
287
216
|
},
|
|
288
217
|
[11155111 /* Sepolia */]: {
|
|
289
218
|
accountFactory: "0x0000000000000000000000000000000000000000",
|
|
290
|
-
reputationCredit: "0x0000000000000000000000000000000000000000",
|
|
291
|
-
lpVault: "0x0000000000000000000000000000000000000000",
|
|
292
219
|
validationRegistry: "0x0000000000000000000000000000000000000000",
|
|
220
|
+
agentReputation: "0x0000000000000000000000000000000000000000",
|
|
293
221
|
usdc: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
|
|
294
222
|
identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
|
|
295
|
-
|
|
223
|
+
morphoBlue: "0x0000000000000000000000000000000000000000"
|
|
296
224
|
},
|
|
297
225
|
[31337 /* Hardhat */]: {
|
|
298
226
|
accountFactory: "0x0000000000000000000000000000000000000000",
|
|
299
|
-
reputationCredit: "0x0000000000000000000000000000000000000000",
|
|
300
|
-
lpVault: "0x0000000000000000000000000000000000000000",
|
|
301
227
|
validationRegistry: "0x0000000000000000000000000000000000000000",
|
|
228
|
+
agentReputation: "0x0000000000000000000000000000000000000000",
|
|
302
229
|
usdc: "0x56d4d6aEe0278c5Df2FA23Ecb32eC146C9446FDf",
|
|
303
230
|
identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
|
|
304
|
-
|
|
305
|
-
morphoCredit: "0x0000000000000000000000000000000000000000"
|
|
231
|
+
morphoBlue: "0x0000000000000000000000000000000000000000"
|
|
306
232
|
}
|
|
307
233
|
};
|
|
308
234
|
var RPC_URLS = {
|
|
@@ -338,61 +264,44 @@ function createConfig(chainId, options) {
|
|
|
338
264
|
}
|
|
339
265
|
};
|
|
340
266
|
}
|
|
341
|
-
function getUSDCAddress(chainId) {
|
|
342
|
-
return CONTRACT_ADDRESSES[chainId].usdc;
|
|
343
|
-
}
|
|
344
267
|
|
|
345
268
|
// src/clients/AgetherClient.ts
|
|
346
269
|
var AgetherClient = class _AgetherClient {
|
|
347
|
-
/**
|
|
348
|
-
* Undercollateralized credit is disabled for now.
|
|
349
|
-
* All credit operations go through MorphoCredit (overcollateralized).
|
|
350
|
-
*/
|
|
351
|
-
_requireUndercollateralizedEnabled() {
|
|
352
|
-
throw new AgetherError(
|
|
353
|
-
"Undercollateralized credit is not available yet. Use MorphoCredit for overcollateralized lending via the CLI: agether morpho-deposit / morpho-draw / morpho-repay",
|
|
354
|
-
"UNDERCOLLATERALIZED_NOT_AVAILABLE"
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
270
|
constructor(options) {
|
|
358
271
|
this.config = options.config;
|
|
359
272
|
this.signer = options.signer;
|
|
360
273
|
this.agentId = options.agentId;
|
|
361
274
|
const provider = options.signer.provider;
|
|
362
|
-
if (!provider)
|
|
363
|
-
throw new AgetherError("Signer must have a provider", "NO_PROVIDER");
|
|
364
|
-
}
|
|
275
|
+
if (!provider) throw new AgetherError("Signer must have a provider", "NO_PROVIDER");
|
|
365
276
|
this.accountFactory = new import_ethers.Contract(
|
|
366
277
|
options.config.contracts.accountFactory,
|
|
367
278
|
ACCOUNT_FACTORY_ABI,
|
|
368
279
|
options.signer
|
|
369
280
|
);
|
|
370
|
-
this.
|
|
371
|
-
options.config.contracts.
|
|
372
|
-
|
|
373
|
-
|
|
281
|
+
this.identityRegistry = new import_ethers.Contract(
|
|
282
|
+
options.config.contracts.identityRegistry,
|
|
283
|
+
IDENTITY_REGISTRY_ABI,
|
|
284
|
+
provider
|
|
285
|
+
);
|
|
286
|
+
this.validationRegistry = new import_ethers.Contract(
|
|
287
|
+
options.config.contracts.validationRegistry,
|
|
288
|
+
VALIDATION_REGISTRY_ABI,
|
|
289
|
+
provider
|
|
290
|
+
);
|
|
291
|
+
this.agentReputation = new import_ethers.Contract(
|
|
292
|
+
options.config.contracts.agentReputation,
|
|
293
|
+
AGENT_REPUTATION_ABI,
|
|
294
|
+
provider
|
|
374
295
|
);
|
|
375
296
|
}
|
|
376
|
-
//
|
|
377
|
-
/**
|
|
378
|
-
* Create client from private key.
|
|
379
|
-
*
|
|
380
|
-
* Simplest usage — only needs key, agentId, and chainId:
|
|
381
|
-
* AgetherClient.fromPrivateKey(key, 42n, ChainId.Sepolia)
|
|
382
|
-
*
|
|
383
|
-
* All contract addresses and RPC URLs are resolved automatically.
|
|
384
|
-
*/
|
|
297
|
+
// Static Factory
|
|
385
298
|
static fromPrivateKey(privateKey, agentId, chainIdOrConfig) {
|
|
386
299
|
const config = typeof chainIdOrConfig === "number" ? getDefaultConfig(chainIdOrConfig) : chainIdOrConfig;
|
|
387
300
|
const provider = new import_ethers.ethers.JsonRpcProvider(config.rpcUrl);
|
|
388
301
|
const signer = new import_ethers.ethers.Wallet(privateKey, provider);
|
|
389
302
|
return new _AgetherClient({ config, signer, agentId });
|
|
390
303
|
}
|
|
391
|
-
//
|
|
392
|
-
/**
|
|
393
|
-
* Create an AgentAccount (smart wallet) for this agent.
|
|
394
|
-
* Returns the account address.
|
|
395
|
-
*/
|
|
304
|
+
// Account Management
|
|
396
305
|
async createAccount() {
|
|
397
306
|
const tx = await this.accountFactory.createAccount(this.agentId);
|
|
398
307
|
const receipt = await tx.wait();
|
|
@@ -410,138 +319,113 @@ var AgetherClient = class _AgetherClient {
|
|
|
410
319
|
}
|
|
411
320
|
return this.accountAddress;
|
|
412
321
|
}
|
|
413
|
-
/**
|
|
414
|
-
* Get the AgentAccount address for this agent.
|
|
415
|
-
*/
|
|
416
322
|
async getAccountAddress() {
|
|
417
323
|
if (this.accountAddress) return this.accountAddress;
|
|
418
324
|
const addr = await this.accountFactory.getAccount(this.agentId);
|
|
419
325
|
if (addr === import_ethers.ethers.ZeroAddress) {
|
|
420
|
-
throw new AgetherError(
|
|
421
|
-
"No account found. Create one first with createAccount().",
|
|
422
|
-
"NO_ACCOUNT"
|
|
423
|
-
);
|
|
326
|
+
throw new AgetherError("No account found. Create one with createAccount().", "NO_ACCOUNT");
|
|
424
327
|
}
|
|
425
328
|
this.accountAddress = addr;
|
|
426
329
|
return addr;
|
|
427
330
|
}
|
|
428
|
-
/**
|
|
429
|
-
* Check if an account already exists.
|
|
430
|
-
*/
|
|
431
331
|
async accountExists() {
|
|
432
332
|
return this.accountFactory.accountExists(this.agentId);
|
|
433
333
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
* Apply for a credit line.
|
|
437
|
-
* @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
|
|
438
|
-
*/
|
|
439
|
-
async apply(_limitOrApplication) {
|
|
440
|
-
this._requireUndercollateralizedEnabled();
|
|
334
|
+
async predictAddress() {
|
|
335
|
+
return this.accountFactory.predictAddress(this.agentId);
|
|
441
336
|
}
|
|
442
|
-
//
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const
|
|
448
|
-
const
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
limit: data[1],
|
|
452
|
-
used: data[2],
|
|
453
|
-
aprBps: data[3],
|
|
454
|
-
accruedInterest: data[4],
|
|
455
|
-
requestedLimit: data[5],
|
|
456
|
-
createdAt: data[6],
|
|
457
|
-
lastActivityAt: data[7],
|
|
458
|
-
status: Number(data[8])
|
|
337
|
+
// Balances
|
|
338
|
+
async getBalances() {
|
|
339
|
+
const provider = this.signer.provider;
|
|
340
|
+
const eoaAddr = await this.signer.getAddress();
|
|
341
|
+
const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, provider);
|
|
342
|
+
const ethBal = await provider.getBalance(eoaAddr);
|
|
343
|
+
const usdcBal = await usdc.balanceOf(eoaAddr);
|
|
344
|
+
const result = {
|
|
345
|
+
eoa: { eth: import_ethers.ethers.formatEther(ethBal), usdc: import_ethers.ethers.formatUnits(usdcBal, 6) }
|
|
459
346
|
};
|
|
347
|
+
try {
|
|
348
|
+
const acctAddr = await this.getAccountAddress();
|
|
349
|
+
const acctEth = await provider.getBalance(acctAddr);
|
|
350
|
+
const acctUsdc = await usdc.balanceOf(acctAddr);
|
|
351
|
+
result.account = {
|
|
352
|
+
address: acctAddr,
|
|
353
|
+
eth: import_ethers.ethers.formatEther(acctEth),
|
|
354
|
+
usdc: import_ethers.ethers.formatUnits(acctUsdc, 6)
|
|
355
|
+
};
|
|
356
|
+
} catch {
|
|
357
|
+
}
|
|
358
|
+
return result;
|
|
460
359
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
const
|
|
466
|
-
const
|
|
360
|
+
async fundAccount(usdcAmount) {
|
|
361
|
+
const acctAddr = await this.getAccountAddress();
|
|
362
|
+
const usdc = new import_ethers.Contract(this.config.contracts.usdc, ERC20_ABI, this.signer);
|
|
363
|
+
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
364
|
+
const tx = await usdc.transfer(acctAddr, amount);
|
|
365
|
+
const receipt = await tx.wait();
|
|
467
366
|
return {
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
aprBps: info.aprBps,
|
|
473
|
-
isActive: info.isActive,
|
|
474
|
-
requiresCollateral: info.requiresCollateral
|
|
367
|
+
txHash: receipt.hash,
|
|
368
|
+
blockNumber: receipt.blockNumber,
|
|
369
|
+
status: receipt.status === 1 ? "success" : "failed",
|
|
370
|
+
gasUsed: receipt.gasUsed
|
|
475
371
|
};
|
|
476
372
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
return this.reputationCredit.maxDrawable(account);
|
|
373
|
+
async withdrawUsdc(usdcAmount) {
|
|
374
|
+
const acctAddr = await this.getAccountAddress();
|
|
375
|
+
const account = new import_ethers.Contract(acctAddr, AGENT_ACCOUNT_ABI, this.signer);
|
|
376
|
+
const amount = import_ethers.ethers.parseUnits(usdcAmount, 6);
|
|
377
|
+
const eoaAddr = await this.signer.getAddress();
|
|
378
|
+
const tx = await account.withdraw(this.config.contracts.usdc, amount, eoaAddr);
|
|
379
|
+
const receipt = await tx.wait();
|
|
380
|
+
return {
|
|
381
|
+
txHash: receipt.hash,
|
|
382
|
+
blockNumber: receipt.blockNumber,
|
|
383
|
+
status: receipt.status === 1 ? "success" : "failed",
|
|
384
|
+
gasUsed: receipt.gasUsed
|
|
385
|
+
};
|
|
491
386
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
const
|
|
497
|
-
|
|
387
|
+
async withdrawEth(ethAmount) {
|
|
388
|
+
const acctAddr = await this.getAccountAddress();
|
|
389
|
+
const account = new import_ethers.Contract(acctAddr, AGENT_ACCOUNT_ABI, this.signer);
|
|
390
|
+
const amount = import_ethers.ethers.parseEther(ethAmount);
|
|
391
|
+
const eoaAddr = await this.signer.getAddress();
|
|
392
|
+
const tx = await account.withdrawETH(amount, eoaAddr);
|
|
393
|
+
const receipt = await tx.wait();
|
|
394
|
+
return {
|
|
395
|
+
txHash: receipt.hash,
|
|
396
|
+
blockNumber: receipt.blockNumber,
|
|
397
|
+
status: receipt.status === 1 ? "success" : "failed",
|
|
398
|
+
gasUsed: receipt.gasUsed
|
|
399
|
+
};
|
|
498
400
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
async isEligible() {
|
|
503
|
-
const account = await this.getAccountAddress();
|
|
504
|
-
return this.reputationCredit.isEligible(account);
|
|
401
|
+
// Identity & Validation
|
|
402
|
+
async isKyaApproved() {
|
|
403
|
+
return this.validationRegistry.isAgentCodeApproved(this.agentId);
|
|
505
404
|
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
405
|
+
async identityExists() {
|
|
406
|
+
try {
|
|
407
|
+
await this.identityRegistry.ownerOf(this.agentId);
|
|
408
|
+
return true;
|
|
409
|
+
} catch {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
513
412
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
* @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
|
|
517
|
-
*/
|
|
518
|
-
async repay(_amountOrRequest) {
|
|
519
|
-
this._requireUndercollateralizedEnabled();
|
|
413
|
+
async getIdentityOwner() {
|
|
414
|
+
return this.identityRegistry.ownerOf(this.agentId);
|
|
520
415
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
*/
|
|
525
|
-
async repayFull() {
|
|
526
|
-
this._requireUndercollateralizedEnabled();
|
|
416
|
+
// Reputation
|
|
417
|
+
async getCreditScore() {
|
|
418
|
+
return this.agentReputation.getCreditScore(this.agentId);
|
|
527
419
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
* @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
|
|
532
|
-
*/
|
|
533
|
-
async requestLimitIncrease(_newLimit) {
|
|
534
|
-
this._requireUndercollateralizedEnabled();
|
|
420
|
+
async isScoreFresh() {
|
|
421
|
+
const [fresh, age] = await this.agentReputation.isScoreFresh(this.agentId);
|
|
422
|
+
return { fresh, age };
|
|
535
423
|
}
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
* @deprecated Undercollateralized credit is not available yet. Use MorphoCredit.
|
|
540
|
-
*/
|
|
541
|
-
async pay(_service, _amount, _asset = "USDC") {
|
|
542
|
-
this._requireUndercollateralizedEnabled();
|
|
424
|
+
async isEligible(minScore = 500n) {
|
|
425
|
+
const [eligible, currentScore] = await this.agentReputation.isEligible(this.agentId, minScore);
|
|
426
|
+
return { eligible, currentScore };
|
|
543
427
|
}
|
|
544
|
-
//
|
|
428
|
+
// Getters
|
|
545
429
|
get chainId() {
|
|
546
430
|
return this.config.chainId;
|
|
547
431
|
}
|
|
@@ -551,643 +435,640 @@ var AgetherClient = class _AgetherClient {
|
|
|
551
435
|
get currentAccountAddress() {
|
|
552
436
|
return this.accountAddress;
|
|
553
437
|
}
|
|
554
|
-
/** Get the underlying signer */
|
|
555
438
|
getSigner() {
|
|
556
439
|
return this.signer;
|
|
557
440
|
}
|
|
441
|
+
getAgentId() {
|
|
442
|
+
return this.agentId;
|
|
443
|
+
}
|
|
558
444
|
};
|
|
559
445
|
|
|
560
|
-
// src/clients/
|
|
446
|
+
// src/clients/MorphoClient.ts
|
|
447
|
+
var import_ethers2 = require("ethers");
|
|
561
448
|
var import_axios = __toESM(require("axios"));
|
|
562
|
-
var
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
449
|
+
var BASE_COLLATERALS = {
|
|
450
|
+
WETH: { address: "0x4200000000000000000000000000000000000006", symbol: "WETH", decimals: 18 },
|
|
451
|
+
wstETH: { address: "0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452", symbol: "wstETH", decimals: 18 },
|
|
452
|
+
cbETH: { address: "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22", symbol: "cbETH", decimals: 18 }
|
|
453
|
+
};
|
|
454
|
+
var MORPHO_API_URL = "https://api.morpho.org/graphql";
|
|
455
|
+
var morphoIface = new import_ethers2.ethers.Interface(MORPHO_BLUE_ABI);
|
|
456
|
+
var erc20Iface = new import_ethers2.ethers.Interface(ERC20_ABI);
|
|
457
|
+
var MorphoClient = class {
|
|
458
|
+
constructor(config) {
|
|
459
|
+
this._marketCache = /* @__PURE__ */ new Map();
|
|
460
|
+
this._discoveredAt = 0;
|
|
461
|
+
const chainId = config.chainId ?? 8453 /* Base */;
|
|
462
|
+
const defaultCfg = getDefaultConfig(chainId);
|
|
463
|
+
this.config = defaultCfg;
|
|
464
|
+
this.agentId = config.agentId;
|
|
465
|
+
this.provider = new import_ethers2.ethers.JsonRpcProvider(config.rpcUrl || defaultCfg.rpcUrl);
|
|
466
|
+
this.wallet = new import_ethers2.ethers.Wallet(config.privateKey, this.provider);
|
|
467
|
+
const addrs = { ...defaultCfg.contracts, ...config.contracts };
|
|
468
|
+
this.accountFactory = new import_ethers2.Contract(addrs.accountFactory, ACCOUNT_FACTORY_ABI, this.wallet);
|
|
469
|
+
this.morphoBlue = new import_ethers2.Contract(addrs.morphoBlue, MORPHO_BLUE_ABI, this.provider);
|
|
470
|
+
this.agentReputation = new import_ethers2.Contract(addrs.agentReputation, AGENT_REPUTATION_ABI, this.provider);
|
|
471
|
+
this.identityRegistry = new import_ethers2.Contract(addrs.identityRegistry, IDENTITY_REGISTRY_ABI, this.provider);
|
|
472
|
+
}
|
|
473
|
+
// ════════════════════════════════════════════════════════
|
|
474
|
+
// Account Management
|
|
475
|
+
// ════════════════════════════════════════════════════════
|
|
476
|
+
/** Resolve the AgentAccount address (cached). */
|
|
477
|
+
async getAccountAddress() {
|
|
478
|
+
if (this._accountAddress) return this._accountAddress;
|
|
479
|
+
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
480
|
+
const addr = await this.accountFactory.getAccount(BigInt(this.agentId));
|
|
481
|
+
if (addr === import_ethers2.ethers.ZeroAddress) {
|
|
482
|
+
throw new AgetherError("No AgentAccount found. Call register() first.", "NO_ACCOUNT");
|
|
483
|
+
}
|
|
484
|
+
this._accountAddress = addr;
|
|
485
|
+
return addr;
|
|
486
|
+
}
|
|
487
|
+
getAgentId() {
|
|
488
|
+
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
489
|
+
return this.agentId;
|
|
490
|
+
}
|
|
491
|
+
getWalletAddress() {
|
|
492
|
+
return this.wallet.address;
|
|
572
493
|
}
|
|
573
494
|
/**
|
|
574
|
-
*
|
|
575
|
-
*
|
|
495
|
+
* Register: create ERC-8004 identity + AgentAccount in one flow.
|
|
496
|
+
* If already registered, returns existing state.
|
|
576
497
|
*/
|
|
577
|
-
async
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
if (!data.approved) {
|
|
586
|
-
throw new ScoringRejectedError(data.riskScore, data.reason);
|
|
587
|
-
}
|
|
588
|
-
return {
|
|
589
|
-
approved: data.approved,
|
|
590
|
-
limit: BigInt(data.limit),
|
|
591
|
-
aprBps: data.aprBps,
|
|
592
|
-
riskScore: data.riskScore,
|
|
593
|
-
bayesianScore: data.bayesianScore ?? 0,
|
|
594
|
-
confidence: data.confidence ?? 0,
|
|
595
|
-
reason: data.reason
|
|
596
|
-
};
|
|
597
|
-
} catch (error) {
|
|
598
|
-
if (error instanceof ScoringRejectedError) {
|
|
599
|
-
throw error;
|
|
600
|
-
}
|
|
601
|
-
if (import_axios.default.isAxiosError(error)) {
|
|
602
|
-
throw new AgetherError(
|
|
603
|
-
`Scoring request failed: ${error.message}`,
|
|
604
|
-
"SCORING_REQUEST_FAILED",
|
|
605
|
-
{ status: error.response?.status }
|
|
606
|
-
);
|
|
498
|
+
async register(_name) {
|
|
499
|
+
const eoaAddr = this.wallet.address;
|
|
500
|
+
if (this.agentId) {
|
|
501
|
+
const exists = await this.accountFactory.accountExists(BigInt(this.agentId));
|
|
502
|
+
if (exists) {
|
|
503
|
+
const acct = await this.accountFactory.getAccount(BigInt(this.agentId));
|
|
504
|
+
this._accountAddress = acct;
|
|
505
|
+
return { agentId: this.agentId, address: eoaAddr, agentAccount: acct, alreadyRegistered: true };
|
|
607
506
|
}
|
|
608
|
-
throw error;
|
|
609
507
|
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
508
|
+
const balance = await this.identityRegistry.balanceOf(eoaAddr);
|
|
509
|
+
let agentId;
|
|
510
|
+
if (balance > 0n && this.agentId) {
|
|
511
|
+
agentId = BigInt(this.agentId);
|
|
512
|
+
} else if (balance > 0n) {
|
|
513
|
+
throw new AgetherError(
|
|
514
|
+
"Wallet already has an ERC-8004 identity but agentId is unknown. Pass agentId in config.",
|
|
515
|
+
"AGENT_ID_UNKNOWN"
|
|
516
|
+
);
|
|
517
|
+
} else {
|
|
518
|
+
const regTx = await this.identityRegistry.register();
|
|
519
|
+
const regReceipt = await regTx.wait();
|
|
520
|
+
agentId = 0n;
|
|
521
|
+
for (const log of regReceipt.logs) {
|
|
522
|
+
try {
|
|
523
|
+
const parsed = this.identityRegistry.interface.parseLog({ topics: log.topics, data: log.data });
|
|
524
|
+
if (parsed?.name === "Transfer") {
|
|
525
|
+
agentId = parsed.args[2];
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
} catch {
|
|
529
|
+
continue;
|
|
630
530
|
}
|
|
631
|
-
throw new AgetherError(
|
|
632
|
-
`Process application failed: ${error.message}`,
|
|
633
|
-
"PROCESS_APPLICATION_FAILED",
|
|
634
|
-
{ status: error.response?.status }
|
|
635
|
-
);
|
|
636
531
|
}
|
|
637
|
-
throw
|
|
532
|
+
if (agentId === 0n) throw new AgetherError("Failed to parse agentId from registration", "PARSE_ERROR");
|
|
638
533
|
}
|
|
534
|
+
this.agentId = agentId.toString();
|
|
535
|
+
const acctExists = await this.accountFactory.accountExists(agentId);
|
|
536
|
+
let txHash;
|
|
537
|
+
if (!acctExists) {
|
|
538
|
+
const tx = await this.accountFactory.createAccount(agentId);
|
|
539
|
+
const receipt = await tx.wait();
|
|
540
|
+
txHash = receipt.hash;
|
|
541
|
+
}
|
|
542
|
+
const acctAddr = await this.accountFactory.getAccount(agentId);
|
|
543
|
+
this._accountAddress = acctAddr;
|
|
544
|
+
return {
|
|
545
|
+
agentId: this.agentId,
|
|
546
|
+
address: eoaAddr,
|
|
547
|
+
agentAccount: acctAddr,
|
|
548
|
+
alreadyRegistered: acctExists,
|
|
549
|
+
tx: txHash
|
|
550
|
+
};
|
|
639
551
|
}
|
|
640
|
-
/**
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
const
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
async getAgentCredit(agentId) {
|
|
665
|
-
const response = await this.client.get(
|
|
666
|
-
`/credit/agent/${agentId.toString()}`
|
|
667
|
-
);
|
|
668
|
-
return response.data;
|
|
552
|
+
/** Get ETH / USDC balances for EOA and AgentAccount. */
|
|
553
|
+
async getBalances() {
|
|
554
|
+
const eoaAddr = this.wallet.address;
|
|
555
|
+
const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, this.provider);
|
|
556
|
+
const ethBal = await this.provider.getBalance(eoaAddr);
|
|
557
|
+
const usdcBal = await usdc.balanceOf(eoaAddr);
|
|
558
|
+
const result = {
|
|
559
|
+
agentId: this.agentId || "?",
|
|
560
|
+
address: eoaAddr,
|
|
561
|
+
eth: import_ethers2.ethers.formatEther(ethBal),
|
|
562
|
+
usdc: import_ethers2.ethers.formatUnits(usdcBal, 6)
|
|
563
|
+
};
|
|
564
|
+
try {
|
|
565
|
+
const acctAddr = await this.getAccountAddress();
|
|
566
|
+
const acctEth = await this.provider.getBalance(acctAddr);
|
|
567
|
+
const acctUsdc = await usdc.balanceOf(acctAddr);
|
|
568
|
+
result.agentAccount = {
|
|
569
|
+
address: acctAddr,
|
|
570
|
+
eth: import_ethers2.ethers.formatEther(acctEth),
|
|
571
|
+
usdc: import_ethers2.ethers.formatUnits(acctUsdc, 6)
|
|
572
|
+
};
|
|
573
|
+
} catch {
|
|
574
|
+
}
|
|
575
|
+
return result;
|
|
669
576
|
}
|
|
670
|
-
/**
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
const
|
|
676
|
-
|
|
577
|
+
/** Transfer USDC from EOA to AgentAccount. */
|
|
578
|
+
async fundAccount(usdcAmount) {
|
|
579
|
+
const acctAddr = await this.getAccountAddress();
|
|
580
|
+
const usdc = new import_ethers2.Contract(this.config.contracts.usdc, ERC20_ABI, this.wallet);
|
|
581
|
+
const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
|
|
582
|
+
const tx = await usdc.transfer(acctAddr, amount);
|
|
583
|
+
const receipt = await tx.wait();
|
|
584
|
+
return { tx: receipt.hash, amount: usdcAmount, agentAccount: acctAddr };
|
|
677
585
|
}
|
|
586
|
+
// ════════════════════════════════════════════════════════
|
|
587
|
+
// Market Discovery (Morpho GraphQL API)
|
|
588
|
+
// ════════════════════════════════════════════════════════
|
|
678
589
|
/**
|
|
679
|
-
*
|
|
680
|
-
*
|
|
590
|
+
* Fetch USDC borrow markets on Base from Morpho API.
|
|
591
|
+
* Caches results for 5 minutes.
|
|
681
592
|
*/
|
|
682
|
-
async
|
|
683
|
-
|
|
684
|
-
|
|
593
|
+
async getMarkets(forceRefresh = false) {
|
|
594
|
+
if (!forceRefresh && this._discoveredMarkets && Date.now() - this._discoveredAt < 3e5) {
|
|
595
|
+
return this._discoveredMarkets;
|
|
596
|
+
}
|
|
597
|
+
const chainId = this.config.chainId;
|
|
598
|
+
const usdcAddr = this.config.contracts.usdc.toLowerCase();
|
|
599
|
+
const query = `{
|
|
600
|
+
markets(
|
|
601
|
+
first: 50
|
|
602
|
+
orderBy: SupplyAssetsUsd
|
|
603
|
+
orderDirection: Desc
|
|
604
|
+
where: { chainId_in: [${chainId}], loanAssetAddress_in: ["${usdcAddr}"] }
|
|
605
|
+
) {
|
|
606
|
+
items {
|
|
607
|
+
uniqueKey
|
|
608
|
+
lltv
|
|
609
|
+
oracleAddress
|
|
610
|
+
irmAddress
|
|
611
|
+
loanAsset { address symbol decimals }
|
|
612
|
+
collateralAsset { address symbol decimals }
|
|
613
|
+
state {
|
|
614
|
+
borrowAssets
|
|
615
|
+
supplyAssets
|
|
616
|
+
utilization
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}`;
|
|
621
|
+
try {
|
|
622
|
+
const resp = await import_axios.default.post(MORPHO_API_URL, { query }, { timeout: 1e4 });
|
|
623
|
+
const items = resp.data?.data?.markets?.items ?? [];
|
|
624
|
+
this._discoveredMarkets = items.map((m) => ({
|
|
625
|
+
uniqueKey: m.uniqueKey,
|
|
626
|
+
loanAsset: m.loanAsset,
|
|
627
|
+
collateralAsset: m.collateralAsset ?? { address: import_ethers2.ethers.ZeroAddress, symbol: "N/A", decimals: 0 },
|
|
628
|
+
oracle: m.oracleAddress,
|
|
629
|
+
irm: m.irmAddress,
|
|
630
|
+
lltv: BigInt(m.lltv),
|
|
631
|
+
totalSupplyAssets: BigInt(m.state?.supplyAssets ?? "0"),
|
|
632
|
+
totalBorrowAssets: BigInt(m.state?.borrowAssets ?? "0"),
|
|
633
|
+
utilization: m.state?.utilization ? Number(m.state.utilization) : 0
|
|
634
|
+
}));
|
|
635
|
+
this._discoveredAt = Date.now();
|
|
636
|
+
for (const mi of this._discoveredMarkets) {
|
|
637
|
+
if (mi.collateralAsset.address !== import_ethers2.ethers.ZeroAddress) {
|
|
638
|
+
this._marketCache.set(mi.collateralAsset.address.toLowerCase(), {
|
|
639
|
+
loanToken: mi.loanAsset.address,
|
|
640
|
+
collateralToken: mi.collateralAsset.address,
|
|
641
|
+
oracle: mi.oracle,
|
|
642
|
+
irm: mi.irm,
|
|
643
|
+
lltv: mi.lltv
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return this._discoveredMarkets;
|
|
648
|
+
} catch {
|
|
649
|
+
return this._discoveredMarkets ?? [];
|
|
650
|
+
}
|
|
685
651
|
}
|
|
686
652
|
/**
|
|
687
|
-
* Get
|
|
688
|
-
*
|
|
653
|
+
* Get MarketParams for a collateral token.
|
|
654
|
+
* Tries cache → API → on-chain idToMarketParams.
|
|
689
655
|
*/
|
|
690
|
-
async
|
|
691
|
-
const
|
|
692
|
-
|
|
656
|
+
async findMarketForCollateral(collateralSymbolOrAddress) {
|
|
657
|
+
const colInfo = BASE_COLLATERALS[collateralSymbolOrAddress];
|
|
658
|
+
const colAddr = (colInfo?.address ?? collateralSymbolOrAddress).toLowerCase();
|
|
659
|
+
const cached = this._marketCache.get(colAddr);
|
|
660
|
+
if (cached) return cached;
|
|
661
|
+
await this.getMarkets();
|
|
662
|
+
const fromApi = this._marketCache.get(colAddr);
|
|
663
|
+
if (fromApi) return fromApi;
|
|
664
|
+
throw new AgetherError(
|
|
665
|
+
`No Morpho market found for collateral ${collateralSymbolOrAddress}`,
|
|
666
|
+
"MARKET_NOT_FOUND"
|
|
693
667
|
);
|
|
694
|
-
return response.data;
|
|
695
668
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
LP_VAULT_ABI,
|
|
707
|
-
options.signer
|
|
708
|
-
);
|
|
669
|
+
/** Read MarketParams on-chain by market ID (bytes32). */
|
|
670
|
+
async getMarketParams(marketId) {
|
|
671
|
+
const result = await this.morphoBlue.idToMarketParams(marketId);
|
|
672
|
+
return {
|
|
673
|
+
loanToken: result.loanToken,
|
|
674
|
+
collateralToken: result.collateralToken,
|
|
675
|
+
oracle: result.oracle,
|
|
676
|
+
irm: result.irm,
|
|
677
|
+
lltv: result.lltv
|
|
678
|
+
};
|
|
709
679
|
}
|
|
710
|
-
//
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
async
|
|
715
|
-
const
|
|
716
|
-
const
|
|
717
|
-
await this.signer.getAddress(),
|
|
718
|
-
this.config.contracts.lpVault
|
|
719
|
-
);
|
|
720
|
-
if (allowance < amount) {
|
|
721
|
-
const approveTx = await asset.approve(
|
|
722
|
-
this.config.contracts.lpVault,
|
|
723
|
-
amount
|
|
724
|
-
);
|
|
725
|
-
await approveTx.wait();
|
|
726
|
-
}
|
|
727
|
-
const receiver = await this.signer.getAddress();
|
|
728
|
-
const tx = await this.vault.deposit(amount, receiver);
|
|
729
|
-
const receipt = await tx.wait();
|
|
680
|
+
// ════════════════════════════════════════════════════════
|
|
681
|
+
// Position Reads
|
|
682
|
+
// ════════════════════════════════════════════════════════
|
|
683
|
+
/** Read on-chain position for a specific market. */
|
|
684
|
+
async getPosition(marketId) {
|
|
685
|
+
const acctAddr = await this.getAccountAddress();
|
|
686
|
+
const pos = await this.morphoBlue.position(marketId, acctAddr);
|
|
730
687
|
return {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
gasUsed: receipt.gasUsed
|
|
688
|
+
supplyShares: pos.supplyShares,
|
|
689
|
+
borrowShares: pos.borrowShares,
|
|
690
|
+
collateral: pos.collateral
|
|
735
691
|
};
|
|
736
692
|
}
|
|
737
693
|
/**
|
|
738
|
-
*
|
|
694
|
+
* Full status: positions across all discovered markets.
|
|
739
695
|
*/
|
|
740
|
-
async
|
|
741
|
-
const
|
|
742
|
-
const
|
|
743
|
-
const
|
|
744
|
-
|
|
696
|
+
async getStatus() {
|
|
697
|
+
const acctAddr = await this.getAccountAddress();
|
|
698
|
+
const markets = await this.getMarkets();
|
|
699
|
+
const positions = [];
|
|
700
|
+
let totalDebt = 0n;
|
|
701
|
+
for (const m of markets) {
|
|
702
|
+
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
703
|
+
try {
|
|
704
|
+
const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
|
|
705
|
+
if (pos.collateral === 0n && pos.borrowShares === 0n && pos.supplyShares === 0n) continue;
|
|
706
|
+
let debt = 0n;
|
|
707
|
+
if (pos.borrowShares > 0n) {
|
|
708
|
+
try {
|
|
709
|
+
const mkt = await this.morphoBlue.market(m.uniqueKey);
|
|
710
|
+
const totalBorrowShares = BigInt(mkt.totalBorrowShares);
|
|
711
|
+
const totalBorrowAssets = BigInt(mkt.totalBorrowAssets);
|
|
712
|
+
debt = totalBorrowShares > 0n ? BigInt(pos.borrowShares) * totalBorrowAssets / totalBorrowShares : 0n;
|
|
713
|
+
totalDebt += debt;
|
|
714
|
+
} catch {
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
positions.push({
|
|
718
|
+
marketId: m.uniqueKey,
|
|
719
|
+
collateralToken: m.collateralAsset.symbol,
|
|
720
|
+
collateral: import_ethers2.ethers.formatUnits(pos.collateral, m.collateralAsset.decimals),
|
|
721
|
+
borrowShares: pos.borrowShares.toString(),
|
|
722
|
+
supplyShares: pos.supplyShares.toString(),
|
|
723
|
+
debt: import_ethers2.ethers.formatUnits(debt, 6)
|
|
724
|
+
});
|
|
725
|
+
} catch {
|
|
726
|
+
continue;
|
|
727
|
+
}
|
|
728
|
+
}
|
|
745
729
|
return {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
730
|
+
agentId: this.agentId || "?",
|
|
731
|
+
agentAccount: acctAddr,
|
|
732
|
+
totalDebt: import_ethers2.ethers.formatUnits(totalDebt, 6),
|
|
733
|
+
positions
|
|
750
734
|
};
|
|
751
735
|
}
|
|
736
|
+
// ════════════════════════════════════════════════════════
|
|
737
|
+
// Lending Operations (all via AgentAccount.executeBatch)
|
|
738
|
+
// ════════════════════════════════════════════════════════
|
|
752
739
|
/**
|
|
753
|
-
*
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
740
|
+
* Deposit collateral into Morpho Blue.
|
|
741
|
+
*
|
|
742
|
+
* Flow:
|
|
743
|
+
* 1. EOA transfers collateral to AgentAccount
|
|
744
|
+
* 2. AgentAccount.executeBatch:
|
|
745
|
+
* [collateral.approve(MorphoBlue), Morpho.supplyCollateral(params)]
|
|
746
|
+
*/
|
|
747
|
+
async supplyCollateral(tokenSymbol, amount, marketParams) {
|
|
748
|
+
const acctAddr = await this.getAccountAddress();
|
|
749
|
+
const colInfo = BASE_COLLATERALS[tokenSymbol];
|
|
750
|
+
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
751
|
+
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
752
|
+
const weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
|
|
753
|
+
const morphoAddr = this.config.contracts.morphoBlue;
|
|
754
|
+
const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this.wallet);
|
|
755
|
+
const transferTx = await colToken.transfer(acctAddr, weiAmount);
|
|
756
|
+
await transferTx.wait();
|
|
757
|
+
const targets = [colInfo.address, morphoAddr];
|
|
758
|
+
const values = [0n, 0n];
|
|
759
|
+
const datas = [
|
|
760
|
+
erc20Iface.encodeFunctionData("approve", [morphoAddr, weiAmount]),
|
|
761
|
+
morphoIface.encodeFunctionData("supplyCollateral", [
|
|
762
|
+
this._toTuple(params),
|
|
763
|
+
weiAmount,
|
|
764
|
+
acctAddr,
|
|
765
|
+
"0x"
|
|
766
|
+
])
|
|
767
|
+
];
|
|
768
|
+
const receipt = await this.batch(targets, values, datas);
|
|
760
769
|
return {
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
770
|
+
tx: receipt.hash,
|
|
771
|
+
collateralToken: tokenSymbol,
|
|
772
|
+
amount,
|
|
773
|
+
agentAccount: acctAddr
|
|
765
774
|
};
|
|
766
775
|
}
|
|
767
|
-
// ============ View Functions ============
|
|
768
776
|
/**
|
|
769
|
-
*
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
777
|
+
* Borrow USDC against existing collateral.
|
|
778
|
+
*
|
|
779
|
+
* AgentAccount.execute: Morpho.borrow(params, amount, 0, account, account)
|
|
780
|
+
*
|
|
781
|
+
* @param usdcAmount - USDC amount (e.g. '100')
|
|
782
|
+
* @param tokenSymbol - collateral symbol to identify which market (default: first with collateral)
|
|
783
|
+
*/
|
|
784
|
+
async borrow(usdcAmount, tokenSymbol, marketParams) {
|
|
785
|
+
const acctAddr = await this.getAccountAddress();
|
|
786
|
+
const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
|
|
787
|
+
const morphoAddr = this.config.contracts.morphoBlue;
|
|
788
|
+
let params;
|
|
789
|
+
let usedToken = tokenSymbol || "WETH";
|
|
790
|
+
if (marketParams) {
|
|
791
|
+
params = marketParams;
|
|
792
|
+
} else if (tokenSymbol) {
|
|
793
|
+
params = await this.findMarketForCollateral(tokenSymbol);
|
|
794
|
+
} else {
|
|
795
|
+
const { params: p, symbol } = await this._findActiveMarket();
|
|
796
|
+
params = p;
|
|
797
|
+
usedToken = symbol;
|
|
798
|
+
}
|
|
799
|
+
const data = morphoIface.encodeFunctionData("borrow", [
|
|
800
|
+
this._toTuple(params),
|
|
801
|
+
amount,
|
|
802
|
+
0n,
|
|
803
|
+
acctAddr,
|
|
804
|
+
acctAddr
|
|
776
805
|
]);
|
|
777
|
-
const
|
|
778
|
-
const totalSupply = await this.vault.totalSupply();
|
|
779
|
-
const sharePrice = totalSupply > 0n ? totalAssets * BigInt(1e18) / totalSupply : BigInt(1e18);
|
|
806
|
+
const receipt = await this.exec(morphoAddr, data);
|
|
780
807
|
return {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
sharePrice
|
|
808
|
+
tx: receipt.hash,
|
|
809
|
+
amount: usdcAmount,
|
|
810
|
+
collateralToken: usedToken,
|
|
811
|
+
agentAccount: acctAddr
|
|
786
812
|
};
|
|
787
813
|
}
|
|
788
814
|
/**
|
|
789
|
-
*
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
815
|
+
* Deposit collateral AND borrow USDC in one batched transaction.
|
|
816
|
+
*
|
|
817
|
+
* AgentAccount.executeBatch:
|
|
818
|
+
* [collateral.approve, Morpho.supplyCollateral, Morpho.borrow]
|
|
819
|
+
*
|
|
820
|
+
* The collateral must be transferred to AgentAccount first.
|
|
821
|
+
*/
|
|
822
|
+
async depositAndBorrow(tokenSymbol, collateralAmount, borrowUsdcAmount, marketParams) {
|
|
823
|
+
const acctAddr = await this.getAccountAddress();
|
|
824
|
+
const colInfo = BASE_COLLATERALS[tokenSymbol];
|
|
825
|
+
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
826
|
+
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
827
|
+
const colWei = import_ethers2.ethers.parseUnits(collateralAmount, colInfo.decimals);
|
|
828
|
+
const borrowWei = import_ethers2.ethers.parseUnits(borrowUsdcAmount, 6);
|
|
829
|
+
const morphoAddr = this.config.contracts.morphoBlue;
|
|
830
|
+
const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this.wallet);
|
|
831
|
+
const transferTx = await colToken.transfer(acctAddr, colWei);
|
|
832
|
+
await transferTx.wait();
|
|
833
|
+
const targets = [colInfo.address, morphoAddr, morphoAddr];
|
|
834
|
+
const values = [0n, 0n, 0n];
|
|
835
|
+
const datas = [
|
|
836
|
+
erc20Iface.encodeFunctionData("approve", [morphoAddr, colWei]),
|
|
837
|
+
morphoIface.encodeFunctionData("supplyCollateral", [
|
|
838
|
+
this._toTuple(params),
|
|
839
|
+
colWei,
|
|
840
|
+
acctAddr,
|
|
841
|
+
"0x"
|
|
842
|
+
]),
|
|
843
|
+
morphoIface.encodeFunctionData("borrow", [
|
|
844
|
+
this._toTuple(params),
|
|
845
|
+
borrowWei,
|
|
846
|
+
0n,
|
|
847
|
+
acctAddr,
|
|
848
|
+
acctAddr
|
|
849
|
+
])
|
|
850
|
+
];
|
|
851
|
+
const receipt = await this.batch(targets, values, datas);
|
|
796
852
|
return {
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
853
|
+
tx: receipt.hash,
|
|
854
|
+
collateralToken: tokenSymbol,
|
|
855
|
+
collateralAmount,
|
|
856
|
+
borrowAmount: borrowUsdcAmount,
|
|
857
|
+
agentAccount: acctAddr
|
|
800
858
|
};
|
|
801
859
|
}
|
|
802
860
|
/**
|
|
803
|
-
*
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
861
|
+
* Repay borrowed USDC from AgentAccount.
|
|
862
|
+
*
|
|
863
|
+
* AgentAccount.executeBatch:
|
|
864
|
+
* [USDC.approve(MorphoBlue), Morpho.repay(params)]
|
|
865
|
+
*/
|
|
866
|
+
async repay(usdcAmount, tokenSymbol, marketParams) {
|
|
867
|
+
const acctAddr = await this.getAccountAddress();
|
|
868
|
+
const amount = import_ethers2.ethers.parseUnits(usdcAmount, 6);
|
|
869
|
+
const morphoAddr = this.config.contracts.morphoBlue;
|
|
870
|
+
const usdcAddr = this.config.contracts.usdc;
|
|
871
|
+
let params;
|
|
872
|
+
if (marketParams) {
|
|
873
|
+
params = marketParams;
|
|
874
|
+
} else if (tokenSymbol) {
|
|
875
|
+
params = await this.findMarketForCollateral(tokenSymbol);
|
|
876
|
+
} else {
|
|
877
|
+
const { params: p } = await this._findActiveMarket();
|
|
878
|
+
params = p;
|
|
879
|
+
}
|
|
880
|
+
const targets = [usdcAddr, morphoAddr];
|
|
881
|
+
const values = [0n, 0n];
|
|
882
|
+
const datas = [
|
|
883
|
+
erc20Iface.encodeFunctionData("approve", [morphoAddr, amount]),
|
|
884
|
+
morphoIface.encodeFunctionData("repay", [
|
|
885
|
+
this._toTuple(params),
|
|
886
|
+
amount,
|
|
887
|
+
0n,
|
|
888
|
+
acctAddr,
|
|
889
|
+
"0x"
|
|
890
|
+
])
|
|
891
|
+
];
|
|
892
|
+
const receipt = await this.batch(targets, values, datas);
|
|
893
|
+
let remainingDebt = "0";
|
|
894
|
+
try {
|
|
895
|
+
const status = await this.getStatus();
|
|
896
|
+
remainingDebt = status.totalDebt;
|
|
897
|
+
} catch {
|
|
898
|
+
}
|
|
899
|
+
return { tx: receipt.hash, amount: usdcAmount, remainingDebt };
|
|
807
900
|
}
|
|
808
901
|
/**
|
|
809
|
-
*
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
902
|
+
* Withdraw collateral from Morpho Blue.
|
|
903
|
+
*
|
|
904
|
+
* AgentAccount.execute: Morpho.withdrawCollateral(params, amount, account, receiver)
|
|
905
|
+
*
|
|
906
|
+
* @param receiver - defaults to EOA wallet
|
|
907
|
+
*/
|
|
908
|
+
async withdrawCollateral(tokenSymbol, amount, marketParams, receiver) {
|
|
909
|
+
const acctAddr = await this.getAccountAddress();
|
|
910
|
+
const colInfo = BASE_COLLATERALS[tokenSymbol];
|
|
911
|
+
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
912
|
+
const params = marketParams ?? await this.findMarketForCollateral(tokenSymbol);
|
|
913
|
+
const morphoAddr = this.config.contracts.morphoBlue;
|
|
914
|
+
const dest = receiver || this.wallet.address;
|
|
915
|
+
let weiAmount;
|
|
916
|
+
if (amount === "all") {
|
|
917
|
+
const markets = await this.getMarkets();
|
|
918
|
+
const market = markets.find(
|
|
919
|
+
(m) => m.collateralAsset.address.toLowerCase() === colInfo.address.toLowerCase()
|
|
920
|
+
);
|
|
921
|
+
if (!market) throw new AgetherError("Market not found", "MARKET_NOT_FOUND");
|
|
922
|
+
const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
|
|
923
|
+
weiAmount = pos.collateral;
|
|
924
|
+
if (weiAmount === 0n) throw new AgetherError("No collateral to withdraw", "NO_COLLATERAL");
|
|
925
|
+
} else {
|
|
926
|
+
weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
|
|
927
|
+
}
|
|
928
|
+
const data = morphoIface.encodeFunctionData("withdrawCollateral", [
|
|
929
|
+
this._toTuple(params),
|
|
930
|
+
weiAmount,
|
|
931
|
+
acctAddr,
|
|
932
|
+
dest
|
|
933
|
+
]);
|
|
934
|
+
const receipt = await this.exec(morphoAddr, data);
|
|
935
|
+
let remainingCollateral = "0";
|
|
936
|
+
try {
|
|
937
|
+
const markets = await this.getMarkets();
|
|
938
|
+
const market = markets.find(
|
|
939
|
+
(m) => m.collateralAsset.address.toLowerCase() === colInfo.address.toLowerCase()
|
|
940
|
+
);
|
|
941
|
+
if (market) {
|
|
942
|
+
const pos = await this.morphoBlue.position(market.uniqueKey, acctAddr);
|
|
943
|
+
remainingCollateral = import_ethers2.ethers.formatUnits(pos.collateral, colInfo.decimals);
|
|
944
|
+
}
|
|
945
|
+
} catch {
|
|
946
|
+
}
|
|
947
|
+
return {
|
|
948
|
+
tx: receipt.hash,
|
|
949
|
+
token: tokenSymbol,
|
|
950
|
+
amount: amount === "all" ? import_ethers2.ethers.formatUnits(weiAmount, colInfo.decimals) : amount,
|
|
951
|
+
remainingCollateral,
|
|
952
|
+
destination: dest
|
|
953
|
+
};
|
|
813
954
|
}
|
|
814
955
|
/**
|
|
815
|
-
*
|
|
956
|
+
* Sponsor: transfer collateral to another agent's AgentAccount.
|
|
957
|
+
* (The agent must then supplyCollateral themselves via their own account.)
|
|
816
958
|
*/
|
|
817
|
-
async
|
|
818
|
-
const
|
|
819
|
-
|
|
959
|
+
async sponsor(target, tokenSymbol, amount) {
|
|
960
|
+
const colInfo = BASE_COLLATERALS[tokenSymbol];
|
|
961
|
+
if (!colInfo) throw new AgetherError(`Unknown collateral: ${tokenSymbol}`, "UNKNOWN_COLLATERAL");
|
|
962
|
+
let targetAddr;
|
|
963
|
+
if (target.address) {
|
|
964
|
+
targetAddr = target.address;
|
|
965
|
+
} else if (target.agentId) {
|
|
966
|
+
targetAddr = await this.accountFactory.getAccount(BigInt(target.agentId));
|
|
967
|
+
if (targetAddr === import_ethers2.ethers.ZeroAddress) throw new AgetherError("Target agent has no account", "NO_ACCOUNT");
|
|
968
|
+
} else {
|
|
969
|
+
throw new AgetherError("Provide agentId or address", "INVALID_TARGET");
|
|
970
|
+
}
|
|
971
|
+
const weiAmount = import_ethers2.ethers.parseUnits(amount, colInfo.decimals);
|
|
972
|
+
const colToken = new import_ethers2.Contract(colInfo.address, ERC20_ABI, this.wallet);
|
|
973
|
+
const tx = await colToken.transfer(targetAddr, weiAmount);
|
|
974
|
+
const receipt = await tx.wait();
|
|
975
|
+
return { tx: receipt.hash, targetAccount: targetAddr, targetAgentId: target.agentId };
|
|
976
|
+
}
|
|
977
|
+
// ════════════════════════════════════════════════════════
|
|
978
|
+
// Reputation (AgentReputation contract)
|
|
979
|
+
// ════════════════════════════════════════════════════════
|
|
980
|
+
async getCreditScore() {
|
|
981
|
+
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
982
|
+
return this.agentReputation.getCreditScore(BigInt(this.agentId));
|
|
983
|
+
}
|
|
984
|
+
async getAttestation() {
|
|
985
|
+
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
986
|
+
const att = await this.agentReputation.getAttestation(BigInt(this.agentId));
|
|
987
|
+
return { score: att.score, timestamp: att.timestamp, signer: att.signer };
|
|
988
|
+
}
|
|
989
|
+
async isEligible(minScore = 500n) {
|
|
990
|
+
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
991
|
+
const [eligible, currentScore] = await this.agentReputation.isEligible(BigInt(this.agentId), minScore);
|
|
992
|
+
return { eligible, currentScore };
|
|
993
|
+
}
|
|
994
|
+
async isScoreFresh() {
|
|
995
|
+
if (!this.agentId) throw new AgetherError("agentId not set", "NO_AGENT_ID");
|
|
996
|
+
const [fresh, age] = await this.agentReputation.isScoreFresh(BigInt(this.agentId));
|
|
997
|
+
return { fresh, age };
|
|
998
|
+
}
|
|
999
|
+
// ════════════════════════════════════════════════════════
|
|
1000
|
+
// Internal Helpers
|
|
1001
|
+
// ════════════════════════════════════════════════════════
|
|
1002
|
+
/**
|
|
1003
|
+
* Execute a single call via AgentAccount.execute.
|
|
1004
|
+
*/
|
|
1005
|
+
async exec(target, data, value = 0n) {
|
|
1006
|
+
const acctAddr = await this.getAccountAddress();
|
|
1007
|
+
const account = new import_ethers2.Contract(acctAddr, AGENT_ACCOUNT_ABI, this.wallet);
|
|
1008
|
+
let gasLimit;
|
|
1009
|
+
try {
|
|
1010
|
+
const estimate = await account.execute.estimateGas(target, value, data);
|
|
1011
|
+
gasLimit = estimate * 130n / 100n;
|
|
1012
|
+
} catch {
|
|
1013
|
+
gasLimit = 500000n;
|
|
1014
|
+
}
|
|
1015
|
+
const tx = await account.execute(target, value, data, { gasLimit });
|
|
1016
|
+
return tx.wait();
|
|
820
1017
|
}
|
|
821
1018
|
/**
|
|
822
|
-
*
|
|
1019
|
+
* Execute multiple calls via AgentAccount.executeBatch.
|
|
823
1020
|
*/
|
|
824
|
-
async
|
|
825
|
-
const
|
|
826
|
-
const
|
|
827
|
-
|
|
828
|
-
const lpYield = avgAPR * (1e4 - protocolFee) / 1e4;
|
|
829
|
-
const effectiveYield = lpYield * stats.utilizationRate / 100;
|
|
830
|
-
return effectiveYield / 100;
|
|
831
|
-
}
|
|
832
|
-
// ============ Utility ============
|
|
833
|
-
async getAsset() {
|
|
834
|
-
if (!this.asset) {
|
|
835
|
-
const assetAddress = await this.vault.asset();
|
|
836
|
-
this.asset = new import_ethers2.Contract(assetAddress, ERC20_ABI, this.signer);
|
|
837
|
-
}
|
|
838
|
-
return this.asset;
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* Get underlying asset address
|
|
842
|
-
*/
|
|
843
|
-
async getAssetAddress() {
|
|
844
|
-
return await this.vault.asset();
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
|
-
* Get vault share token address
|
|
848
|
-
*/
|
|
849
|
-
getVaultAddress() {
|
|
850
|
-
return this.config.contracts.lpVault;
|
|
851
|
-
}
|
|
852
|
-
};
|
|
853
|
-
|
|
854
|
-
// src/clients/AgentIdentityClient.ts
|
|
855
|
-
var import_ethers3 = require("ethers");
|
|
856
|
-
var ERC8004_IDENTITY_ABI = [
|
|
857
|
-
// Registration
|
|
858
|
-
"function register() returns (uint256)",
|
|
859
|
-
"function register(string agentURI) returns (uint256)",
|
|
860
|
-
"function register(string agentURI, tuple(string key, bytes value)[] metadata) returns (uint256)",
|
|
861
|
-
// Management
|
|
862
|
-
"function setAgentURI(uint256 agentId, string newURI)",
|
|
863
|
-
"function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)",
|
|
864
|
-
"function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)",
|
|
865
|
-
// ERC-721 standard
|
|
866
|
-
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
867
|
-
"function balanceOf(address owner) view returns (uint256)",
|
|
868
|
-
"function tokenURI(uint256 tokenId) view returns (string)",
|
|
869
|
-
"function transferFrom(address from, address to, uint256 tokenId)",
|
|
870
|
-
"function approve(address to, uint256 tokenId)",
|
|
871
|
-
"function getApproved(uint256 tokenId) view returns (address)",
|
|
872
|
-
// Events
|
|
873
|
-
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
|
|
874
|
-
];
|
|
875
|
-
var ERC8004_REPUTATION_ABI = [
|
|
876
|
-
// Feedback
|
|
877
|
-
"function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash)",
|
|
878
|
-
"function revokeFeedback(uint256 agentId, uint64 feedbackIndex)",
|
|
879
|
-
// Queries
|
|
880
|
-
"function readFeedback(uint256 agentId, address clientAddress, uint64 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
|
|
881
|
-
"function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint64 count, int128 summaryValue, uint8 summaryValueDecimals)",
|
|
882
|
-
"function getClients(uint256 agentId) view returns (address[])",
|
|
883
|
-
"function getLastIndex(uint256 agentId, address clientAddress) view returns (uint64)",
|
|
884
|
-
// Registry
|
|
885
|
-
"function getIdentityRegistry() view returns (address)"
|
|
886
|
-
];
|
|
887
|
-
var ERC8004_SEPOLIA = {
|
|
888
|
-
identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
|
|
889
|
-
reputationRegistry: "0x8004B663056A597Dffe9eCcC1965A193B7388713"
|
|
890
|
-
};
|
|
891
|
-
var AgentIdentityClient = class {
|
|
892
|
-
constructor(options) {
|
|
893
|
-
this.config = options.config;
|
|
894
|
-
this.signer = options.signer;
|
|
895
|
-
const identityAddr = options.config.contracts?.identityRegistry || ERC8004_SEPOLIA.identityRegistry;
|
|
896
|
-
const reputationAddr = options.config.contracts?.reputationRegistry || ERC8004_SEPOLIA.reputationRegistry;
|
|
897
|
-
this.identityRegistry = new import_ethers3.ethers.Contract(identityAddr, ERC8004_IDENTITY_ABI, this.signer);
|
|
898
|
-
this.reputationRegistry = new import_ethers3.ethers.Contract(reputationAddr, ERC8004_REPUTATION_ABI, this.signer);
|
|
899
|
-
}
|
|
900
|
-
// ============ Identity Functions ============
|
|
901
|
-
/**
|
|
902
|
-
* Register a new agent (minimal - no metadata)
|
|
903
|
-
*/
|
|
904
|
-
async register() {
|
|
905
|
-
const tx = await this.identityRegistry["register()"]();
|
|
906
|
-
const receipt = await tx.wait();
|
|
907
|
-
const agentId = this.parseAgentIdFromReceipt(receipt);
|
|
908
|
-
return { agentId, txHash: receipt.hash };
|
|
909
|
-
}
|
|
910
|
-
/**
|
|
911
|
-
* Register agent with IPFS/HTTP URI to metadata JSON
|
|
912
|
-
* @param agentURI URI pointing to agent metadata (ipfs:// or https://)
|
|
913
|
-
*/
|
|
914
|
-
async registerWithURI(agentURI) {
|
|
915
|
-
const tx = await this.identityRegistry["register(string)"](agentURI);
|
|
916
|
-
const receipt = await tx.wait();
|
|
917
|
-
const agentId = this.parseAgentIdFromReceipt(receipt);
|
|
918
|
-
return { agentId, txHash: receipt.hash };
|
|
919
|
-
}
|
|
920
|
-
/**
|
|
921
|
-
* Check if the signer already owns an ERC-8004 identity token.
|
|
922
|
-
* Returns true if balanceOf > 0, false otherwise.
|
|
923
|
-
* Note: Cannot determine the specific agentId without enumeration —
|
|
924
|
-
* use agether init <pk> --agent-id <id> if you know your agentId.
|
|
925
|
-
*/
|
|
926
|
-
async hasExistingIdentity() {
|
|
927
|
-
try {
|
|
928
|
-
const address = await this.signer.getAddress();
|
|
929
|
-
const balance = await this.identityRegistry.balanceOf(address);
|
|
930
|
-
return balance > 0n;
|
|
931
|
-
} catch {
|
|
932
|
-
return false;
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
/**
|
|
936
|
-
* Register only if no identity exists; otherwise throw.
|
|
937
|
-
* Prevents accidental double-registration.
|
|
938
|
-
*/
|
|
939
|
-
async registerOrGet() {
|
|
940
|
-
const hasIdentity = await this.hasExistingIdentity();
|
|
941
|
-
if (hasIdentity) {
|
|
942
|
-
throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
|
|
943
|
-
}
|
|
944
|
-
const result = await this.register();
|
|
945
|
-
return { ...result, existing: false };
|
|
946
|
-
}
|
|
947
|
-
/**
|
|
948
|
-
* Register with URI only if no identity exists; otherwise throw.
|
|
949
|
-
* Prevents accidental double-registration.
|
|
950
|
-
*/
|
|
951
|
-
async registerOrGetWithURI(agentURI) {
|
|
952
|
-
const hasIdentity = await this.hasExistingIdentity();
|
|
953
|
-
if (hasIdentity) {
|
|
954
|
-
throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
|
|
955
|
-
}
|
|
956
|
-
const result = await this.registerWithURI(agentURI);
|
|
957
|
-
return { ...result, existing: false };
|
|
958
|
-
}
|
|
959
|
-
/**
|
|
960
|
-
* Register agent with URI and on-chain metadata
|
|
961
|
-
*/
|
|
962
|
-
async registerWithMetadata(agentURI, metadata) {
|
|
963
|
-
const metadataEntries = metadata.map((m) => ({
|
|
964
|
-
key: m.key,
|
|
965
|
-
value: import_ethers3.ethers.toUtf8Bytes(m.value)
|
|
966
|
-
}));
|
|
967
|
-
const tx = await this.identityRegistry["register(string,tuple(string,bytes)[])"](
|
|
968
|
-
agentURI,
|
|
969
|
-
metadataEntries
|
|
970
|
-
);
|
|
971
|
-
const receipt = await tx.wait();
|
|
972
|
-
const agentId = this.parseAgentIdFromReceipt(receipt);
|
|
973
|
-
return { agentId, txHash: receipt.hash };
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* Get agent owner address
|
|
977
|
-
*/
|
|
978
|
-
async getOwner(agentId) {
|
|
979
|
-
return await this.identityRegistry.ownerOf(agentId);
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* Get agent URI (metadata JSON location)
|
|
983
|
-
*/
|
|
984
|
-
async getAgentURI(agentId) {
|
|
985
|
-
return await this.identityRegistry.tokenURI(agentId);
|
|
986
|
-
}
|
|
987
|
-
/**
|
|
988
|
-
* Update agent URI
|
|
989
|
-
*/
|
|
990
|
-
async setAgentURI(agentId, newURI) {
|
|
991
|
-
const tx = await this.identityRegistry.setAgentURI(agentId, newURI);
|
|
992
|
-
const receipt = await tx.wait();
|
|
993
|
-
return receipt.hash;
|
|
994
|
-
}
|
|
995
|
-
/**
|
|
996
|
-
* Set on-chain metadata (key-value)
|
|
997
|
-
*/
|
|
998
|
-
async setMetadata(agentId, key, value) {
|
|
999
|
-
const tx = await this.identityRegistry.setMetadata(
|
|
1000
|
-
agentId,
|
|
1001
|
-
key,
|
|
1002
|
-
import_ethers3.ethers.toUtf8Bytes(value)
|
|
1003
|
-
);
|
|
1004
|
-
const receipt = await tx.wait();
|
|
1005
|
-
return receipt.hash;
|
|
1006
|
-
}
|
|
1007
|
-
/**
|
|
1008
|
-
* Get on-chain metadata
|
|
1009
|
-
*/
|
|
1010
|
-
async getMetadata(agentId, key) {
|
|
1011
|
-
const value = await this.identityRegistry.getMetadata(agentId, key);
|
|
1012
|
-
return import_ethers3.ethers.toUtf8String(value);
|
|
1013
|
-
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Transfer agent to new owner
|
|
1016
|
-
*/
|
|
1017
|
-
async transfer(agentId, to) {
|
|
1018
|
-
const from = await this.signer.getAddress();
|
|
1019
|
-
const tx = await this.identityRegistry.transferFrom(from, to, agentId);
|
|
1020
|
-
const receipt = await tx.wait();
|
|
1021
|
-
return receipt.hash;
|
|
1022
|
-
}
|
|
1023
|
-
/**
|
|
1024
|
-
* Fetch and parse agent metadata from URI
|
|
1025
|
-
*/
|
|
1026
|
-
async fetchAgentMetadata(agentId) {
|
|
1027
|
-
try {
|
|
1028
|
-
const uri = await this.getAgentURI(agentId);
|
|
1029
|
-
let fetchUrl = uri;
|
|
1030
|
-
if (uri.startsWith("ipfs://")) {
|
|
1031
|
-
const cid = uri.replace("ipfs://", "");
|
|
1032
|
-
fetchUrl = `https://ipfs.io/ipfs/${cid}`;
|
|
1033
|
-
}
|
|
1034
|
-
const response = await fetch(fetchUrl);
|
|
1035
|
-
if (!response.ok) return null;
|
|
1036
|
-
return await response.json();
|
|
1037
|
-
} catch {
|
|
1038
|
-
return null;
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
// ============ Reputation Functions ============
|
|
1042
|
-
/**
|
|
1043
|
-
* Give feedback to an agent
|
|
1044
|
-
*/
|
|
1045
|
-
async giveFeedback(input) {
|
|
1046
|
-
const feedbackHash = import_ethers3.ethers.keccak256(
|
|
1047
|
-
import_ethers3.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
1048
|
-
["uint256", "int128", "uint256"],
|
|
1049
|
-
[input.agentId, input.value, Date.now()]
|
|
1050
|
-
)
|
|
1051
|
-
);
|
|
1052
|
-
const tx = await this.reputationRegistry.giveFeedback(
|
|
1053
|
-
input.agentId,
|
|
1054
|
-
input.value,
|
|
1055
|
-
input.decimals || 0,
|
|
1056
|
-
input.tag1 || "",
|
|
1057
|
-
input.tag2 || "",
|
|
1058
|
-
input.endpoint || "",
|
|
1059
|
-
input.feedbackURI || "",
|
|
1060
|
-
feedbackHash
|
|
1061
|
-
);
|
|
1062
|
-
const receipt = await tx.wait();
|
|
1063
|
-
return receipt.hash;
|
|
1064
|
-
}
|
|
1065
|
-
/**
|
|
1066
|
-
* Give positive feedback (shorthand)
|
|
1067
|
-
*/
|
|
1068
|
-
async givePosisitiveFeedback(agentId, value = 100, tags = {}) {
|
|
1069
|
-
return this.giveFeedback({
|
|
1070
|
-
agentId,
|
|
1071
|
-
value: Math.abs(value),
|
|
1072
|
-
...tags
|
|
1073
|
-
});
|
|
1074
|
-
}
|
|
1075
|
-
/**
|
|
1076
|
-
* Give negative feedback (e.g., for defaults)
|
|
1077
|
-
*/
|
|
1078
|
-
async giveNegativeFeedback(agentId, value = -100, tags = {}) {
|
|
1079
|
-
return this.giveFeedback({
|
|
1080
|
-
agentId,
|
|
1081
|
-
value: -Math.abs(value),
|
|
1082
|
-
...tags
|
|
1083
|
-
});
|
|
1084
|
-
}
|
|
1085
|
-
/**
|
|
1086
|
-
* Record credit default in reputation system
|
|
1087
|
-
*/
|
|
1088
|
-
async recordCreditDefault(agentId, creditLineId) {
|
|
1089
|
-
return this.giveFeedback({
|
|
1090
|
-
agentId,
|
|
1091
|
-
value: -100,
|
|
1092
|
-
tag1: "credit",
|
|
1093
|
-
tag2: "default",
|
|
1094
|
-
feedbackURI: `agether://default/${creditLineId}`
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
/**
|
|
1098
|
-
* Get reputation summary for an agent
|
|
1099
|
-
*/
|
|
1100
|
-
async getReputation(agentId, tag1 = "", tag2 = "") {
|
|
1101
|
-
const clients = await this.reputationRegistry.getClients(agentId);
|
|
1102
|
-
if (clients.length === 0) {
|
|
1103
|
-
return {
|
|
1104
|
-
count: 0,
|
|
1105
|
-
totalValue: 0,
|
|
1106
|
-
averageValue: 0,
|
|
1107
|
-
clients: []
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
const [count, summaryValue, decimals] = await this.reputationRegistry.getSummary(
|
|
1111
|
-
agentId,
|
|
1112
|
-
clients,
|
|
1113
|
-
tag1,
|
|
1114
|
-
tag2
|
|
1115
|
-
);
|
|
1116
|
-
const divisor = 10 ** Number(decimals);
|
|
1117
|
-
const total = Number(summaryValue) / divisor;
|
|
1118
|
-
return {
|
|
1119
|
-
count: Number(count),
|
|
1120
|
-
totalValue: total,
|
|
1121
|
-
averageValue: Number(count) > 0 ? total / Number(count) : 0,
|
|
1122
|
-
clients: clients.map((c) => c)
|
|
1123
|
-
};
|
|
1124
|
-
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Check if agent has negative credit reputation
|
|
1127
|
-
*/
|
|
1128
|
-
async hasNegativeCreditReputation(agentId) {
|
|
1129
|
-
const rep = await this.getReputation(agentId, "credit", "");
|
|
1130
|
-
return rep.count > 0 && rep.averageValue < 0;
|
|
1131
|
-
}
|
|
1132
|
-
// ============ Agether Integration ============
|
|
1133
|
-
/**
|
|
1134
|
-
* Verify agent is eligible for Agether credit
|
|
1135
|
-
* Checks:
|
|
1136
|
-
* 1. Agent exists in ERC-8004 registry
|
|
1137
|
-
* 2. Agent has no negative credit reputation
|
|
1138
|
-
*/
|
|
1139
|
-
async verifyForCredit(agentId) {
|
|
1140
|
-
let owner;
|
|
1021
|
+
async batch(targets, values, datas) {
|
|
1022
|
+
const acctAddr = await this.getAccountAddress();
|
|
1023
|
+
const account = new import_ethers2.Contract(acctAddr, AGENT_ACCOUNT_ABI, this.wallet);
|
|
1024
|
+
let gasLimit;
|
|
1141
1025
|
try {
|
|
1142
|
-
|
|
1026
|
+
const estimate = await account.executeBatch.estimateGas(targets, values, datas);
|
|
1027
|
+
gasLimit = estimate * 130n / 100n;
|
|
1143
1028
|
} catch {
|
|
1144
|
-
|
|
1145
|
-
}
|
|
1146
|
-
const reputation = await this.getReputation(agentId, "credit", "");
|
|
1147
|
-
if (reputation.count > 0 && reputation.averageValue < 0) {
|
|
1148
|
-
return {
|
|
1149
|
-
eligible: false,
|
|
1150
|
-
reason: "Agent has negative credit reputation",
|
|
1151
|
-
owner,
|
|
1152
|
-
reputation
|
|
1153
|
-
};
|
|
1029
|
+
gasLimit = 800000n;
|
|
1154
1030
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1031
|
+
const tx = await account.executeBatch(targets, values, datas, { gasLimit });
|
|
1032
|
+
return tx.wait();
|
|
1033
|
+
}
|
|
1034
|
+
/** Convert MorphoMarketParams to Solidity tuple. */
|
|
1035
|
+
_toTuple(p) {
|
|
1036
|
+
return [p.loanToken, p.collateralToken, p.oracle, p.irm, p.lltv];
|
|
1037
|
+
}
|
|
1038
|
+
/** Find the first market where the agent has collateral deposited. */
|
|
1039
|
+
async _findActiveMarket() {
|
|
1040
|
+
const acctAddr = await this.getAccountAddress();
|
|
1041
|
+
const markets = await this.getMarkets();
|
|
1042
|
+
for (const m of markets) {
|
|
1043
|
+
if (!m.collateralAsset || m.collateralAsset.address === import_ethers2.ethers.ZeroAddress) continue;
|
|
1164
1044
|
try {
|
|
1165
|
-
const
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1045
|
+
const pos = await this.morphoBlue.position(m.uniqueKey, acctAddr);
|
|
1046
|
+
if (pos.collateral > 0n) {
|
|
1047
|
+
return {
|
|
1048
|
+
params: {
|
|
1049
|
+
loanToken: m.loanAsset.address,
|
|
1050
|
+
collateralToken: m.collateralAsset.address,
|
|
1051
|
+
oracle: m.oracle,
|
|
1052
|
+
irm: m.irm,
|
|
1053
|
+
lltv: m.lltv
|
|
1054
|
+
},
|
|
1055
|
+
symbol: m.collateralAsset.symbol
|
|
1056
|
+
};
|
|
1171
1057
|
}
|
|
1172
1058
|
} catch {
|
|
1173
1059
|
continue;
|
|
1174
1060
|
}
|
|
1175
1061
|
}
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
/**
|
|
1179
|
-
* Get contract addresses
|
|
1180
|
-
*/
|
|
1181
|
-
getContractAddresses() {
|
|
1182
|
-
return {
|
|
1183
|
-
identity: this.identityRegistry.target,
|
|
1184
|
-
reputation: this.reputationRegistry.target
|
|
1185
|
-
};
|
|
1062
|
+
const params = await this.findMarketForCollateral("WETH");
|
|
1063
|
+
return { params, symbol: "WETH" };
|
|
1186
1064
|
}
|
|
1187
1065
|
};
|
|
1188
1066
|
|
|
1067
|
+
// src/clients/ScoringClient.ts
|
|
1068
|
+
var import_axios2 = __toESM(require("axios"));
|
|
1069
|
+
|
|
1189
1070
|
// src/clients/X402Client.ts
|
|
1190
|
-
var
|
|
1071
|
+
var import_ethers3 = require("ethers");
|
|
1191
1072
|
var USDC_DOMAINS = {
|
|
1192
1073
|
"eip155:1": { name: "USD Coin", version: "2", address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
|
|
1193
1074
|
"eip155:8453": { name: "USD Coin", version: "2", address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
|
|
@@ -1199,11 +1080,11 @@ function chainIdFromNetwork(network) {
|
|
|
1199
1080
|
const m = network.match(/^eip155:(\d+)$/);
|
|
1200
1081
|
return m ? Number(m[1]) : 1;
|
|
1201
1082
|
}
|
|
1202
|
-
var
|
|
1083
|
+
var X402Client = class {
|
|
1203
1084
|
constructor(config) {
|
|
1204
1085
|
this.config = config;
|
|
1205
|
-
const provider = new
|
|
1206
|
-
this.wallet = new
|
|
1086
|
+
const provider = new import_ethers3.ethers.JsonRpcProvider(config.rpcUrl);
|
|
1087
|
+
this.wallet = new import_ethers3.ethers.Wallet(config.privateKey, provider);
|
|
1207
1088
|
}
|
|
1208
1089
|
async get(url, opts) {
|
|
1209
1090
|
return this.request(url, { ...opts, method: "GET" });
|
|
@@ -1248,9 +1129,6 @@ var _X402Client = class _X402Client {
|
|
|
1248
1129
|
console.log(` amount : ${requirements.amount} (atomic)`);
|
|
1249
1130
|
console.log(` asset : ${requirements.asset}`);
|
|
1250
1131
|
console.log(` payTo : ${requirements.payTo}`);
|
|
1251
|
-
if (this.config.autoDraw && this.config.accountAddress && this.config.morphoCreditAddress) {
|
|
1252
|
-
await this.ensureBalance(BigInt(requirements.amount));
|
|
1253
|
-
}
|
|
1254
1132
|
console.log(" [3/4] Signing EIP-3009 transferWithAuthorization\u2026");
|
|
1255
1133
|
const paymentPayload = await this.buildPaymentPayload(requirements, resource, url);
|
|
1256
1134
|
const paymentB64 = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
|
|
@@ -1346,7 +1224,7 @@ var _X402Client = class _X402Client {
|
|
|
1346
1224
|
const now = Math.floor(Date.now() / 1e3);
|
|
1347
1225
|
const validAfter = String(now - 60);
|
|
1348
1226
|
const validBefore = String(now + (reqs.maxTimeoutSeconds || 300));
|
|
1349
|
-
const nonce =
|
|
1227
|
+
const nonce = import_ethers3.ethers.hexlify(import_ethers3.ethers.randomBytes(32));
|
|
1350
1228
|
const chainId = chainIdFromNetwork(reqs.network);
|
|
1351
1229
|
const usdcDomain = USDC_DOMAINS[reqs.network] || USDC_DOMAINS["eip155:1"];
|
|
1352
1230
|
const payerAddress = this.config.accountAddress || this.wallet.address;
|
|
@@ -1411,43 +1289,6 @@ var _X402Client = class _X402Client {
|
|
|
1411
1289
|
}
|
|
1412
1290
|
};
|
|
1413
1291
|
}
|
|
1414
|
-
async ensureBalance(requiredAmount) {
|
|
1415
|
-
const accountAddr = this.config.accountAddress;
|
|
1416
|
-
const morphoAddr = this.config.morphoCreditAddress;
|
|
1417
|
-
const provider = this.wallet.provider;
|
|
1418
|
-
const usdcAddr = USDC_DOMAINS["eip155:8453"]?.address || USDC_DOMAINS["eip155:1"].address;
|
|
1419
|
-
const usdc = new import_ethers4.ethers.Contract(usdcAddr, _X402Client.ERC20_BALANCE_ABI, provider);
|
|
1420
|
-
const balance = await usdc.balanceOf(accountAddr);
|
|
1421
|
-
const needed = requiredAmount + requiredAmount / 10n;
|
|
1422
|
-
if (balance >= needed) return;
|
|
1423
|
-
const deficit = needed - balance;
|
|
1424
|
-
const minDraw = import_ethers4.ethers.parseUnits("10", 6);
|
|
1425
|
-
const drawAmount = deficit > minDraw ? deficit : minDraw;
|
|
1426
|
-
console.log(` [auto-draw] USDC balance $${import_ethers4.ethers.formatUnits(balance, 6)} < needed $${import_ethers4.ethers.formatUnits(needed, 6)}`);
|
|
1427
|
-
const morpho = new import_ethers4.ethers.Contract(morphoAddr, _X402Client.MORPHO_DRAW_ABI, provider);
|
|
1428
|
-
let collateralAddr = null;
|
|
1429
|
-
for (const addr of _X402Client.AUTO_DRAW_COLLATERALS) {
|
|
1430
|
-
try {
|
|
1431
|
-
const pos = await morpho.getPosition(accountAddr, addr);
|
|
1432
|
-
if (pos.collateralAmount > 0n) {
|
|
1433
|
-
collateralAddr = addr;
|
|
1434
|
-
break;
|
|
1435
|
-
}
|
|
1436
|
-
} catch {
|
|
1437
|
-
continue;
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
if (!collateralAddr) {
|
|
1441
|
-
throw new Error("autoDraw failed: no collateral deposited in Morpho. Deposit collateral first.");
|
|
1442
|
-
}
|
|
1443
|
-
const account = new import_ethers4.ethers.Contract(accountAddr, _X402Client.AGENT_ACCOUNT_EXEC_ABI, this.wallet);
|
|
1444
|
-
const morphoIface = new import_ethers4.ethers.Interface(_X402Client.MORPHO_DRAW_ABI);
|
|
1445
|
-
const calldata = morphoIface.encodeFunctionData("drawWithCollateral", [collateralAddr, drawAmount]);
|
|
1446
|
-
console.log(` [auto-draw] Borrowing $${import_ethers4.ethers.formatUnits(drawAmount, 6)} from Morpho...`);
|
|
1447
|
-
const tx = await account.execute(morphoAddr, 0, calldata);
|
|
1448
|
-
await tx.wait();
|
|
1449
|
-
console.log(` [auto-draw] \u2713 Borrowed (tx: ${tx.hash.slice(0, 14)}\u2026)`);
|
|
1450
|
-
}
|
|
1451
1292
|
// ──────────── Risk check via our backend ────────────
|
|
1452
1293
|
async riskCheck(paymentPayload, reqs) {
|
|
1453
1294
|
try {
|
|
@@ -1479,811 +1320,429 @@ var _X402Client = class _X402Client {
|
|
|
1479
1320
|
}
|
|
1480
1321
|
}
|
|
1481
1322
|
};
|
|
1482
|
-
// ──────────── Auto-draw (Flow 9) ────────────
|
|
1483
|
-
//
|
|
1484
|
-
// When autoDraw is enabled and the AgentAccount has insufficient USDC,
|
|
1485
|
-
// automatically borrow from Morpho credit line to cover the payment.
|
|
1486
|
-
_X402Client.MORPHO_DRAW_ABI = [
|
|
1487
|
-
"function drawWithCollateral(address collateralToken, uint256 amount)",
|
|
1488
|
-
"function getPosition(address account, address collateralToken) view returns (tuple(uint256 collateralAmount, uint256 borrowedAmount, uint256 borrowShares, bool isActive))"
|
|
1489
|
-
];
|
|
1490
|
-
_X402Client.AGENT_ACCOUNT_EXEC_ABI = [
|
|
1491
|
-
"function execute(address target, uint256 value, bytes data) payable returns (bytes)"
|
|
1492
|
-
];
|
|
1493
|
-
_X402Client.ERC20_BALANCE_ABI = [
|
|
1494
|
-
"function balanceOf(address) view returns (uint256)"
|
|
1495
|
-
];
|
|
1496
|
-
_X402Client.AUTO_DRAW_COLLATERALS = [
|
|
1497
|
-
"0x4200000000000000000000000000000000000006",
|
|
1498
|
-
// WETH
|
|
1499
|
-
"0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452",
|
|
1500
|
-
// wstETH
|
|
1501
|
-
"0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22"
|
|
1502
|
-
// cbETH
|
|
1503
|
-
];
|
|
1504
|
-
var X402Client = _X402Client;
|
|
1505
1323
|
|
|
1506
|
-
// src/clients/
|
|
1507
|
-
var
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
this.erc20Iface = new import_ethers5.ethers.Interface(ERC20_ABI);
|
|
1518
|
-
this.config = config;
|
|
1519
|
-
const provider = new import_ethers5.ethers.JsonRpcProvider(config.rpcUrl);
|
|
1520
|
-
this.signer = new import_ethers5.ethers.Wallet(config.privateKey, provider);
|
|
1521
|
-
this.morpho = new import_ethers5.Contract(config.contracts.morphoCredit, MORPHO_CREDIT_ABI, this.signer);
|
|
1522
|
-
this.factory = new import_ethers5.Contract(config.contracts.accountFactory, ACCOUNT_FACTORY_ABI, this.signer);
|
|
1523
|
-
this.collaterals = collaterals || BASE_COLLATERALS;
|
|
1524
|
-
if (config.agentId) this.resolvedAgentId = config.agentId.toString();
|
|
1525
|
-
}
|
|
1526
|
-
// ── Helpers ──
|
|
1527
|
-
resolveToken(tokenSymbol) {
|
|
1528
|
-
const token = this.collaterals[tokenSymbol];
|
|
1529
|
-
if (!token) {
|
|
1530
|
-
throw new Error(`Unsupported collateral token: ${tokenSymbol}. Supported: ${Object.keys(this.collaterals).join(", ")}`);
|
|
1324
|
+
// src/clients/ScoringClient.ts
|
|
1325
|
+
var ScoringClient = class {
|
|
1326
|
+
constructor(config) {
|
|
1327
|
+
this.endpoint = config.endpoint;
|
|
1328
|
+
this.client = import_axios2.default.create({
|
|
1329
|
+
baseURL: config.endpoint,
|
|
1330
|
+
headers: { "Content-Type": "application/json" },
|
|
1331
|
+
timeout: 3e4
|
|
1332
|
+
});
|
|
1333
|
+
if (config.x402) {
|
|
1334
|
+
this.x402Client = new X402Client(config.x402);
|
|
1531
1335
|
}
|
|
1532
|
-
return token;
|
|
1533
1336
|
}
|
|
1337
|
+
// ════════════════════════════════════════════════════════
|
|
1338
|
+
// Score (x402-gated — computes & submits on-chain)
|
|
1339
|
+
// ════════════════════════════════════════════════════════
|
|
1534
1340
|
/**
|
|
1535
|
-
*
|
|
1341
|
+
* Request a fresh score computation.
|
|
1342
|
+
*
|
|
1343
|
+
* This is x402-gated: the backend returns 402, the X402Client
|
|
1344
|
+
* signs an EIP-3009 payment, and the backend computes + submits
|
|
1345
|
+
* the score on-chain via AgentReputation.submitScore().
|
|
1346
|
+
*
|
|
1347
|
+
* Returns the ScoreResult with breakdown and txHash.
|
|
1536
1348
|
*/
|
|
1537
|
-
async
|
|
1538
|
-
|
|
1539
|
-
if (!this.
|
|
1540
|
-
throw new
|
|
1349
|
+
async requestScore(agentId) {
|
|
1350
|
+
const id = agentId.toString();
|
|
1351
|
+
if (!this.x402Client) {
|
|
1352
|
+
throw new AgetherError(
|
|
1353
|
+
"x402 config required for paid scoring. Provide x402 in ScoringClientConfig.",
|
|
1354
|
+
"X402_NOT_CONFIGURED"
|
|
1355
|
+
);
|
|
1541
1356
|
}
|
|
1542
|
-
const
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1357
|
+
const result = await this.x402Client.get(
|
|
1358
|
+
`${this.endpoint}/score/${id}`
|
|
1359
|
+
);
|
|
1360
|
+
if (!result.success || !result.data) {
|
|
1361
|
+
throw new AgetherError(
|
|
1362
|
+
`Scoring request failed: ${result.error || "unknown error"}`,
|
|
1363
|
+
"SCORING_FAILED"
|
|
1364
|
+
);
|
|
1546
1365
|
}
|
|
1547
|
-
|
|
1548
|
-
this.resolvedAgentId = tokenId.toString();
|
|
1549
|
-
return this.resolvedAgentId;
|
|
1366
|
+
return result.data;
|
|
1550
1367
|
}
|
|
1368
|
+
// ════════════════════════════════════════════════════════
|
|
1369
|
+
// Current Score (free — reads on-chain)
|
|
1370
|
+
// ════════════════════════════════════════════════════════
|
|
1551
1371
|
/**
|
|
1552
|
-
* Get the
|
|
1372
|
+
* Get the current on-chain score (free, no payment required).
|
|
1553
1373
|
*/
|
|
1554
|
-
async
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
const addr = await this.factory.getAccount(agentId);
|
|
1558
|
-
if (addr === import_ethers5.ethers.ZeroAddress) {
|
|
1559
|
-
throw new Error("No AgentAccount found. Register first.");
|
|
1560
|
-
}
|
|
1561
|
-
this.accountAddress = addr;
|
|
1562
|
-
return addr;
|
|
1374
|
+
async getCurrentScore(agentId) {
|
|
1375
|
+
const response = await this.client.get(`/score/${agentId.toString()}/current`);
|
|
1376
|
+
return response.data;
|
|
1563
1377
|
}
|
|
1378
|
+
// ════════════════════════════════════════════════════════
|
|
1379
|
+
// Agent Details
|
|
1380
|
+
// ════════════════════════════════════════════════════════
|
|
1564
1381
|
/**
|
|
1565
|
-
* Get
|
|
1382
|
+
* Get detailed agent info from backend.
|
|
1566
1383
|
*/
|
|
1567
|
-
async
|
|
1568
|
-
const
|
|
1569
|
-
|
|
1570
|
-
throw new Error(`No AgentAccount for agent #${agentId}`);
|
|
1571
|
-
}
|
|
1572
|
-
return addr;
|
|
1384
|
+
async getAgentDetails(agentId) {
|
|
1385
|
+
const response = await this.client.get(`/agents/${agentId.toString()}/details`);
|
|
1386
|
+
return response.data;
|
|
1573
1387
|
}
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1388
|
+
// ════════════════════════════════════════════════════════
|
|
1389
|
+
// Service Status
|
|
1390
|
+
// ════════════════════════════════════════════════════════
|
|
1391
|
+
/**
|
|
1392
|
+
* Health check.
|
|
1393
|
+
*/
|
|
1394
|
+
async getHealth() {
|
|
1395
|
+
const response = await this.client.get("/health");
|
|
1396
|
+
return response.data;
|
|
1581
1397
|
}
|
|
1582
1398
|
/**
|
|
1583
|
-
*
|
|
1584
|
-
* This is 2 EOA txs (approve + depositCollateralFor) — cannot be batched
|
|
1585
|
-
* because both are called from EOA, not from AgentAccount.
|
|
1399
|
+
* Detailed status (contracts, signer, chain).
|
|
1586
1400
|
*/
|
|
1587
|
-
async
|
|
1588
|
-
const
|
|
1589
|
-
|
|
1590
|
-
if (balance < amount) {
|
|
1591
|
-
throw new Error(
|
|
1592
|
-
`Insufficient ${tokenInfo.symbol}: have ${import_ethers5.ethers.formatUnits(balance, tokenInfo.decimals)}, need ${import_ethers5.ethers.formatUnits(amount, tokenInfo.decimals)}`
|
|
1593
|
-
);
|
|
1594
|
-
}
|
|
1595
|
-
const approveTx = await token.approve(this.config.contracts.morphoCredit, amount);
|
|
1596
|
-
await approveTx.wait();
|
|
1597
|
-
const depositTx = await this.morpho.depositCollateralFor(accountAddr, tokenInfo.address, amount);
|
|
1598
|
-
await depositTx.wait();
|
|
1599
|
-
return depositTx.hash;
|
|
1401
|
+
async getStatus() {
|
|
1402
|
+
const response = await this.client.get("/status");
|
|
1403
|
+
return response.data;
|
|
1600
1404
|
}
|
|
1601
1405
|
/**
|
|
1602
|
-
*
|
|
1603
|
-
* Each call is { target, value, data }.
|
|
1406
|
+
* Agent count and list.
|
|
1604
1407
|
*/
|
|
1605
|
-
async
|
|
1606
|
-
const
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1408
|
+
async getAgentCount() {
|
|
1409
|
+
const response = await this.client.get("/agents/count");
|
|
1410
|
+
return response.data;
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
// src/clients/AgentIdentityClient.ts
|
|
1415
|
+
var import_ethers4 = require("ethers");
|
|
1416
|
+
var ERC8004_IDENTITY_ABI = [
|
|
1417
|
+
// Registration
|
|
1418
|
+
"function register() returns (uint256)",
|
|
1419
|
+
"function register(string agentURI) returns (uint256)",
|
|
1420
|
+
"function register(string agentURI, tuple(string key, bytes value)[] metadata) returns (uint256)",
|
|
1421
|
+
// Management
|
|
1422
|
+
"function setAgentURI(uint256 agentId, string newURI)",
|
|
1423
|
+
"function setMetadata(uint256 agentId, string metadataKey, bytes metadataValue)",
|
|
1424
|
+
"function getMetadata(uint256 agentId, string metadataKey) view returns (bytes)",
|
|
1425
|
+
// ERC-721 standard
|
|
1426
|
+
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
1427
|
+
"function balanceOf(address owner) view returns (uint256)",
|
|
1428
|
+
"function tokenURI(uint256 tokenId) view returns (string)",
|
|
1429
|
+
"function transferFrom(address from, address to, uint256 tokenId)",
|
|
1430
|
+
"function approve(address to, uint256 tokenId)",
|
|
1431
|
+
"function getApproved(uint256 tokenId) view returns (address)",
|
|
1432
|
+
// Events
|
|
1433
|
+
"event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)"
|
|
1434
|
+
];
|
|
1435
|
+
var ERC8004_REPUTATION_ABI = [
|
|
1436
|
+
// Feedback
|
|
1437
|
+
"function giveFeedback(uint256 agentId, int128 value, uint8 valueDecimals, string tag1, string tag2, string endpoint, string feedbackURI, bytes32 feedbackHash)",
|
|
1438
|
+
"function revokeFeedback(uint256 agentId, uint64 feedbackIndex)",
|
|
1439
|
+
// Queries
|
|
1440
|
+
"function readFeedback(uint256 agentId, address clientAddress, uint64 feedbackIndex) view returns (int128 value, uint8 valueDecimals, string tag1, string tag2, bool isRevoked)",
|
|
1441
|
+
"function getSummary(uint256 agentId, address[] clientAddresses, string tag1, string tag2) view returns (uint64 count, int128 summaryValue, uint8 summaryValueDecimals)",
|
|
1442
|
+
"function getClients(uint256 agentId) view returns (address[])",
|
|
1443
|
+
"function getLastIndex(uint256 agentId, address clientAddress) view returns (uint64)",
|
|
1444
|
+
// Registry
|
|
1445
|
+
"function getIdentityRegistry() view returns (address)"
|
|
1446
|
+
];
|
|
1447
|
+
var ERC8004_SEPOLIA = {
|
|
1448
|
+
identityRegistry: "0x8004A818BFB912233c491871b3d84c89A494BD9e",
|
|
1449
|
+
reputationRegistry: "0x8004B663056A597Dffe9eCcC1965A193B7388713"
|
|
1450
|
+
};
|
|
1451
|
+
var AgentIdentityClient = class {
|
|
1452
|
+
constructor(options) {
|
|
1453
|
+
this.config = options.config;
|
|
1454
|
+
this.signer = options.signer;
|
|
1455
|
+
const identityAddr = options.config.contracts?.identityRegistry || ERC8004_SEPOLIA.identityRegistry;
|
|
1456
|
+
const reputationAddr = options.config.contracts?.reputationRegistry || ERC8004_SEPOLIA.reputationRegistry;
|
|
1457
|
+
this.identityRegistry = new import_ethers4.ethers.Contract(identityAddr, ERC8004_IDENTITY_ABI, this.signer);
|
|
1458
|
+
this.reputationRegistry = new import_ethers4.ethers.Contract(reputationAddr, ERC8004_REPUTATION_ABI, this.signer);
|
|
1614
1459
|
}
|
|
1615
|
-
//
|
|
1616
|
-
// Register
|
|
1617
|
-
// ══════════════════════════════════════════
|
|
1460
|
+
// ============ Identity Functions ============
|
|
1618
1461
|
/**
|
|
1619
|
-
* Register a new
|
|
1620
|
-
* If already registered (agentId in config or on-chain), returns existing info.
|
|
1621
|
-
*
|
|
1622
|
-
* @param name - Agent display name
|
|
1462
|
+
* Register a new agent (minimal - no metadata)
|
|
1623
1463
|
*/
|
|
1624
|
-
async register(
|
|
1625
|
-
|
|
1626
|
-
throw new Error("agentRegistry address required for register()");
|
|
1627
|
-
}
|
|
1628
|
-
const registry = new import_ethers5.Contract(
|
|
1629
|
-
this.config.contracts.agentRegistry,
|
|
1630
|
-
_MorphoCreditClient.ERC8004_ABI,
|
|
1631
|
-
this.signer
|
|
1632
|
-
);
|
|
1633
|
-
if (this.resolvedAgentId) {
|
|
1634
|
-
const agentId2 = BigInt(this.resolvedAgentId);
|
|
1635
|
-
const owner = await registry.ownerOf(agentId2);
|
|
1636
|
-
if (owner.toLowerCase() !== this.signer.address.toLowerCase()) {
|
|
1637
|
-
throw new Error("agentId in config does not belong to this wallet");
|
|
1638
|
-
}
|
|
1639
|
-
const exists2 = await this.factory.accountExists(agentId2);
|
|
1640
|
-
if (!exists2) {
|
|
1641
|
-
const tx2 = await this.factory.createAccount(agentId2);
|
|
1642
|
-
await tx2.wait();
|
|
1643
|
-
}
|
|
1644
|
-
const accountAddr2 = await this.factory.getAccount(this.resolvedAgentId);
|
|
1645
|
-
return {
|
|
1646
|
-
tx: "",
|
|
1647
|
-
agentId: this.resolvedAgentId,
|
|
1648
|
-
address: this.signer.address,
|
|
1649
|
-
agentAccount: accountAddr2,
|
|
1650
|
-
alreadyRegistered: true
|
|
1651
|
-
};
|
|
1652
|
-
}
|
|
1653
|
-
const balance = await registry.balanceOf(this.signer.address);
|
|
1654
|
-
if (balance > 0n) {
|
|
1655
|
-
const tokenId = await registry.tokenOfOwnerByIndex(this.signer.address, 0);
|
|
1656
|
-
this.resolvedAgentId = tokenId.toString();
|
|
1657
|
-
const accountAddr2 = await this.factory.getAccount(this.resolvedAgentId);
|
|
1658
|
-
return {
|
|
1659
|
-
tx: "",
|
|
1660
|
-
agentId: this.resolvedAgentId,
|
|
1661
|
-
address: this.signer.address,
|
|
1662
|
-
agentAccount: accountAddr2,
|
|
1663
|
-
alreadyRegistered: true
|
|
1664
|
-
};
|
|
1665
|
-
}
|
|
1666
|
-
const registrationFile = JSON.stringify({
|
|
1667
|
-
type: "https://eips.ethereum.org/EIPS/eip-8004#registration-v1",
|
|
1668
|
-
name,
|
|
1669
|
-
description: "AI agent registered via @agether/sdk",
|
|
1670
|
-
active: true,
|
|
1671
|
-
registrations: [{
|
|
1672
|
-
agentId: 0,
|
|
1673
|
-
agentRegistry: `eip155:8453:${this.config.contracts.agentRegistry}`
|
|
1674
|
-
}]
|
|
1675
|
-
});
|
|
1676
|
-
const agentURI = `data:application/json;base64,${Buffer.from(registrationFile).toString("base64")}`;
|
|
1677
|
-
const tx = await registry["register(string)"](agentURI);
|
|
1464
|
+
async register() {
|
|
1465
|
+
const tx = await this.identityRegistry["register()"]();
|
|
1678
1466
|
const receipt = await tx.wait();
|
|
1679
|
-
const
|
|
1680
|
-
|
|
1681
|
-
if (!transferLog?.topics?.[3]) {
|
|
1682
|
-
throw new Error("Could not parse agentId from receipt");
|
|
1683
|
-
}
|
|
1684
|
-
const agentId = BigInt(transferLog.topics[3]);
|
|
1685
|
-
this.resolvedAgentId = agentId.toString();
|
|
1686
|
-
const exists = await this.factory.accountExists(agentId);
|
|
1687
|
-
if (!exists) {
|
|
1688
|
-
const accTx = await this.factory.createAccount(agentId);
|
|
1689
|
-
await accTx.wait();
|
|
1690
|
-
}
|
|
1691
|
-
const accountAddr = await this.factory.getAccount(this.resolvedAgentId);
|
|
1692
|
-
this.accountAddress = accountAddr;
|
|
1693
|
-
return {
|
|
1694
|
-
tx: tx.hash,
|
|
1695
|
-
agentId: this.resolvedAgentId,
|
|
1696
|
-
address: this.signer.address,
|
|
1697
|
-
agentAccount: accountAddr,
|
|
1698
|
-
alreadyRegistered: false
|
|
1699
|
-
};
|
|
1467
|
+
const agentId = this.parseAgentIdFromReceipt(receipt);
|
|
1468
|
+
return { agentId, txHash: receipt.hash };
|
|
1700
1469
|
}
|
|
1701
|
-
// ══════════════════════════════════════════
|
|
1702
|
-
// Balances & Status
|
|
1703
|
-
// ══════════════════════════════════════════
|
|
1704
1470
|
/**
|
|
1705
|
-
*
|
|
1471
|
+
* Register agent with IPFS/HTTP URI to metadata JSON
|
|
1472
|
+
* @param agentURI URI pointing to agent metadata (ipfs:// or https://)
|
|
1706
1473
|
*/
|
|
1707
|
-
async
|
|
1708
|
-
const
|
|
1709
|
-
const
|
|
1710
|
-
const
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1474
|
+
async registerWithURI(agentURI) {
|
|
1475
|
+
const tx = await this.identityRegistry["register(string)"](agentURI);
|
|
1476
|
+
const receipt = await tx.wait();
|
|
1477
|
+
const agentId = this.parseAgentIdFromReceipt(receipt);
|
|
1478
|
+
return { agentId, txHash: receipt.hash };
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Check if the signer already owns an ERC-8004 identity token.
|
|
1482
|
+
* Returns true if balanceOf > 0, false otherwise.
|
|
1483
|
+
* Note: Cannot determine the specific agentId without enumeration —
|
|
1484
|
+
* use agether init <pk> --agent-id <id> if you know your agentId.
|
|
1485
|
+
*/
|
|
1486
|
+
async hasExistingIdentity() {
|
|
1718
1487
|
try {
|
|
1719
|
-
const
|
|
1720
|
-
const
|
|
1721
|
-
|
|
1722
|
-
result.agentAccount = {
|
|
1723
|
-
address: accountAddr,
|
|
1724
|
-
eth: import_ethers5.ethers.formatEther(accEth),
|
|
1725
|
-
usdc: import_ethers5.ethers.formatUnits(accUsdc, 6)
|
|
1726
|
-
};
|
|
1488
|
+
const address = await this.signer.getAddress();
|
|
1489
|
+
const balance = await this.identityRegistry.balanceOf(address);
|
|
1490
|
+
return balance > 0n;
|
|
1727
1491
|
} catch {
|
|
1492
|
+
return false;
|
|
1728
1493
|
}
|
|
1729
|
-
return result;
|
|
1730
1494
|
}
|
|
1731
1495
|
/**
|
|
1732
|
-
*
|
|
1496
|
+
* Register only if no identity exists; otherwise throw.
|
|
1497
|
+
* Prevents accidental double-registration.
|
|
1733
1498
|
*/
|
|
1734
|
-
async
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
const pos = await this.morpho.getPosition(accountAddr, info.address);
|
|
1739
|
-
if (pos.isActive || pos.collateralAmount > 0n || pos.borrowedAmount > 0n) {
|
|
1740
|
-
positions.push({
|
|
1741
|
-
token: symbol,
|
|
1742
|
-
collateral: import_ethers5.ethers.formatUnits(pos.collateralAmount, info.decimals),
|
|
1743
|
-
debt: `$${import_ethers5.ethers.formatUnits(pos.borrowedAmount, 6)}`,
|
|
1744
|
-
active: pos.isActive
|
|
1745
|
-
});
|
|
1746
|
-
}
|
|
1499
|
+
async registerOrGet() {
|
|
1500
|
+
const hasIdentity = await this.hasExistingIdentity();
|
|
1501
|
+
if (hasIdentity) {
|
|
1502
|
+
throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
|
|
1747
1503
|
}
|
|
1748
|
-
const
|
|
1749
|
-
return {
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1504
|
+
const result = await this.register();
|
|
1505
|
+
return { ...result, existing: false };
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Register with URI only if no identity exists; otherwise throw.
|
|
1509
|
+
* Prevents accidental double-registration.
|
|
1510
|
+
*/
|
|
1511
|
+
async registerOrGetWithURI(agentURI) {
|
|
1512
|
+
const hasIdentity = await this.hasExistingIdentity();
|
|
1513
|
+
if (hasIdentity) {
|
|
1514
|
+
throw new Error("Wallet already owns an ERC-8004 identity. Use agether init <pk> --agent-id <id> to set it.");
|
|
1515
|
+
}
|
|
1516
|
+
const result = await this.registerWithURI(agentURI);
|
|
1517
|
+
return { ...result, existing: false };
|
|
1518
|
+
}
|
|
1519
|
+
/**
|
|
1520
|
+
* Register agent with URI and on-chain metadata
|
|
1521
|
+
*/
|
|
1522
|
+
async registerWithMetadata(agentURI, metadata) {
|
|
1523
|
+
const metadataEntries = metadata.map((m) => ({
|
|
1524
|
+
key: m.key,
|
|
1525
|
+
value: import_ethers4.ethers.toUtf8Bytes(m.value)
|
|
1526
|
+
}));
|
|
1527
|
+
const tx = await this.identityRegistry["register(string,tuple(string,bytes)[])"](
|
|
1528
|
+
agentURI,
|
|
1529
|
+
metadataEntries
|
|
1530
|
+
);
|
|
1531
|
+
const receipt = await tx.wait();
|
|
1532
|
+
const agentId = this.parseAgentIdFromReceipt(receipt);
|
|
1533
|
+
return { agentId, txHash: receipt.hash };
|
|
1534
|
+
}
|
|
1535
|
+
/**
|
|
1536
|
+
* Get agent owner address
|
|
1537
|
+
*/
|
|
1538
|
+
async getOwner(agentId) {
|
|
1539
|
+
return await this.identityRegistry.ownerOf(agentId);
|
|
1754
1540
|
}
|
|
1755
|
-
// ══════════════════════════════════════════
|
|
1756
|
-
// Fund AgentAccount
|
|
1757
|
-
// ══════════════════════════════════════════
|
|
1758
1541
|
/**
|
|
1759
|
-
*
|
|
1760
|
-
*
|
|
1761
|
-
* @param amount - Human-readable USDC amount (e.g. "50")
|
|
1542
|
+
* Get agent URI (metadata JSON location)
|
|
1762
1543
|
*/
|
|
1763
|
-
async
|
|
1764
|
-
|
|
1765
|
-
const accountAddr = await this.getAccountAddress();
|
|
1766
|
-
const usdc = new import_ethers5.Contract(this.config.contracts.usdc, ERC20_ABI, this.signer);
|
|
1767
|
-
const balance = await usdc.balanceOf(this.signer.address);
|
|
1768
|
-
if (balance < amountWei) {
|
|
1769
|
-
throw new Error(`Insufficient USDC: have $${import_ethers5.ethers.formatUnits(balance, 6)}, need $${amount}`);
|
|
1770
|
-
}
|
|
1771
|
-
const tx = await usdc.transfer(accountAddr, amountWei);
|
|
1772
|
-
await tx.wait();
|
|
1773
|
-
return { tx: tx.hash, amount, agentAccount: accountAddr };
|
|
1544
|
+
async getAgentURI(agentId) {
|
|
1545
|
+
return await this.identityRegistry.tokenURI(agentId);
|
|
1774
1546
|
}
|
|
1775
1547
|
/**
|
|
1776
|
-
*
|
|
1777
|
-
* Call before borrow() or depositAndBorrow() to get a clear error instead of on-chain revert.
|
|
1778
|
-
*
|
|
1779
|
-
* @param tokenSymbol - Collateral token
|
|
1780
|
-
* @param additionalCollateral - Additional collateral being deposited ("0" if just borrowing)
|
|
1781
|
-
* @param borrowAmount - USDC to borrow
|
|
1548
|
+
* Update agent URI
|
|
1782
1549
|
*/
|
|
1783
|
-
async
|
|
1784
|
-
const
|
|
1785
|
-
const
|
|
1786
|
-
|
|
1787
|
-
const marketInfo = await this.morpho.markets(tokenInfo.address);
|
|
1788
|
-
const oracle = new import_ethers5.Contract(marketInfo.params.oracle, _MorphoCreditClient.ORACLE_ABI, this.signer.provider);
|
|
1789
|
-
const oraclePrice = await oracle.price();
|
|
1790
|
-
const addCollateralWei = import_ethers5.ethers.parseUnits(additionalCollateral, tokenInfo.decimals);
|
|
1791
|
-
const borrowWei = import_ethers5.ethers.parseUnits(borrowAmount, 6);
|
|
1792
|
-
const totalCollateral = pos.collateralAmount + addCollateralWei;
|
|
1793
|
-
const totalDebt = pos.borrowedAmount + borrowWei;
|
|
1794
|
-
const collateralValue = totalCollateral * oraclePrice / BigInt("1000000000000000000000000000000000000");
|
|
1795
|
-
const collateralUsd = Number(collateralValue) / 1e6;
|
|
1796
|
-
const currentDebtUsd = Number(pos.borrowedAmount) / 1e6;
|
|
1797
|
-
const maxLtvBps = Number(marketInfo.maxLtvBps);
|
|
1798
|
-
const currentLtv = collateralValue > 0n ? Number(pos.borrowedAmount * 10000n / collateralValue) / 100 : 0;
|
|
1799
|
-
const newLtv = collateralValue > 0n ? Number(totalDebt * 10000n / collateralValue) / 100 : totalDebt > 0n ? Infinity : 0;
|
|
1800
|
-
const maxLtv = maxLtvBps / 100;
|
|
1801
|
-
const maxBorrow = collateralUsd * maxLtvBps / 1e4;
|
|
1802
|
-
const ok = newLtv <= maxLtv;
|
|
1803
|
-
return {
|
|
1804
|
-
ok,
|
|
1805
|
-
currentLtv,
|
|
1806
|
-
newLtv,
|
|
1807
|
-
maxLtv,
|
|
1808
|
-
collateralUsd,
|
|
1809
|
-
currentDebt: currentDebtUsd,
|
|
1810
|
-
maxBorrow,
|
|
1811
|
-
message: ok ? void 0 : `ExceedsMaxLtv: borrowing $${borrowAmount} would push LTV to ${newLtv.toFixed(1)}% (max ${maxLtv}%). Collateral: $${collateralUsd.toFixed(2)}. Current debt: $${currentDebtUsd.toFixed(3)}. Max additional borrow: $${(maxBorrow - currentDebtUsd).toFixed(2)}. Deposit more collateral or borrow less.`
|
|
1812
|
-
};
|
|
1550
|
+
async setAgentURI(agentId, newURI) {
|
|
1551
|
+
const tx = await this.identityRegistry.setAgentURI(agentId, newURI);
|
|
1552
|
+
const receipt = await tx.wait();
|
|
1553
|
+
return receipt.hash;
|
|
1813
1554
|
}
|
|
1814
|
-
// ══════════════════════════════════════════
|
|
1815
|
-
// Flow 3: Deposit collateral only
|
|
1816
|
-
// ══════════════════════════════════════════
|
|
1817
1555
|
/**
|
|
1818
|
-
*
|
|
1819
|
-
* Does NOT borrow — use `borrow()` or `depositAndBorrow()` for that.
|
|
1820
|
-
*
|
|
1821
|
-
* @param tokenSymbol - Collateral token (WETH, wstETH, cbETH)
|
|
1822
|
-
* @param amount - Human-readable amount (e.g. "0.05")
|
|
1556
|
+
* Set on-chain metadata (key-value)
|
|
1823
1557
|
*/
|
|
1824
|
-
async
|
|
1825
|
-
const
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
const
|
|
1831
|
-
return
|
|
1832
|
-
tx: txHash,
|
|
1833
|
-
amount: amountWei,
|
|
1834
|
-
token: tokenSymbol,
|
|
1835
|
-
agentAccount: accountAddr,
|
|
1836
|
-
totalCollateral: pos.collateralAmount
|
|
1837
|
-
};
|
|
1558
|
+
async setMetadata(agentId, key, value) {
|
|
1559
|
+
const tx = await this.identityRegistry.setMetadata(
|
|
1560
|
+
agentId,
|
|
1561
|
+
key,
|
|
1562
|
+
import_ethers4.ethers.toUtf8Bytes(value)
|
|
1563
|
+
);
|
|
1564
|
+
const receipt = await tx.wait();
|
|
1565
|
+
return receipt.hash;
|
|
1838
1566
|
}
|
|
1839
|
-
// ══════════════════════════════════════════
|
|
1840
|
-
// Flow 2: Deposit + borrow in one call
|
|
1841
|
-
// ══════════════════════════════════════════
|
|
1842
1567
|
/**
|
|
1843
|
-
*
|
|
1844
|
-
* USDC lands in the AgentAccount.
|
|
1845
|
-
*
|
|
1846
|
-
* @param tokenSymbol - Collateral token (WETH, wstETH, cbETH)
|
|
1847
|
-
* @param collateralAmount - Human-readable collateral amount (e.g. "0.05")
|
|
1848
|
-
* @param borrowAmount - Human-readable USDC amount to borrow (e.g. "50")
|
|
1568
|
+
* Get on-chain metadata
|
|
1849
1569
|
*/
|
|
1850
|
-
async
|
|
1851
|
-
const
|
|
1852
|
-
|
|
1853
|
-
const borrowWei = import_ethers5.ethers.parseUnits(borrowAmount, 6);
|
|
1854
|
-
const accountAddr = await this.getAccountAddress();
|
|
1855
|
-
const ltvCheck = await this.checkLtv(tokenSymbol, collateralAmount, borrowAmount);
|
|
1856
|
-
if (!ltvCheck.ok) throw new Error(ltvCheck.message);
|
|
1857
|
-
const depositTxHash = await this.approveAndDeposit(accountAddr, tokenInfo, collateralWei);
|
|
1858
|
-
await this.ensureCreditProvider(accountAddr);
|
|
1859
|
-
const account = new import_ethers5.Contract(accountAddr, AGENT_ACCOUNT_ABI, this.signer);
|
|
1860
|
-
const calldata = this.morphoIface.encodeFunctionData("drawWithCollateral", [tokenInfo.address, borrowWei]);
|
|
1861
|
-
const borrowTx = await account.execute(this.config.contracts.morphoCredit, 0, calldata);
|
|
1862
|
-
await borrowTx.wait();
|
|
1863
|
-
const totalDebt = await this.morpho.getTotalDebt(accountAddr);
|
|
1864
|
-
return {
|
|
1865
|
-
depositTx: depositTxHash,
|
|
1866
|
-
borrowTx: borrowTx.hash,
|
|
1867
|
-
collateral: { amount: collateralWei, token: tokenSymbol },
|
|
1868
|
-
borrowed: borrowWei,
|
|
1869
|
-
agentAccount: accountAddr,
|
|
1870
|
-
totalDebt
|
|
1871
|
-
};
|
|
1570
|
+
async getMetadata(agentId, key) {
|
|
1571
|
+
const value = await this.identityRegistry.getMetadata(agentId, key);
|
|
1572
|
+
return import_ethers4.ethers.toUtf8String(value);
|
|
1872
1573
|
}
|
|
1873
|
-
// ══════════════════════════════════════════
|
|
1874
|
-
// Flow 8: Borrow against existing collateral
|
|
1875
|
-
// ══════════════════════════════════════════
|
|
1876
1574
|
/**
|
|
1877
|
-
*
|
|
1878
|
-
* Auto-detects which collateral token has a position.
|
|
1879
|
-
* USDC lands in AgentAccount — ready for x402 payments.
|
|
1880
|
-
*
|
|
1881
|
-
* @param amount - Human-readable USDC amount (e.g. "100")
|
|
1575
|
+
* Transfer agent to new owner
|
|
1882
1576
|
*/
|
|
1883
|
-
async
|
|
1884
|
-
const
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
const pos = await this.morpho.getPosition(accountAddr, info.address);
|
|
1889
|
-
if (pos.collateralAmount > 0n) {
|
|
1890
|
-
activeToken = info;
|
|
1891
|
-
break;
|
|
1892
|
-
}
|
|
1893
|
-
}
|
|
1894
|
-
if (!activeToken) {
|
|
1895
|
-
throw new Error("No collateral deposited. Use deposit() or depositAndBorrow() first.");
|
|
1896
|
-
}
|
|
1897
|
-
const ltvCheck = await this.checkLtv(activeToken.symbol, "0", amount);
|
|
1898
|
-
if (!ltvCheck.ok) throw new Error(ltvCheck.message);
|
|
1899
|
-
await this.ensureCreditProvider(accountAddr);
|
|
1900
|
-
const account = new import_ethers5.Contract(accountAddr, AGENT_ACCOUNT_ABI, this.signer);
|
|
1901
|
-
const calldata = this.morphoIface.encodeFunctionData("drawWithCollateral", [activeToken.address, borrowWei]);
|
|
1902
|
-
const tx = await account.execute(this.config.contracts.morphoCredit, 0, calldata);
|
|
1903
|
-
await tx.wait();
|
|
1904
|
-
const totalDebt = await this.morpho.getTotalDebt(accountAddr);
|
|
1905
|
-
return {
|
|
1906
|
-
tx: tx.hash,
|
|
1907
|
-
amount: borrowWei,
|
|
1908
|
-
agentAccount: accountAddr,
|
|
1909
|
-
totalDebt,
|
|
1910
|
-
collateralToken: activeToken.symbol
|
|
1911
|
-
};
|
|
1577
|
+
async transfer(agentId, to) {
|
|
1578
|
+
const from = await this.signer.getAddress();
|
|
1579
|
+
const tx = await this.identityRegistry.transferFrom(from, to, agentId);
|
|
1580
|
+
const receipt = await tx.wait();
|
|
1581
|
+
return receipt.hash;
|
|
1912
1582
|
}
|
|
1913
|
-
// ══════════════════════════════════════════
|
|
1914
|
-
// Flows 4-7: Sponsor (deposit for another agent)
|
|
1915
|
-
// ══════════════════════════════════════════
|
|
1916
1583
|
/**
|
|
1917
|
-
*
|
|
1918
|
-
* Optionally borrow USDC for the agent (only works if caller owns the AgentAccount).
|
|
1919
|
-
*
|
|
1920
|
-
* Supports both agentId lookup and direct address.
|
|
1921
|
-
*
|
|
1922
|
-
* @param target - `{ agentId: "17676" }` or `{ address: "0x..." }`
|
|
1923
|
-
* @param tokenSymbol - Collateral token
|
|
1924
|
-
* @param amount - Human-readable collateral amount
|
|
1925
|
-
* @param borrowAmount - Optional: USDC to borrow (only if caller is owner)
|
|
1584
|
+
* Fetch and parse agent metadata from URI
|
|
1926
1585
|
*/
|
|
1927
|
-
async
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
accountAddr = await this.getAccountForAgent(target.agentId);
|
|
1935
|
-
} else {
|
|
1936
|
-
accountAddr = target.address;
|
|
1937
|
-
}
|
|
1938
|
-
const depositTxHash = await this.approveAndDeposit(accountAddr, tokenInfo, collateralWei);
|
|
1939
|
-
const result = {
|
|
1940
|
-
depositTx: depositTxHash,
|
|
1941
|
-
targetAccount: accountAddr,
|
|
1942
|
-
targetAgentId,
|
|
1943
|
-
collateral: { amount: collateralWei, token: tokenSymbol },
|
|
1944
|
-
totalCollateral: 0n,
|
|
1945
|
-
totalDebt: 0n
|
|
1946
|
-
};
|
|
1947
|
-
if (borrowAmount) {
|
|
1948
|
-
const borrowWei = import_ethers5.ethers.parseUnits(borrowAmount, 6);
|
|
1949
|
-
try {
|
|
1950
|
-
await this.ensureCreditProvider(accountAddr);
|
|
1951
|
-
const account = new import_ethers5.Contract(accountAddr, AGENT_ACCOUNT_ABI, this.signer);
|
|
1952
|
-
const calldata = this.morphoIface.encodeFunctionData("drawWithCollateral", [tokenInfo.address, borrowWei]);
|
|
1953
|
-
const tx = await account.execute(this.config.contracts.morphoCredit, 0, calldata);
|
|
1954
|
-
await tx.wait();
|
|
1955
|
-
result.borrowTx = tx.hash;
|
|
1956
|
-
result.borrowed = borrowWei;
|
|
1957
|
-
} catch (e) {
|
|
1958
|
-
throw new Error(`Borrow failed (caller may not own this AgentAccount): ${e.message}`);
|
|
1586
|
+
async fetchAgentMetadata(agentId) {
|
|
1587
|
+
try {
|
|
1588
|
+
const uri = await this.getAgentURI(agentId);
|
|
1589
|
+
let fetchUrl = uri;
|
|
1590
|
+
if (uri.startsWith("ipfs://")) {
|
|
1591
|
+
const cid = uri.replace("ipfs://", "");
|
|
1592
|
+
fetchUrl = `https://ipfs.io/ipfs/${cid}`;
|
|
1959
1593
|
}
|
|
1594
|
+
const response = await fetch(fetchUrl);
|
|
1595
|
+
if (!response.ok) return null;
|
|
1596
|
+
return await response.json();
|
|
1597
|
+
} catch {
|
|
1598
|
+
return null;
|
|
1960
1599
|
}
|
|
1961
|
-
const pos = await this.morpho.getPosition(accountAddr, tokenInfo.address);
|
|
1962
|
-
result.totalCollateral = pos.collateralAmount;
|
|
1963
|
-
result.totalDebt = pos.borrowedAmount;
|
|
1964
|
-
return result;
|
|
1965
1600
|
}
|
|
1966
|
-
//
|
|
1967
|
-
// Repay & Withdraw
|
|
1968
|
-
// ══════════════════════════════════════════
|
|
1601
|
+
// ============ Reputation Functions ============
|
|
1969
1602
|
/**
|
|
1970
|
-
*
|
|
1971
|
-
*
|
|
1972
|
-
* @param amount - Human-readable USDC amount (e.g. "50")
|
|
1603
|
+
* Give feedback to an agent
|
|
1973
1604
|
*/
|
|
1974
|
-
async
|
|
1975
|
-
const
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
const
|
|
1992
|
-
|
|
1993
|
-
target: this.config.contracts.usdc,
|
|
1994
|
-
value: 0n,
|
|
1995
|
-
data: this.erc20Iface.encodeFunctionData("approve", [this.config.contracts.morphoCredit, amountWei])
|
|
1996
|
-
},
|
|
1997
|
-
{
|
|
1998
|
-
target: this.config.contracts.morphoCredit,
|
|
1999
|
-
value: 0n,
|
|
2000
|
-
data: this.morphoIface.encodeFunctionData("repayWithCollateral", [collateralAddr, amountWei])
|
|
2001
|
-
}
|
|
2002
|
-
]);
|
|
2003
|
-
const totalDebt = await this.morpho.getTotalDebt(accountAddr);
|
|
2004
|
-
return { tx: txHash, amount: amountWei, remainingDebt: totalDebt };
|
|
1605
|
+
async giveFeedback(input) {
|
|
1606
|
+
const feedbackHash = import_ethers4.ethers.keccak256(
|
|
1607
|
+
import_ethers4.ethers.AbiCoder.defaultAbiCoder().encode(
|
|
1608
|
+
["uint256", "int128", "uint256"],
|
|
1609
|
+
[input.agentId, input.value, Date.now()]
|
|
1610
|
+
)
|
|
1611
|
+
);
|
|
1612
|
+
const tx = await this.reputationRegistry.giveFeedback(
|
|
1613
|
+
input.agentId,
|
|
1614
|
+
input.value,
|
|
1615
|
+
input.decimals || 0,
|
|
1616
|
+
input.tag1 || "",
|
|
1617
|
+
input.tag2 || "",
|
|
1618
|
+
input.endpoint || "",
|
|
1619
|
+
input.feedbackURI || "",
|
|
1620
|
+
feedbackHash
|
|
1621
|
+
);
|
|
1622
|
+
const receipt = await tx.wait();
|
|
1623
|
+
return receipt.hash;
|
|
2005
1624
|
}
|
|
2006
1625
|
/**
|
|
2007
|
-
*
|
|
2008
|
-
*
|
|
2009
|
-
* @param tokenSymbol - Collateral token
|
|
2010
|
-
* @param amount - Human-readable amount or "all"
|
|
1626
|
+
* Give positive feedback (shorthand)
|
|
2011
1627
|
*/
|
|
2012
|
-
async
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
}
|
|
2019
|
-
const withdrawAmount = amount.toLowerCase() === "all" ? pos.collateralAmount : import_ethers5.ethers.parseUnits(amount, tokenInfo.decimals);
|
|
2020
|
-
if (withdrawAmount > pos.collateralAmount) {
|
|
2021
|
-
throw new Error(
|
|
2022
|
-
`Cannot withdraw more than deposited: max ${import_ethers5.ethers.formatUnits(pos.collateralAmount, tokenInfo.decimals)} ${tokenSymbol}`
|
|
2023
|
-
);
|
|
2024
|
-
}
|
|
2025
|
-
const txHash = await this.batch(accountAddr, [
|
|
2026
|
-
{
|
|
2027
|
-
target: this.config.contracts.morphoCredit,
|
|
2028
|
-
value: 0n,
|
|
2029
|
-
data: this.morphoIface.encodeFunctionData("withdrawCollateral", [tokenInfo.address, withdrawAmount])
|
|
2030
|
-
},
|
|
2031
|
-
{
|
|
2032
|
-
target: tokenInfo.address,
|
|
2033
|
-
value: 0n,
|
|
2034
|
-
data: this.erc20Iface.encodeFunctionData("transfer", [this.signer.address, withdrawAmount])
|
|
2035
|
-
}
|
|
2036
|
-
]);
|
|
2037
|
-
const newPos = await this.morpho.getPosition(accountAddr, tokenInfo.address);
|
|
2038
|
-
return {
|
|
2039
|
-
tx: txHash,
|
|
2040
|
-
amount: withdrawAmount,
|
|
2041
|
-
token: tokenSymbol,
|
|
2042
|
-
destination: this.signer.address,
|
|
2043
|
-
remainingCollateral: newPos.collateralAmount
|
|
2044
|
-
};
|
|
1628
|
+
async givePosisitiveFeedback(agentId, value = 100, tags = {}) {
|
|
1629
|
+
return this.giveFeedback({
|
|
1630
|
+
agentId,
|
|
1631
|
+
value: Math.abs(value),
|
|
1632
|
+
...tags
|
|
1633
|
+
});
|
|
2045
1634
|
}
|
|
2046
|
-
// ══════════════════════════════════════════
|
|
2047
|
-
// View methods
|
|
2048
|
-
// ══════════════════════════════════════════
|
|
2049
1635
|
/**
|
|
2050
|
-
*
|
|
1636
|
+
* Give negative feedback (e.g., for defaults)
|
|
2051
1637
|
*/
|
|
2052
|
-
async
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
collateralAmount: pos.collateralAmount,
|
|
2059
|
-
borrowedAmount: pos.borrowedAmount,
|
|
2060
|
-
borrowShares: pos.borrowShares,
|
|
2061
|
-
isActive: pos.isActive
|
|
2062
|
-
};
|
|
1638
|
+
async giveNegativeFeedback(agentId, value = -100, tags = {}) {
|
|
1639
|
+
return this.giveFeedback({
|
|
1640
|
+
agentId,
|
|
1641
|
+
value: -Math.abs(value),
|
|
1642
|
+
...tags
|
|
1643
|
+
});
|
|
2063
1644
|
}
|
|
2064
1645
|
/**
|
|
2065
|
-
*
|
|
1646
|
+
* Record credit default in reputation system
|
|
2066
1647
|
*/
|
|
2067
|
-
async
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
collateralAmount: pos.collateralAmount,
|
|
2076
|
-
borrowedAmount: pos.borrowedAmount,
|
|
2077
|
-
borrowShares: pos.borrowShares,
|
|
2078
|
-
isActive: pos.isActive
|
|
2079
|
-
});
|
|
2080
|
-
}
|
|
2081
|
-
}
|
|
2082
|
-
return positions;
|
|
1648
|
+
async recordCreditDefault(agentId, creditLineId) {
|
|
1649
|
+
return this.giveFeedback({
|
|
1650
|
+
agentId,
|
|
1651
|
+
value: -100,
|
|
1652
|
+
tag1: "credit",
|
|
1653
|
+
tag2: "default",
|
|
1654
|
+
feedbackURI: `agether://default/${creditLineId}`
|
|
1655
|
+
});
|
|
2083
1656
|
}
|
|
2084
1657
|
/**
|
|
2085
|
-
* Get
|
|
1658
|
+
* Get reputation summary for an agent
|
|
2086
1659
|
*/
|
|
2087
|
-
async
|
|
2088
|
-
const
|
|
2089
|
-
|
|
1660
|
+
async getReputation(agentId, tag1 = "", tag2 = "") {
|
|
1661
|
+
const clients = await this.reputationRegistry.getClients(agentId);
|
|
1662
|
+
if (clients.length === 0) {
|
|
1663
|
+
return {
|
|
1664
|
+
count: 0,
|
|
1665
|
+
totalValue: 0,
|
|
1666
|
+
averageValue: 0,
|
|
1667
|
+
clients: []
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
const [count, summaryValue, decimals] = await this.reputationRegistry.getSummary(
|
|
1671
|
+
agentId,
|
|
1672
|
+
clients,
|
|
1673
|
+
tag1,
|
|
1674
|
+
tag2
|
|
1675
|
+
);
|
|
1676
|
+
const divisor = 10 ** Number(decimals);
|
|
1677
|
+
const total = Number(summaryValue) / divisor;
|
|
1678
|
+
return {
|
|
1679
|
+
count: Number(count),
|
|
1680
|
+
totalValue: total,
|
|
1681
|
+
averageValue: Number(count) > 0 ? total / Number(count) : 0,
|
|
1682
|
+
clients: clients.map((c) => c)
|
|
1683
|
+
};
|
|
2090
1684
|
}
|
|
2091
1685
|
/**
|
|
2092
|
-
*
|
|
1686
|
+
* Check if agent has negative credit reputation
|
|
2093
1687
|
*/
|
|
2094
|
-
async
|
|
2095
|
-
const
|
|
2096
|
-
|
|
2097
|
-
return usdc.balanceOf(accountAddr);
|
|
2098
|
-
}
|
|
2099
|
-
/** Get the wallet (signer) address */
|
|
2100
|
-
getAddress() {
|
|
2101
|
-
return this.signer.address;
|
|
2102
|
-
}
|
|
2103
|
-
};
|
|
2104
|
-
_MorphoCreditClient.ERC8004_ABI = [
|
|
2105
|
-
"function register(string agentURI) returns (uint256)",
|
|
2106
|
-
"function ownerOf(uint256 tokenId) view returns (address)",
|
|
2107
|
-
"function balanceOf(address owner) view returns (uint256)",
|
|
2108
|
-
"function tokenOfOwnerByIndex(address owner, uint256 index) view returns (uint256)"
|
|
2109
|
-
];
|
|
2110
|
-
// ══════════════════════════════════════════
|
|
2111
|
-
// LTV Pre-check
|
|
2112
|
-
// ══════════════════════════════════════════
|
|
2113
|
-
_MorphoCreditClient.ORACLE_ABI = ["function price() view returns (uint256)"];
|
|
2114
|
-
var MorphoCreditClient = _MorphoCreditClient;
|
|
2115
|
-
|
|
2116
|
-
// src/clients/WalletClient.ts
|
|
2117
|
-
var import_ethers6 = require("ethers");
|
|
2118
|
-
var WalletClient = class {
|
|
2119
|
-
constructor(config) {
|
|
2120
|
-
this.privateKey = null;
|
|
2121
|
-
this.factoryAddress = config.factoryAddress;
|
|
2122
|
-
this.usdcAddress = config.usdcAddress || import_ethers6.ethers.ZeroAddress;
|
|
2123
|
-
this.chainId = config.chainId;
|
|
2124
|
-
this.rpcUrl = config.rpcUrl;
|
|
2125
|
-
this.provider = new import_ethers6.ethers.JsonRpcProvider(config.rpcUrl);
|
|
2126
|
-
this.privateKey = config.privateKey || null;
|
|
1688
|
+
async hasNegativeCreditReputation(agentId) {
|
|
1689
|
+
const rep = await this.getReputation(agentId, "credit", "");
|
|
1690
|
+
return rep.count > 0 && rep.averageValue < 0;
|
|
2127
1691
|
}
|
|
1692
|
+
// ============ Agether Integration ============
|
|
2128
1693
|
/**
|
|
2129
|
-
*
|
|
1694
|
+
* Verify agent is eligible for Agether credit
|
|
1695
|
+
* Checks:
|
|
1696
|
+
* 1. Agent exists in ERC-8004 registry
|
|
1697
|
+
* 2. Agent has no negative credit reputation
|
|
2130
1698
|
*/
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
}
|
|
2138
|
-
getFactoryContract() {
|
|
2139
|
-
if (this.privateKey) {
|
|
2140
|
-
return new import_ethers6.Contract(this.factoryAddress, ACCOUNT_FACTORY_ABI, this.getFreshSigner());
|
|
2141
|
-
}
|
|
2142
|
-
return new import_ethers6.Contract(this.factoryAddress, ACCOUNT_FACTORY_ABI, this.provider);
|
|
2143
|
-
}
|
|
2144
|
-
getAccountContract(accountAddress) {
|
|
2145
|
-
if (this.privateKey) {
|
|
2146
|
-
return new import_ethers6.Contract(accountAddress, AGENT_ACCOUNT_ABI, this.getFreshSigner());
|
|
2147
|
-
}
|
|
2148
|
-
return new import_ethers6.Contract(accountAddress, AGENT_ACCOUNT_ABI, this.provider);
|
|
2149
|
-
}
|
|
2150
|
-
getChainId() {
|
|
2151
|
-
return this.chainId;
|
|
2152
|
-
}
|
|
2153
|
-
// ============ Account Factory ============
|
|
2154
|
-
async accountExists(agentId) {
|
|
2155
|
-
const factory = this.getFactoryContract();
|
|
2156
|
-
return factory.accountExists(agentId);
|
|
2157
|
-
}
|
|
2158
|
-
async getAccount(agentId) {
|
|
2159
|
-
const factory = this.getFactoryContract();
|
|
2160
|
-
const address = await factory.getAccount(agentId);
|
|
2161
|
-
if (address === import_ethers6.ethers.ZeroAddress) {
|
|
2162
|
-
return null;
|
|
2163
|
-
}
|
|
2164
|
-
return address;
|
|
2165
|
-
}
|
|
2166
|
-
async predictAddress(agentId) {
|
|
2167
|
-
const factory = this.getFactoryContract();
|
|
2168
|
-
return factory.predictAddress(agentId);
|
|
2169
|
-
}
|
|
2170
|
-
async createAccount(agentId) {
|
|
2171
|
-
if (!this.privateKey) {
|
|
2172
|
-
throw new Error("Signer not configured - provide privateKey");
|
|
2173
|
-
}
|
|
2174
|
-
const factory = this.getFactoryContract();
|
|
2175
|
-
const tx = await factory.createAccount(agentId);
|
|
2176
|
-
await tx.wait();
|
|
2177
|
-
const account = await this.getAccount(agentId);
|
|
2178
|
-
if (!account) {
|
|
2179
|
-
throw new Error("Account creation failed");
|
|
1699
|
+
async verifyForCredit(agentId) {
|
|
1700
|
+
let owner;
|
|
1701
|
+
try {
|
|
1702
|
+
owner = await this.getOwner(agentId);
|
|
1703
|
+
} catch {
|
|
1704
|
+
return { eligible: false, reason: "Agent not found in ERC-8004 registry" };
|
|
2180
1705
|
}
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
const account = this.getAccountContract(accountAddress);
|
|
2190
|
-
const [agentId, owner, ethBalance] = await Promise.all([
|
|
2191
|
-
account.agentId(),
|
|
2192
|
-
account.owner(),
|
|
2193
|
-
account.ethBalance()
|
|
2194
|
-
]);
|
|
2195
|
-
let usdcBalance = 0n;
|
|
2196
|
-
if (this.usdcAddress !== import_ethers6.ethers.ZeroAddress) {
|
|
2197
|
-
usdcBalance = await account.balanceOf(this.usdcAddress);
|
|
1706
|
+
const reputation = await this.getReputation(agentId, "credit", "");
|
|
1707
|
+
if (reputation.count > 0 && reputation.averageValue < 0) {
|
|
1708
|
+
return {
|
|
1709
|
+
eligible: false,
|
|
1710
|
+
reason: "Agent has negative credit reputation",
|
|
1711
|
+
owner,
|
|
1712
|
+
reputation
|
|
1713
|
+
};
|
|
2198
1714
|
}
|
|
2199
1715
|
return {
|
|
2200
|
-
|
|
2201
|
-
agentId: BigInt(agentId),
|
|
1716
|
+
eligible: true,
|
|
2202
1717
|
owner,
|
|
2203
|
-
|
|
2204
|
-
usdcBalance: BigInt(usdcBalance)
|
|
2205
|
-
};
|
|
2206
|
-
}
|
|
2207
|
-
async getProviderStatus(accountAddress, creditProvider) {
|
|
2208
|
-
const provider = new import_ethers6.Contract(
|
|
2209
|
-
creditProvider,
|
|
2210
|
-
CREDIT_PROVIDER_ABI,
|
|
2211
|
-
this.provider
|
|
2212
|
-
);
|
|
2213
|
-
const [isEligible, totalDebt, maxDrawable] = await Promise.all([
|
|
2214
|
-
provider.isEligible(accountAddress),
|
|
2215
|
-
provider.getTotalDebt(accountAddress),
|
|
2216
|
-
provider.maxDrawable(accountAddress)
|
|
2217
|
-
]);
|
|
2218
|
-
return {
|
|
2219
|
-
provider: creditProvider,
|
|
2220
|
-
isEligible,
|
|
2221
|
-
totalDebt: BigInt(totalDebt),
|
|
2222
|
-
maxDrawable: BigInt(maxDrawable)
|
|
1718
|
+
reputation
|
|
2223
1719
|
};
|
|
2224
1720
|
}
|
|
2225
|
-
// ============
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
}
|
|
2240
|
-
async withdraw(accountAddress, tokenAddress, amount, to) {
|
|
2241
|
-
if (!this.privateKey) {
|
|
2242
|
-
throw new Error("Signer not configured");
|
|
2243
|
-
}
|
|
2244
|
-
const account = this.getAccountContract(accountAddress);
|
|
2245
|
-
const recipient = to || await this.getFreshSigner().getAddress();
|
|
2246
|
-
const tx = await account.withdraw(tokenAddress, amount, recipient);
|
|
2247
|
-
const receipt = await tx.wait();
|
|
2248
|
-
return receipt.hash;
|
|
2249
|
-
}
|
|
2250
|
-
// ============ Credit Operations ============
|
|
2251
|
-
async drawCredit(accountAddress, creditProvider, amount) {
|
|
2252
|
-
if (!this.privateKey) {
|
|
2253
|
-
throw new Error("Signer not configured");
|
|
2254
|
-
}
|
|
2255
|
-
const account = this.getAccountContract(accountAddress);
|
|
2256
|
-
const tx = await account.drawCredit(creditProvider, amount);
|
|
2257
|
-
const receipt = await tx.wait();
|
|
2258
|
-
return receipt.hash;
|
|
2259
|
-
}
|
|
2260
|
-
async repayCredit(accountAddress, creditProvider, amount) {
|
|
2261
|
-
if (!this.privateKey) {
|
|
2262
|
-
throw new Error("Signer not configured");
|
|
2263
|
-
}
|
|
2264
|
-
const account = this.getAccountContract(accountAddress);
|
|
2265
|
-
const tx = await account.repayCredit(creditProvider, amount);
|
|
2266
|
-
const receipt = await tx.wait();
|
|
2267
|
-
return receipt.hash;
|
|
2268
|
-
}
|
|
2269
|
-
// ============ Generic Execute ============
|
|
2270
|
-
async execute(accountAddress, target, value, data) {
|
|
2271
|
-
if (!this.privateKey) {
|
|
2272
|
-
throw new Error("Signer not configured");
|
|
1721
|
+
// ============ Helpers ============
|
|
1722
|
+
parseAgentIdFromReceipt(receipt) {
|
|
1723
|
+
for (const log of receipt.logs) {
|
|
1724
|
+
try {
|
|
1725
|
+
const parsed = this.identityRegistry.interface.parseLog({
|
|
1726
|
+
topics: log.topics,
|
|
1727
|
+
data: log.data
|
|
1728
|
+
});
|
|
1729
|
+
if (parsed?.name === "Transfer") {
|
|
1730
|
+
return parsed.args[2];
|
|
1731
|
+
}
|
|
1732
|
+
} catch {
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
2273
1735
|
}
|
|
2274
|
-
|
|
2275
|
-
const tx = await account.execute(target, value, data);
|
|
2276
|
-
const receipt = await tx.wait();
|
|
2277
|
-
return receipt.hash;
|
|
1736
|
+
return 0n;
|
|
2278
1737
|
}
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
1738
|
+
/**
|
|
1739
|
+
* Get contract addresses
|
|
1740
|
+
*/
|
|
1741
|
+
getContractAddresses() {
|
|
1742
|
+
return {
|
|
1743
|
+
identity: this.identityRegistry.target,
|
|
1744
|
+
reputation: this.reputationRegistry.target
|
|
1745
|
+
};
|
|
2287
1746
|
}
|
|
2288
1747
|
};
|
|
2289
1748
|
|
|
@@ -2335,24 +1794,18 @@ function rateToBps(rate) {
|
|
|
2335
1794
|
AGENT_ACCOUNT_ABI,
|
|
2336
1795
|
AGENT_REPUTATION_ABI,
|
|
2337
1796
|
AgentIdentityClient,
|
|
1797
|
+
AgentNotApprovedError,
|
|
2338
1798
|
AgetherClient,
|
|
2339
1799
|
AgetherError,
|
|
2340
|
-
CREDIT_PROVIDER_ABI,
|
|
2341
1800
|
ChainId,
|
|
2342
|
-
CreditNotActiveError,
|
|
2343
|
-
CreditStatus,
|
|
2344
1801
|
ERC20_ABI,
|
|
2345
1802
|
IDENTITY_REGISTRY_ABI,
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
MorphoCreditClient,
|
|
2350
|
-
REPUTATION_CREDIT_ABI,
|
|
1803
|
+
InsufficientBalanceError,
|
|
1804
|
+
MORPHO_BLUE_ABI,
|
|
1805
|
+
MorphoClient,
|
|
2351
1806
|
ScoringClient,
|
|
2352
1807
|
ScoringRejectedError,
|
|
2353
1808
|
VALIDATION_REGISTRY_ABI,
|
|
2354
|
-
VaultClient,
|
|
2355
|
-
WalletClient,
|
|
2356
1809
|
X402Client,
|
|
2357
1810
|
bpsToRate,
|
|
2358
1811
|
createConfig,
|
|
@@ -2364,7 +1817,6 @@ function rateToBps(rate) {
|
|
|
2364
1817
|
formatUSD,
|
|
2365
1818
|
formatUnits,
|
|
2366
1819
|
getDefaultConfig,
|
|
2367
|
-
getUSDCAddress,
|
|
2368
1820
|
parseUnits,
|
|
2369
1821
|
rateToBps
|
|
2370
1822
|
});
|