@panal/sdk 0.18.0 → 0.18.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +36 -1
- package/dist/client.js +88 -7
- package/package.json +2 -2
package/dist/client.d.ts
CHANGED
|
@@ -129,6 +129,30 @@ export declare class PanalClient {
|
|
|
129
129
|
constructor(options?: PanalClientOptions);
|
|
130
130
|
/** El wallet client, o un error que dice exactamente qué falta. */
|
|
131
131
|
private wallet;
|
|
132
|
+
/**
|
|
133
|
+
* Comprueba ANTES de firmar que la wallet cubre lo que Monad va a reservar.
|
|
134
|
+
*
|
|
135
|
+
* Monad bloquea `gas_limit × maxFeePerGas` antes de ejecutar, aunque luego
|
|
136
|
+
* cobre bastante menos: darse de alta estima ~264.000 de gas, que a 122 gwei
|
|
137
|
+
* son 0,032 MON de reserva (medido el 2026-09-14). Con menos, el nodo
|
|
138
|
+
* rechaza la transacción con «Signer had insufficient balance» y viem lo
|
|
139
|
+
* presenta como un revert del contrato, que no llegó a ejecutarse. Y hay una
|
|
140
|
+
* segunda trampa: reintentar tras recargar con el mismo nonce y las mismas
|
|
141
|
+
* comisiones firma una transacción idéntica, y el nodo repite el rechazo sin
|
|
142
|
+
* volver a mirar el saldo.
|
|
143
|
+
*
|
|
144
|
+
* Si no se puede estimar —RPC caído, o una llamada que el contrato rechazaría
|
|
145
|
+
* por otro motivo— no se bloquea nada: que hable el error de verdad.
|
|
146
|
+
*/
|
|
147
|
+
private comprobarReserva;
|
|
148
|
+
/**
|
|
149
|
+
* Espera el recibo y comprueba que la transacción SALIÓ BIEN.
|
|
150
|
+
*
|
|
151
|
+
* Esperar el recibo no basta: un revert también llega con recibo. Sin mirar
|
|
152
|
+
* `status`, una transacción que gastó gas y no cambió nada volvía como si
|
|
153
|
+
* todo hubiera ido bien.
|
|
154
|
+
*/
|
|
155
|
+
private esperarExito;
|
|
132
156
|
/** Todos los agentes del registry, activos e inactivos. */
|
|
133
157
|
listAgents(): Promise<Agent[]>;
|
|
134
158
|
/**
|
|
@@ -412,7 +436,18 @@ export declare class PanalClient {
|
|
|
412
436
|
* ya están desplegados sin obligarles a actualizarse.
|
|
413
437
|
*/
|
|
414
438
|
private x402Endpoint;
|
|
415
|
-
/**
|
|
439
|
+
/**
|
|
440
|
+
* Retira lo acreditado en una moneda (patrón pull payment).
|
|
441
|
+
*
|
|
442
|
+
* EL GAS VA FIJADO A MANO, y no es un detalle. viem no estima: le pide al nodo
|
|
443
|
+
* que rellene la transacción (`eth_fillTransaction`), y el de Monad devuelve
|
|
444
|
+
* un gas disparatado para retirar MON —1,05 M para una wallet, 10,7 M para
|
|
445
|
+
* otra— cuando lo necesario son 55.157. Monad cobra el LÍMITE entero, no lo
|
|
446
|
+
* usado: una retirada de 1,092 MON pagó 1,096 MON de gas (2026-09-14, tx
|
|
447
|
+
* 0xa960cb7e…). El gas sale de `eth_estimateGas`, que da el número bueno, con
|
|
448
|
+
* un 10 % de margen; y si la estimación vuelve por encima de `TOPE_GAS_RETIRADA`
|
|
449
|
+
* no se firma nada.
|
|
450
|
+
*/
|
|
416
451
|
withdraw(currency?: Address): Promise<Hex>;
|
|
417
452
|
/** Comprueba el saldo antes de firmar, para fallar con un mensaje legible. */
|
|
418
453
|
private assertFunds;
|
package/dist/client.js
CHANGED
|
@@ -24,6 +24,11 @@ import { descend, newEnvelope, remainingBudget } from './envelope.js';
|
|
|
24
24
|
import { NATIVE_CURRENCY, addressesFor, chainFor } from './chains.js';
|
|
25
25
|
import { BUZON_URL, TABLON, VENTANA_FIRMA_S, encargoSignMessage, entregaSignMessage, ofertaSignMessage, } from './tablon.js';
|
|
26
26
|
import { TaskStatus, formatAgentMetadata, parseAgentMetadata, } from './types.js';
|
|
27
|
+
/**
|
|
28
|
+
* Por encima de esto una retirada no se firma: retirar MON son 55.157 de gas y
|
|
29
|
+
* $PANAL 103.511. Ver `withdraw()` para lo que pasó sin este tope.
|
|
30
|
+
*/
|
|
31
|
+
const TOPE_GAS_RETIRADA = 300000n;
|
|
27
32
|
/** Cuántos agentes se leen por llamada al registry. */
|
|
28
33
|
const REGISTRY_PAGE = 50n;
|
|
29
34
|
/** Tope duro de agentes recorridos, por si el registro crece mucho. */
|
|
@@ -126,6 +131,55 @@ export class PanalClient {
|
|
|
126
131
|
}
|
|
127
132
|
return this.walletClient;
|
|
128
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Comprueba ANTES de firmar que la wallet cubre lo que Monad va a reservar.
|
|
136
|
+
*
|
|
137
|
+
* Monad bloquea `gas_limit × maxFeePerGas` antes de ejecutar, aunque luego
|
|
138
|
+
* cobre bastante menos: darse de alta estima ~264.000 de gas, que a 122 gwei
|
|
139
|
+
* son 0,032 MON de reserva (medido el 2026-09-14). Con menos, el nodo
|
|
140
|
+
* rechaza la transacción con «Signer had insufficient balance» y viem lo
|
|
141
|
+
* presenta como un revert del contrato, que no llegó a ejecutarse. Y hay una
|
|
142
|
+
* segunda trampa: reintentar tras recargar con el mismo nonce y las mismas
|
|
143
|
+
* comisiones firma una transacción idéntica, y el nodo repite el rechazo sin
|
|
144
|
+
* volver a mirar el saldo.
|
|
145
|
+
*
|
|
146
|
+
* Si no se puede estimar —RPC caído, o una llamada que el contrato rechazaría
|
|
147
|
+
* por otro motivo— no se bloquea nada: que hable el error de verdad.
|
|
148
|
+
*/
|
|
149
|
+
async comprobarReserva(que, llamada) {
|
|
150
|
+
let gas;
|
|
151
|
+
let maxFeePerGas;
|
|
152
|
+
let saldo;
|
|
153
|
+
try {
|
|
154
|
+
[gas, { maxFeePerGas }, saldo] = await Promise.all([
|
|
155
|
+
this.publicClient.estimateContractGas({ ...llamada, account: this.account }),
|
|
156
|
+
this.publicClient.estimateFeesPerGas(),
|
|
157
|
+
this.publicClient.getBalance({ address: this.account.address }),
|
|
158
|
+
]);
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const reserva = gas * maxFeePerGas + (llamada.value ?? 0n);
|
|
164
|
+
if (saldo < reserva) {
|
|
165
|
+
throw new Error(`${que}: Monad reserva ${formatEther(reserva)} MON por adelantado (límite de gas × precio máximo), ` +
|
|
166
|
+
`aunque luego cobre menos. ${this.account.address} tiene ${formatEther(saldo)} MON: ` +
|
|
167
|
+
`faltan ${formatEther(reserva - saldo)} MON. No se ha enviado nada.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Espera el recibo y comprueba que la transacción SALIÓ BIEN.
|
|
172
|
+
*
|
|
173
|
+
* Esperar el recibo no basta: un revert también llega con recibo. Sin mirar
|
|
174
|
+
* `status`, una transacción que gastó gas y no cambió nada volvía como si
|
|
175
|
+
* todo hubiera ido bien.
|
|
176
|
+
*/
|
|
177
|
+
async esperarExito(hash, que) {
|
|
178
|
+
const recibo = await this.publicClient.waitForTransactionReceipt({ hash });
|
|
179
|
+
if (recibo.status !== 'success') {
|
|
180
|
+
throw new Error(`${que} revirtió (tx ${hash}): se cobró el gas y no cambió nada.`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
129
183
|
// -------------------------------------------------------------------------
|
|
130
184
|
// Lectura
|
|
131
185
|
// -------------------------------------------------------------------------
|
|
@@ -526,15 +580,21 @@ export class PanalClient {
|
|
|
526
580
|
*/
|
|
527
581
|
async registerAgent(params) {
|
|
528
582
|
const wallet = this.wallet();
|
|
529
|
-
const
|
|
583
|
+
const llamada = {
|
|
530
584
|
address: this.addresses.registry,
|
|
531
585
|
abi: registryAbi,
|
|
532
586
|
functionName: 'registerAgent',
|
|
533
587
|
args: [formatAgentMetadata(params.metadata), params.pricePerTask, params.currency ?? NATIVE_CURRENCY],
|
|
588
|
+
};
|
|
589
|
+
await this.comprobarReserva('No se puede dar de alta', llamada);
|
|
590
|
+
const hash = await wallet.writeContract({
|
|
591
|
+
...llamada,
|
|
592
|
+
abi: registryAbi,
|
|
593
|
+
functionName: 'registerAgent',
|
|
534
594
|
chain: chainFor(this.network),
|
|
535
595
|
account: this.account,
|
|
536
596
|
});
|
|
537
|
-
await this.
|
|
597
|
+
await this.esperarExito(hash, 'El alta');
|
|
538
598
|
return hash;
|
|
539
599
|
}
|
|
540
600
|
/** Cambia el nombre, la descripción, las skills o el endpoint publicados. */
|
|
@@ -608,7 +668,7 @@ export class PanalClient {
|
|
|
608
668
|
chain: chainFor(this.network),
|
|
609
669
|
account: this.account,
|
|
610
670
|
});
|
|
611
|
-
await this.
|
|
671
|
+
await this.esperarExito(txHash, `La entrega de #${taskId}`);
|
|
612
672
|
return { txHash, resultHash };
|
|
613
673
|
}
|
|
614
674
|
/**
|
|
@@ -747,7 +807,7 @@ export class PanalClient {
|
|
|
747
807
|
chain: chainFor(this.network),
|
|
748
808
|
account: this.account,
|
|
749
809
|
});
|
|
750
|
-
await this.
|
|
810
|
+
await this.esperarExito(txHash, `Coger la tarea #${taskId}`);
|
|
751
811
|
return { txHash };
|
|
752
812
|
}
|
|
753
813
|
/**
|
|
@@ -990,18 +1050,39 @@ export class PanalClient {
|
|
|
990
1050
|
}
|
|
991
1051
|
return rutaDeAgente(base, 'x402/ask');
|
|
992
1052
|
}
|
|
993
|
-
/**
|
|
1053
|
+
/**
|
|
1054
|
+
* Retira lo acreditado en una moneda (patrón pull payment).
|
|
1055
|
+
*
|
|
1056
|
+
* EL GAS VA FIJADO A MANO, y no es un detalle. viem no estima: le pide al nodo
|
|
1057
|
+
* que rellene la transacción (`eth_fillTransaction`), y el de Monad devuelve
|
|
1058
|
+
* un gas disparatado para retirar MON —1,05 M para una wallet, 10,7 M para
|
|
1059
|
+
* otra— cuando lo necesario son 55.157. Monad cobra el LÍMITE entero, no lo
|
|
1060
|
+
* usado: una retirada de 1,092 MON pagó 1,096 MON de gas (2026-09-14, tx
|
|
1061
|
+
* 0xa960cb7e…). El gas sale de `eth_estimateGas`, que da el número bueno, con
|
|
1062
|
+
* un 10 % de margen; y si la estimación vuelve por encima de `TOPE_GAS_RETIRADA`
|
|
1063
|
+
* no se firma nada.
|
|
1064
|
+
*/
|
|
994
1065
|
async withdraw(currency = NATIVE_CURRENCY) {
|
|
995
1066
|
const wallet = this.wallet();
|
|
996
|
-
const
|
|
1067
|
+
const llamada = {
|
|
997
1068
|
address: this.addresses.escrow,
|
|
998
1069
|
abi: escrowAbi,
|
|
999
1070
|
functionName: 'withdraw',
|
|
1000
1071
|
args: [currency],
|
|
1072
|
+
};
|
|
1073
|
+
const estimado = await this.publicClient.estimateContractGas({ ...llamada, account: this.account });
|
|
1074
|
+
const gas = (estimado * 11n + 9n) / 10n;
|
|
1075
|
+
if (gas > TOPE_GAS_RETIRADA) {
|
|
1076
|
+
throw new Error(`La estimación de gas para retirar salió en ${estimado}, muy por encima de lo normal (~55.000 en MON, ` +
|
|
1077
|
+
`~104.000 en $PANAL). Monad cobra el límite entero: no se ha enviado nada.`);
|
|
1078
|
+
}
|
|
1079
|
+
const hash = await wallet.writeContract({
|
|
1080
|
+
...llamada,
|
|
1081
|
+
gas,
|
|
1001
1082
|
chain: chainFor(this.network),
|
|
1002
1083
|
account: this.account,
|
|
1003
1084
|
});
|
|
1004
|
-
await this.
|
|
1085
|
+
await this.esperarExito(hash, 'La retirada');
|
|
1005
1086
|
return hash;
|
|
1006
1087
|
}
|
|
1007
1088
|
/** Comprueba el saldo antes de firmar, para fallar con un mensaje legible. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@panal/sdk",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "SDK de Panal: contrata agentes de IA autonomos on-chain en Monad",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsc -p tsconfig.json",
|
|
47
47
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
48
|
-
"test": "tsx test/sdk.test.ts && tsx test/x402.test.ts && tsx test/x402-server.test.ts && tsx test/envelope.test.ts && tsx test/files.test.ts && tsx test/attachments.test.ts && tsx test/llm.test.ts && tsx test/skill.test.ts && tsx test/agent-card.test.ts && tsx test/niveles.test.ts && tsx test/tablon.test.ts",
|
|
48
|
+
"test": "tsx test/sdk.test.ts && tsx test/x402.test.ts && tsx test/x402-server.test.ts && tsx test/envelope.test.ts && tsx test/files.test.ts && tsx test/attachments.test.ts && tsx test/llm.test.ts && tsx test/skill.test.ts && tsx test/agent-card.test.ts && tsx test/niveles.test.ts && tsx test/tablon.test.ts && tsx test/reserva.test.ts",
|
|
49
49
|
"test:nombres": "tsx test/nombres.test.ts"
|
|
50
50
|
}
|
|
51
51
|
}
|