wallet-agent-ai-radix 1.0.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/LICENSE +13 -0
- package/README.md +284 -0
- package/dist/agent.d.ts +2 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +158 -0
- package/dist/agent.js.map +1 -0
- package/dist/cli/derive.d.ts +5 -0
- package/dist/cli/derive.d.ts.map +1 -0
- package/dist/cli/derive.js +78 -0
- package/dist/cli/derive.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +32 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +171 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/recover.d.ts +2 -0
- package/dist/cli/recover.d.ts.map +1 -0
- package/dist/cli/recover.js +112 -0
- package/dist/cli/recover.js.map +1 -0
- package/dist/cli/unlock.d.ts +3 -0
- package/dist/cli/unlock.d.ts.map +1 -0
- package/dist/cli/unlock.js +70 -0
- package/dist/cli/unlock.js.map +1 -0
- package/dist/core/AGGRClient.d.ts +19 -0
- package/dist/core/AGGRClient.d.ts.map +1 -0
- package/dist/core/AGGRClient.js +103 -0
- package/dist/core/AGGRClient.js.map +1 -0
- package/dist/core/AgentWallet.d.ts +64 -0
- package/dist/core/AgentWallet.d.ts.map +1 -0
- package/dist/core/AgentWallet.js +658 -0
- package/dist/core/AgentWallet.js.map +1 -0
- package/dist/core/RadixClient.d.ts +15 -0
- package/dist/core/RadixClient.d.ts.map +1 -0
- package/dist/core/RadixClient.js +112 -0
- package/dist/core/RadixClient.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/keystore/Keystore.d.ts +17 -0
- package/dist/keystore/Keystore.d.ts.map +1 -0
- package/dist/keystore/Keystore.js +97 -0
- package/dist/keystore/Keystore.js.map +1 -0
- package/dist/tools/LangChainTools.d.ts +19 -0
- package/dist/tools/LangChainTools.d.ts.map +1 -0
- package/dist/tools/LangChainTools.js +613 -0
- package/dist/tools/LangChainTools.js.map +1 -0
- package/dist/types/index.d.ts +104 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +6 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentWallet = void 0;
|
|
4
|
+
const AGGRClient_1 = require("./AGGRClient");
|
|
5
|
+
const RadixClient_1 = require("./RadixClient");
|
|
6
|
+
// Direcciones de los assets en cada red
|
|
7
|
+
const ASSET_ADDRESSES = {
|
|
8
|
+
mainnet: {
|
|
9
|
+
XRD: "resource_rdx1tknxxxxxxxxxradxrdxxxxxxxxx009923554798xxxxxxxxxradxrd",
|
|
10
|
+
HWBTC: "resource_rdx1t58kkcqdz0mavfz98m98qh9m4jexyl9tacsvlhns6yxs4r6hrm5re5",
|
|
11
|
+
HUSDC: "resource_rdx1thxj9m87sn5cc9ehgp9qxp6vzeqxtce90xm5cp33373tclyp4et4gv",
|
|
12
|
+
HETH: "resource_rdx1th09yvv7tgsrv708ffsgqjjf2mhy84mscmj5jwu4g670fh3e5zgef0",
|
|
13
|
+
HUSDT: "resource_rdx1th4v03gezwgzkuma6p38lnum8ww8t4ds9nvcrkr2p9ft6kxx3kxvhe",
|
|
14
|
+
},
|
|
15
|
+
stokenet: {
|
|
16
|
+
XRD: "resource_tdx_2_1tknxxxxxxxxxradxrdxxxxxxxxx009923554798xxxxxxxxxtfd2jc",
|
|
17
|
+
HWBTC: "resource_tdx_2_1tkshqwxsg524asnfd5e6d27dwc27sywtdmlrpgp63uwdwk90e9sf7h",
|
|
18
|
+
HUSDC: "resource_tdx_2_1tkvlvu3zekq5dj8n34cg4swlnj6mwdkcrcal2wf54kwehwx02ly5my",
|
|
19
|
+
HETH: "resource_tdx_2_1tk76lm84jurun672nkn6893hjsfwyp9wd7qz0yxha5dvu5h72svtzq",
|
|
20
|
+
HUSDT: "resource_tdx_2_1tk6g9gwdph40v8sd8qfte0lgftc7g8xxqkz86dtynwm6x72zm6ezce",
|
|
21
|
+
},
|
|
22
|
+
localnet: {
|
|
23
|
+
XRD: "resource_sim1tknxxxxxxxxxradxrdxxxxxxxxx009923554798xxxxxxxxxakj8n3",
|
|
24
|
+
HUSDC: "resource_sim1t_husdc",
|
|
25
|
+
HWBTC: "resource_sim1t_hwbtc",
|
|
26
|
+
HETH: "resource_sim1t_heth",
|
|
27
|
+
HUSDT: "resource_sim1t_husdt",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
// Configuración de red
|
|
31
|
+
const NETWORK_CONFIG = {
|
|
32
|
+
mainnet: {
|
|
33
|
+
gatewayUrl: "https://mainnet.radixdlt.com",
|
|
34
|
+
networkId: 0x01,
|
|
35
|
+
},
|
|
36
|
+
stokenet: {
|
|
37
|
+
gatewayUrl: "https://stokenet.radixdlt.com",
|
|
38
|
+
networkId: 0x02,
|
|
39
|
+
},
|
|
40
|
+
localnet: {
|
|
41
|
+
gatewayUrl: "http://localhost:8080",
|
|
42
|
+
networkId: 0xf2,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
class AgentWallet {
|
|
46
|
+
constructor(config) {
|
|
47
|
+
const network = config.network ?? "mainnet";
|
|
48
|
+
if (!NETWORK_CONFIG[network]) {
|
|
49
|
+
throw new Error(`[AgentWallet] Invalid network "${network}". Valid options: mainnet, stokenet, localnet`);
|
|
50
|
+
}
|
|
51
|
+
this.config = { ...config, network };
|
|
52
|
+
this.networkConfig = NETWORK_CONFIG[network];
|
|
53
|
+
this.assetAddresses = ASSET_ADDRESSES[network];
|
|
54
|
+
this.activeOrders = new Map();
|
|
55
|
+
this.customAssets = new Map();
|
|
56
|
+
if (config.aggrConfig) {
|
|
57
|
+
this.aggrClient = new AGGRClient_1.AGGRClient(config.aggrConfig);
|
|
58
|
+
}
|
|
59
|
+
if (config.privateKey) {
|
|
60
|
+
this.radixClient = new RadixClient_1.RadixClient(config.privateKey, this.networkConfig);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.radixClient = RadixClient_1.RadixClient.fromKeystore(this.networkConfig);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async checkFunds() {
|
|
67
|
+
try {
|
|
68
|
+
const config = await this.getConfig();
|
|
69
|
+
const balance = await this.getBalance();
|
|
70
|
+
const warnings = [];
|
|
71
|
+
const xrdBalance = balance.balances["XRD"] ?? 0;
|
|
72
|
+
const threshold = config.maxPerTransaction + 10;
|
|
73
|
+
if (xrdBalance < threshold) {
|
|
74
|
+
warnings.push(`Low funds in component: ${xrdBalance} XRD. Minimum recommended: ${threshold} XRD`);
|
|
75
|
+
}
|
|
76
|
+
if (warnings.length > 0 && this.config.onLowFunds) {
|
|
77
|
+
this.config.onLowFunds(warnings);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
console.error("[AgentWallet] checkFunds error:", err);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Obtiene la dirección del resource de un asset
|
|
85
|
+
getAssetAddress(asset) {
|
|
86
|
+
// Primero busca en assets registrados por el usuario
|
|
87
|
+
const custom = this.customAssets.get(asset.toUpperCase());
|
|
88
|
+
if (custom) {
|
|
89
|
+
const address = custom[this.config.network];
|
|
90
|
+
if (address)
|
|
91
|
+
return address;
|
|
92
|
+
throw new Error(`[AgentWallet] Asset ${asset} has no address for network ${this.config.network}`);
|
|
93
|
+
}
|
|
94
|
+
// Si no encuentra busca en los assets por defecto
|
|
95
|
+
const defaults = ASSET_ADDRESSES[this.config.network];
|
|
96
|
+
const address = defaults[asset];
|
|
97
|
+
if (!address) {
|
|
98
|
+
throw new Error(`[AgentWallet] Asset ${asset} not found. Use registerAsset() to add custom assets.`);
|
|
99
|
+
}
|
|
100
|
+
return address;
|
|
101
|
+
}
|
|
102
|
+
// El usuario registra un asset custom con sus addresses por red
|
|
103
|
+
registerAsset(symbol, addresses) {
|
|
104
|
+
const upper = symbol.toUpperCase();
|
|
105
|
+
if (ASSET_ADDRESSES[this.config.network][upper]) {
|
|
106
|
+
throw new Error(`[AgentWallet] Asset ${upper} already exists as a default asset.`);
|
|
107
|
+
}
|
|
108
|
+
this.customAssets.set(upper, addresses);
|
|
109
|
+
console.log(`[AgentWallet] Asset ${upper} registered successfully.`);
|
|
110
|
+
}
|
|
111
|
+
// Lista todos los assets disponibles — default + custom
|
|
112
|
+
listAssets() {
|
|
113
|
+
const defaults = Object.keys(ASSET_ADDRESSES[this.config.network]);
|
|
114
|
+
const custom = Array.from(this.customAssets.keys());
|
|
115
|
+
return [...defaults, ...custom];
|
|
116
|
+
}
|
|
117
|
+
// Consulta el balance del PolicyVault
|
|
118
|
+
async getBalance() {
|
|
119
|
+
const response = await fetch(`${this.networkConfig.gatewayUrl}/state/entity/details`, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: { "Content-Type": "application/json" },
|
|
122
|
+
body: JSON.stringify({
|
|
123
|
+
addresses: [this.config.componentAddress],
|
|
124
|
+
aggregation_level: "Vault",
|
|
125
|
+
opt_ins: {
|
|
126
|
+
explicit_metadata: ["name", "symbol"]
|
|
127
|
+
}
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
const data = await response.json();
|
|
131
|
+
const fungibles = data?.items?.[0]?.fungible_resources?.items || [];
|
|
132
|
+
const balances = {};
|
|
133
|
+
for (const resource of fungibles) {
|
|
134
|
+
const metadata = resource.explicit_metadata?.items || [];
|
|
135
|
+
const symbol = metadata.find((m) => m.key === "symbol")?.value?.typed?.value;
|
|
136
|
+
const amount = parseFloat(resource.vaults?.items?.[0]?.amount || "0");
|
|
137
|
+
if (symbol && amount > 0) {
|
|
138
|
+
balances[symbol] = amount;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
balances,
|
|
143
|
+
componentAddress: this.config.componentAddress,
|
|
144
|
+
timestamp: new Date(),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
// Transfiere assets a una dirección — el agente presenta su badge automáticamente
|
|
148
|
+
async transfer(params) {
|
|
149
|
+
const assetAddress = this.getAssetAddress(params.asset);
|
|
150
|
+
const badgeLocalId = this.config.badgeLocalId ?? "#1#";
|
|
151
|
+
// 1. Obtener configuración actual del contrato
|
|
152
|
+
const config = await this.getConfig();
|
|
153
|
+
// 2. VALIDAR FROZEN
|
|
154
|
+
if (config.frozen) {
|
|
155
|
+
throw new Error(`[AgentWallet] Contract is frozen. No transfers allowed.`);
|
|
156
|
+
}
|
|
157
|
+
// 3. VALIDAR MULTISIG THRESHOLD PRIMERO
|
|
158
|
+
// Si supera el umbral del owner — necesita aprobación del owner
|
|
159
|
+
let manifest;
|
|
160
|
+
if (config.multisigThreshold && params.amount > config.multisigThreshold) {
|
|
161
|
+
console.log(`[AgentWallet] Amount ${params.amount} exceeds multisig threshold (${config.multisigThreshold}). Requesting owner approval...`);
|
|
162
|
+
await this.requestLargeTransfer({
|
|
163
|
+
to: params.to,
|
|
164
|
+
amount: params.amount,
|
|
165
|
+
asset: params.asset,
|
|
166
|
+
reason: params.reason,
|
|
167
|
+
});
|
|
168
|
+
return {
|
|
169
|
+
txId: "pending_owner_approval",
|
|
170
|
+
amount: params.amount,
|
|
171
|
+
asset: params.asset,
|
|
172
|
+
to: params.to,
|
|
173
|
+
reason: params.reason,
|
|
174
|
+
timestamp: new Date(),
|
|
175
|
+
success: false,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// 4. VALIDAR MAX PER TRANSACTION
|
|
179
|
+
// Si supera el límite del agente pero NO el del owner — rechazar
|
|
180
|
+
else if (params.amount > config.maxPerTransaction) {
|
|
181
|
+
throw new Error(`[AgentWallet] Amount ${params.amount} exceeds max per transaction (${config.maxPerTransaction}). ` +
|
|
182
|
+
`For larger transfers up to ${config.multisigThreshold}, owner approval is required. ` +
|
|
183
|
+
`Use requestOwnerApproval() instead.`);
|
|
184
|
+
}
|
|
185
|
+
// 5. TRANSFERENCIA NORMAL — dentro de los límites del agente
|
|
186
|
+
else {
|
|
187
|
+
console.log(`[AgentWallet] Amount ${params.amount} within agent limits. Normal transfer.`);
|
|
188
|
+
manifest = this.buildTransferManifest(params.to, assetAddress, params.amount, this.config.badgeResourceAddress, badgeLocalId);
|
|
189
|
+
}
|
|
190
|
+
// 5. Enviar la transacción
|
|
191
|
+
let txId;
|
|
192
|
+
try {
|
|
193
|
+
txId = await this.submitManifest(manifest);
|
|
194
|
+
console.log("[AgentWallet] TX submitted:", txId);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
console.error("[AgentWallet] submitManifest ERROR:", err);
|
|
198
|
+
throw err;
|
|
199
|
+
}
|
|
200
|
+
this.checkFunds().catch(console.error);
|
|
201
|
+
return {
|
|
202
|
+
txId,
|
|
203
|
+
amount: params.amount,
|
|
204
|
+
asset: params.asset,
|
|
205
|
+
to: params.to,
|
|
206
|
+
reason: params.reason,
|
|
207
|
+
timestamp: new Date(),
|
|
208
|
+
success: true,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
// Agent need these to know the limits
|
|
212
|
+
async getConfig() {
|
|
213
|
+
try {
|
|
214
|
+
// Usar el endpoint correcto
|
|
215
|
+
const response = await fetch(`${this.networkConfig.gatewayUrl}/state/entity/details`, {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers: { "Content-Type": "application/json" },
|
|
218
|
+
body: JSON.stringify({
|
|
219
|
+
addresses: [this.config.componentAddress],
|
|
220
|
+
}),
|
|
221
|
+
});
|
|
222
|
+
const data = await response.json();
|
|
223
|
+
// Extraer los fields del state
|
|
224
|
+
const fields = data?.items?.[0]?.details?.state?.fields ?? [];
|
|
225
|
+
// Buscar los valores por field_name
|
|
226
|
+
let maxPerTransaction = 100;
|
|
227
|
+
let multisigThreshold = 500;
|
|
228
|
+
let dailyCap = 1000;
|
|
229
|
+
let frozen = false;
|
|
230
|
+
for (const field of fields) {
|
|
231
|
+
switch (field.field_name) {
|
|
232
|
+
case "max_per_transaction":
|
|
233
|
+
maxPerTransaction = parseFloat(field.value);
|
|
234
|
+
break;
|
|
235
|
+
case "multisig_threshold":
|
|
236
|
+
multisigThreshold = parseFloat(field.value);
|
|
237
|
+
break;
|
|
238
|
+
case "daily_cap":
|
|
239
|
+
dailyCap = parseFloat(field.value);
|
|
240
|
+
break;
|
|
241
|
+
case "frozen":
|
|
242
|
+
frozen = field.value === true;
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
console.log(" ✅ Config get from agent component:");
|
|
247
|
+
console.log(` 📈 Max for transaction: ${maxPerTransaction}`);
|
|
248
|
+
console.log(` 🔐 Multisig level: ${multisigThreshold}`);
|
|
249
|
+
console.log(` 💰 Dairy limit: ${dailyCap}`);
|
|
250
|
+
console.log(` ❄️ Frozen: ${frozen}`);
|
|
251
|
+
return {
|
|
252
|
+
maxPerTransaction,
|
|
253
|
+
multisigThreshold,
|
|
254
|
+
dailyCap,
|
|
255
|
+
frozen,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
console.error("❌ Error when consult the endpoint:", error);
|
|
260
|
+
}
|
|
261
|
+
// Fallback en caso de error
|
|
262
|
+
return {
|
|
263
|
+
maxPerTransaction: 100,
|
|
264
|
+
multisigThreshold: 500,
|
|
265
|
+
dailyCap: 1000,
|
|
266
|
+
frozen: false,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
// ─── Pending Transfer — Large TX que requiere aprobación del owner ─────────
|
|
270
|
+
async requestLargeTransfer(params) {
|
|
271
|
+
const assetAddress = this.getAssetAddress(params.asset);
|
|
272
|
+
const badgeLocalId = this.config.badgeLocalId ?? "#1#";
|
|
273
|
+
const notarizer = this.config.notarizerAddress ?? RadixClient_1.RadixClient.getAddress();
|
|
274
|
+
const manifest = `
|
|
275
|
+
CALL_METHOD
|
|
276
|
+
Address("${notarizer}")
|
|
277
|
+
"lock_fee"
|
|
278
|
+
Decimal("2")
|
|
279
|
+
;
|
|
280
|
+
CALL_METHOD
|
|
281
|
+
Address("${this.config.componentAddress}")
|
|
282
|
+
"lock_fee"
|
|
283
|
+
Decimal("8")
|
|
284
|
+
;
|
|
285
|
+
CALL_METHOD
|
|
286
|
+
Address("${notarizer}")
|
|
287
|
+
"create_proof_of_non_fungibles"
|
|
288
|
+
Address("${this.config.badgeResourceAddress}")
|
|
289
|
+
Array<NonFungibleLocalId>(NonFungibleLocalId("${badgeLocalId}"))
|
|
290
|
+
;
|
|
291
|
+
POP_FROM_AUTH_ZONE
|
|
292
|
+
Proof("agent_proof")
|
|
293
|
+
;
|
|
294
|
+
CALL_METHOD
|
|
295
|
+
Address("${this.config.componentAddress}")
|
|
296
|
+
"request_large_transfer"
|
|
297
|
+
Address("${params.to}")
|
|
298
|
+
Decimal("${params.amount}")
|
|
299
|
+
Address("${assetAddress}")
|
|
300
|
+
"${params.reason}"
|
|
301
|
+
Proof("agent_proof")
|
|
302
|
+
;
|
|
303
|
+
`;
|
|
304
|
+
await this.submitManifest(manifest);
|
|
305
|
+
this.checkFunds().catch(console.error);
|
|
306
|
+
console.log(`[AgentWallet] Large transfer requested:`);
|
|
307
|
+
console.log(` To: ${params.to}`);
|
|
308
|
+
console.log(` Amount: ${params.amount} ${params.asset}`);
|
|
309
|
+
console.log(` Reason: ${params.reason}`);
|
|
310
|
+
console.log(` ⚠️ Owner approval required — check the web dashboard`);
|
|
311
|
+
return {
|
|
312
|
+
requested: true,
|
|
313
|
+
to: params.to,
|
|
314
|
+
amount: params.amount,
|
|
315
|
+
asset: params.asset,
|
|
316
|
+
reason: params.reason,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
async getPendingTransfer() {
|
|
320
|
+
// CALL_METHOD
|
|
321
|
+
// Address("${this.config.agentAccountAddress}")
|
|
322
|
+
// "create_proof_of_amount"
|
|
323
|
+
// Address("${this.config.ownerBadgeAddress}")
|
|
324
|
+
// Decimal("1")
|
|
325
|
+
// ;
|
|
326
|
+
const manifest = `
|
|
327
|
+
CALL_METHOD
|
|
328
|
+
Address("${this.config.componentAddress}")
|
|
329
|
+
"get_pending_transfer"
|
|
330
|
+
;
|
|
331
|
+
`;
|
|
332
|
+
const response = await fetch(`${this.networkConfig.gatewayUrl}/transaction/preview`, {
|
|
333
|
+
method: "POST",
|
|
334
|
+
headers: { "Content-Type": "application/json" },
|
|
335
|
+
body: JSON.stringify({
|
|
336
|
+
manifest,
|
|
337
|
+
start_epoch_inclusive: 1,
|
|
338
|
+
end_epoch_exclusive: 100,
|
|
339
|
+
nonce: 1,
|
|
340
|
+
signer_public_keys: [],
|
|
341
|
+
notary_public_key: {
|
|
342
|
+
key_type: "EddsaEd25519",
|
|
343
|
+
key_hex: "0000000000000000000000000000000000000000000000000000000000000001"
|
|
344
|
+
},
|
|
345
|
+
notary_is_signatory: true,
|
|
346
|
+
tip_percentage: 0,
|
|
347
|
+
flags: {
|
|
348
|
+
use_free_credit: true,
|
|
349
|
+
assume_all_signature_proofs: true,
|
|
350
|
+
skip_epoch_check: true
|
|
351
|
+
}
|
|
352
|
+
})
|
|
353
|
+
});
|
|
354
|
+
const data = await response.json();
|
|
355
|
+
//const output = data?.receipt?.output?.[1]?.programmatic_json;
|
|
356
|
+
const output = data?.receipt?.output?.[0]?.programmatic_json;
|
|
357
|
+
// variant_id "0" = None, "1" = Some
|
|
358
|
+
if (!output || output.variant_id === "0") {
|
|
359
|
+
return { hasPending: false };
|
|
360
|
+
}
|
|
361
|
+
const fields = output?.fields?.[0]?.fields;
|
|
362
|
+
return {
|
|
363
|
+
hasPending: true,
|
|
364
|
+
to: fields?.[0]?.value,
|
|
365
|
+
amount: fields?.[1]?.value,
|
|
366
|
+
asset: fields?.[2]?.value,
|
|
367
|
+
reason: fields?.[3]?.value,
|
|
368
|
+
requestedAtEpoch: fields?.[4]?.value,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
//Ejecuta un swap a través del AGGR
|
|
372
|
+
async swap(params) {
|
|
373
|
+
if (!this.aggrClient) {
|
|
374
|
+
throw new Error("[AgentWallet] AGGR not configured. Pass aggrConfig to use swap.");
|
|
375
|
+
}
|
|
376
|
+
const fromAddress = this.getAssetAddress(params.fromAsset);
|
|
377
|
+
const toAddress = this.getAssetAddress(params.toAsset);
|
|
378
|
+
const badgeLocalId = this.config.badgeLocalId ?? "#1#";
|
|
379
|
+
// Descuentamos el fee del PolicyVault (0.1%) antes de llamar al AGGR
|
|
380
|
+
// para que el monto que llega al AGGR sea el real disponible
|
|
381
|
+
const FEE_RATE = 0.001;
|
|
382
|
+
const adjustedAmount = params.amount * (1 - FEE_RATE);
|
|
383
|
+
// Llamamos al AGGR con el monto ajustado y el PolicyVault como fromAddress
|
|
384
|
+
const quote = await this.aggrClient.getSwapQuote(fromAddress, toAddress, adjustedAmount, this.config.componentAddress);
|
|
385
|
+
// VERSION MOCK
|
|
386
|
+
//console.log("=== AGGR MANIFEST RAW ===");
|
|
387
|
+
//console.log(quote.manifest);
|
|
388
|
+
if (this.config.aggrConfig?.mockMode) {
|
|
389
|
+
console.log("[AgentWallet] SWAP MOCK — simulando resultado sin TX");
|
|
390
|
+
return {
|
|
391
|
+
txId: "mock_swap_" + Date.now(),
|
|
392
|
+
fromAsset: params.fromAsset,
|
|
393
|
+
toAsset: params.toAsset,
|
|
394
|
+
amountIn: params.amount,
|
|
395
|
+
amountOut: quote.outputTokens,
|
|
396
|
+
executedPrice: quote.outputTokens / params.amount,
|
|
397
|
+
reason: params.reason,
|
|
398
|
+
timestamp: new Date(),
|
|
399
|
+
success: true,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
// FIN MOCK
|
|
403
|
+
const aggrManifest = quote.manifest;
|
|
404
|
+
//
|
|
405
|
+
const withdrawPattern = /CALL_METHOD\s+Address\("[^"]+"\)\s+"withdraw"[^;]+;/;
|
|
406
|
+
const manifestWithoutWithdraw = aggrManifest.replace(withdrawPattern, "");
|
|
407
|
+
const notarizer = this.config.notarizerAddress ?? RadixClient_1.RadixClient.getAddress();
|
|
408
|
+
// Construimos el resto manifiesto
|
|
409
|
+
const manifest = `
|
|
410
|
+
CALL_METHOD
|
|
411
|
+
Address("${notarizer}")
|
|
412
|
+
"lock_fee"
|
|
413
|
+
Decimal("10")
|
|
414
|
+
;
|
|
415
|
+
CALL_METHOD
|
|
416
|
+
Address("${this.config.componentAddress}")
|
|
417
|
+
"lock_fee"
|
|
418
|
+
Decimal("8")
|
|
419
|
+
;
|
|
420
|
+
CALL_METHOD
|
|
421
|
+
Address("${notarizer}")
|
|
422
|
+
"create_proof_of_non_fungibles"
|
|
423
|
+
Address("${this.config.badgeResourceAddress}")
|
|
424
|
+
Array<NonFungibleLocalId>(NonFungibleLocalId("${badgeLocalId}"))
|
|
425
|
+
;
|
|
426
|
+
POP_FROM_AUTH_ZONE
|
|
427
|
+
Proof("agent_proof")
|
|
428
|
+
;
|
|
429
|
+
CALL_METHOD
|
|
430
|
+
Address("${this.config.componentAddress}")
|
|
431
|
+
"transfer"
|
|
432
|
+
Address("${this.config.componentAddress}")
|
|
433
|
+
Decimal("${params.amount}")
|
|
434
|
+
Address("${fromAddress}")
|
|
435
|
+
"swap ${params.fromAsset} to ${params.toAsset}"
|
|
436
|
+
Proof("agent_proof")
|
|
437
|
+
;
|
|
438
|
+
${manifestWithoutWithdraw}
|
|
439
|
+
`;
|
|
440
|
+
const txId = await this.submitManifest(manifest);
|
|
441
|
+
this.checkFunds().catch(console.error);
|
|
442
|
+
return {
|
|
443
|
+
txId,
|
|
444
|
+
fromAsset: params.fromAsset,
|
|
445
|
+
toAsset: params.toAsset,
|
|
446
|
+
amountIn: params.amount,
|
|
447
|
+
amountOut: quote.outputTokens,
|
|
448
|
+
executedPrice: quote.outputTokens / params.amount,
|
|
449
|
+
reason: params.reason,
|
|
450
|
+
timestamp: new Date(),
|
|
451
|
+
success: true,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
// Crea una orden condicional — ejecuta cuando se cumple el precio trigger
|
|
455
|
+
async createConditionalOrder(order) {
|
|
456
|
+
if (!this.aggrClient) {
|
|
457
|
+
throw new Error("[AgentWallet] AGGR not configured. Pass aggrConfig to use conditional orders.");
|
|
458
|
+
}
|
|
459
|
+
const orderId = `order_${Date.now()}`;
|
|
460
|
+
const fromAddress = this.getAssetAddress(order.asset);
|
|
461
|
+
const toAddress = this.getAssetAddress(order.againstAsset);
|
|
462
|
+
const cancelWatch = await this.aggrClient.watchPrice(fromAddress, toAddress, order.amount, this.config.componentAddress, order.triggerPrice, order.condition, 5000, async (quote) => {
|
|
463
|
+
console.log(`[AgentWallet] Order ${orderId} triggered at price ${quote.outputTokens / order.amount}`);
|
|
464
|
+
// Cancela el monitor antes de ejecutar
|
|
465
|
+
this.cancelOrder(orderId);
|
|
466
|
+
// Mock — no ejecutar swap real
|
|
467
|
+
if (this.config.aggrConfig?.mockMode) {
|
|
468
|
+
console.log(`[AgentWallet] MOCK order ${orderId} triggered`);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
// FIN MOCK
|
|
472
|
+
await this.swap({
|
|
473
|
+
fromAsset: order.asset,
|
|
474
|
+
toAsset: order.againstAsset,
|
|
475
|
+
amount: order.amount,
|
|
476
|
+
maxSlippage: order.maxSlippage,
|
|
477
|
+
reason: `conditional order ${orderId} triggered at price ${quote.outputTokens / order.amount}`,
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
// Guardamos la función de cancelación
|
|
481
|
+
this.activeOrders.set(orderId, cancelWatch);
|
|
482
|
+
return orderId;
|
|
483
|
+
}
|
|
484
|
+
// Cancela una orden condicional activa
|
|
485
|
+
cancelOrder(orderId) {
|
|
486
|
+
const cancel = this.activeOrders.get(orderId);
|
|
487
|
+
if (cancel) {
|
|
488
|
+
cancel();
|
|
489
|
+
this.activeOrders.delete(orderId);
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
// Lista las órdenes activas
|
|
495
|
+
listActiveOrders() {
|
|
496
|
+
return Array.from(this.activeOrders.keys());
|
|
497
|
+
}
|
|
498
|
+
// Construye el manifiesto de transferencia desde el PolicyVault
|
|
499
|
+
buildTransferManifest(to, assetAddress, amount, badgeResourceAddress, badgeLocalId = "#1#") {
|
|
500
|
+
const notarizer = this.config.notarizerAddress ?? RadixClient_1.RadixClient.getAddress();
|
|
501
|
+
return `
|
|
502
|
+
CALL_METHOD
|
|
503
|
+
Address("${notarizer}")
|
|
504
|
+
"lock_fee"
|
|
505
|
+
Decimal("2")
|
|
506
|
+
;
|
|
507
|
+
CALL_METHOD
|
|
508
|
+
Address("${this.config.componentAddress}")
|
|
509
|
+
"lock_fee"
|
|
510
|
+
Decimal("8")
|
|
511
|
+
;
|
|
512
|
+
CALL_METHOD
|
|
513
|
+
Address("${notarizer}")
|
|
514
|
+
"create_proof_of_non_fungibles"
|
|
515
|
+
Address("${badgeResourceAddress}")
|
|
516
|
+
Array<NonFungibleLocalId>(NonFungibleLocalId("${badgeLocalId}"))
|
|
517
|
+
;
|
|
518
|
+
POP_FROM_AUTH_ZONE
|
|
519
|
+
Proof("agent_proof")
|
|
520
|
+
;
|
|
521
|
+
CALL_METHOD
|
|
522
|
+
Address("${this.config.componentAddress}")
|
|
523
|
+
"transfer"
|
|
524
|
+
Address("${to}")
|
|
525
|
+
Decimal("${amount}")
|
|
526
|
+
Address("${assetAddress}")
|
|
527
|
+
"transfer"
|
|
528
|
+
Proof("agent_proof")
|
|
529
|
+
;
|
|
530
|
+
CALL_METHOD
|
|
531
|
+
Address("${to}")
|
|
532
|
+
"try_deposit_batch_or_abort"
|
|
533
|
+
Expression("ENTIRE_WORKTOP")
|
|
534
|
+
None
|
|
535
|
+
;`;
|
|
536
|
+
}
|
|
537
|
+
// Firma y envía un manifiesto a la red Radix
|
|
538
|
+
async submitManifest(manifest) {
|
|
539
|
+
return await this.radixClient.submitManifest(manifest);
|
|
540
|
+
}
|
|
541
|
+
waitForOwnerApproval(pendingDetails, intervalMs = 30000) {
|
|
542
|
+
let active = true;
|
|
543
|
+
let fromStateVersion = 0;
|
|
544
|
+
// Obtener state_version actual antes de arrancar el polling
|
|
545
|
+
const init = async () => {
|
|
546
|
+
try {
|
|
547
|
+
const response = await fetch(`${this.networkConfig.gatewayUrl}/stream/transactions`, {
|
|
548
|
+
method: "POST",
|
|
549
|
+
headers: { "Content-Type": "application/json" },
|
|
550
|
+
body: JSON.stringify({
|
|
551
|
+
affected_global_entities_filter: [this.config.componentAddress],
|
|
552
|
+
limit_per_page: 1,
|
|
553
|
+
order: "Desc",
|
|
554
|
+
})
|
|
555
|
+
});
|
|
556
|
+
const data = await response.json();
|
|
557
|
+
fromStateVersion = data?.items?.[0]?.state_version ?? 0;
|
|
558
|
+
console.log(`[AgentWallet] waitForOwnerApproval — starting from state_version: ${fromStateVersion}`);
|
|
559
|
+
}
|
|
560
|
+
catch (err) {
|
|
561
|
+
console.error("[AgentWallet] waitForOwnerApproval init error:", err);
|
|
562
|
+
}
|
|
563
|
+
setTimeout(checkResult, intervalMs);
|
|
564
|
+
};
|
|
565
|
+
const checkResult = async () => {
|
|
566
|
+
if (!active)
|
|
567
|
+
return;
|
|
568
|
+
try {
|
|
569
|
+
const response = await fetch(`${this.networkConfig.gatewayUrl}/stream/transactions`, {
|
|
570
|
+
method: "POST",
|
|
571
|
+
headers: { "Content-Type": "application/json" },
|
|
572
|
+
body: JSON.stringify({
|
|
573
|
+
affected_global_entities_filter: [this.config.componentAddress],
|
|
574
|
+
limit_per_page: 10,
|
|
575
|
+
order: "Desc",
|
|
576
|
+
opt_ins: { manifest_instructions: true }
|
|
577
|
+
})
|
|
578
|
+
});
|
|
579
|
+
const data = await response.json();
|
|
580
|
+
const items = data?.items || [];
|
|
581
|
+
// Solo mirar TXs posteriores al inicio del polling
|
|
582
|
+
const newItems = items.filter((tx) => tx.state_version > fromStateVersion);
|
|
583
|
+
for (const tx of newItems) {
|
|
584
|
+
const instructions = tx.manifest_instructions || "";
|
|
585
|
+
if (instructions.includes("approve_transfer")) {
|
|
586
|
+
active = false;
|
|
587
|
+
this.config.onTransferApproved?.(pendingDetails);
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
if (instructions.includes("reject_transfer")) {
|
|
591
|
+
active = false;
|
|
592
|
+
this.config.onTransferRejected?.(pendingDetails);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
catch (err) {
|
|
598
|
+
console.error("[AgentWallet] waitForOwnerApproval error:", err);
|
|
599
|
+
}
|
|
600
|
+
if (active)
|
|
601
|
+
setTimeout(checkResult, intervalMs);
|
|
602
|
+
};
|
|
603
|
+
init();
|
|
604
|
+
return () => { active = false; };
|
|
605
|
+
}
|
|
606
|
+
async getWhitelist() {
|
|
607
|
+
const manifest = `
|
|
608
|
+
CALL_METHOD
|
|
609
|
+
Address("${this.config.componentAddress}")
|
|
610
|
+
"get_whitelist"
|
|
611
|
+
;
|
|
612
|
+
`;
|
|
613
|
+
const response = await fetch(`${this.networkConfig.gatewayUrl}/transaction/preview`, {
|
|
614
|
+
method: "POST",
|
|
615
|
+
headers: { "Content-Type": "application/json" },
|
|
616
|
+
body: JSON.stringify({
|
|
617
|
+
manifest,
|
|
618
|
+
start_epoch_inclusive: 1,
|
|
619
|
+
end_epoch_exclusive: 100,
|
|
620
|
+
nonce: 1,
|
|
621
|
+
signer_public_keys: [],
|
|
622
|
+
notary_public_key: {
|
|
623
|
+
key_type: "EddsaEd25519",
|
|
624
|
+
key_hex: "0000000000000000000000000000000000000000000000000000000000000001"
|
|
625
|
+
},
|
|
626
|
+
notary_is_signatory: true,
|
|
627
|
+
tip_percentage: 0,
|
|
628
|
+
flags: {
|
|
629
|
+
use_free_credit: true,
|
|
630
|
+
assume_all_signature_proofs: true,
|
|
631
|
+
skip_epoch_check: true
|
|
632
|
+
}
|
|
633
|
+
})
|
|
634
|
+
});
|
|
635
|
+
const data = await response.json();
|
|
636
|
+
const output = data?.receipt?.output?.[0]?.programmatic_json;
|
|
637
|
+
if (!output || !output.elements)
|
|
638
|
+
return [];
|
|
639
|
+
return output.elements.map((item) => ({
|
|
640
|
+
name: item.fields?.[0]?.value ?? "",
|
|
641
|
+
address: item.fields?.[1]?.value ?? "",
|
|
642
|
+
}));
|
|
643
|
+
}
|
|
644
|
+
async getSwapQuote(fromAsset, toAsset, amount) {
|
|
645
|
+
if (!this.aggrClient) {
|
|
646
|
+
throw new Error("[AgentWallet] AGGR not configured. Pass aggrConfig to use swap quotes.");
|
|
647
|
+
}
|
|
648
|
+
const fromAddress = this.getAssetAddress(fromAsset);
|
|
649
|
+
const toAddress = this.getAssetAddress(toAsset);
|
|
650
|
+
const quote = await this.aggrClient.getSwapQuote(fromAddress, toAddress, amount, this.config.componentAddress);
|
|
651
|
+
return {
|
|
652
|
+
outputTokens: quote.outputTokens,
|
|
653
|
+
price: quote.outputTokens / amount,
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
exports.AgentWallet = AgentWallet;
|
|
658
|
+
//# sourceMappingURL=AgentWallet.js.map
|