Whitepaper · 13
Análisis de rendimiento
Gas medido en la implementación de referencia con cero, uno y dos hooks, qué factores determinan el costo y recomendaciones para el despliegue.
Método
Las cifras de esta página salen de ejecutar scripts/gas-report-standard.js en la red local de Hardhat sobre la implementación de referencia compilada (solc 0.8.22, viaIR, optimizer.runs = 1 para ARexToken), en septiembre de 2026. «En caliente» significa que el par remitente/destinatario/id es el mismo de la transferencia anterior, por lo que los slots de almacenamiento ya se usaron en el bloque. Reglas externas (hooks) medidas: EligibilityHook (CHECK + ELIGIBILITY) y ExpiryHook (CHECK). Para reproducir las mediciones, ejecuta:
npx hardhat run scripts/gas-report-standard.js
Resultados
xychart-beta
title "Gas de una transferencia en caliente según la cantidad de hooks (miles)"
x-axis ["sin hooks", "Eligibility", "Eligibility + Expiry"]
y-axis "gas (k)" 0 --> 140
bar [66.8, 91.2, 119.6]El costo crece de forma lineal con la cantidad de hooks: cada uno suma una llamada externa a check por id y, si es un hook de habilitación, también canSend y canReceive. La tabla muestra todas las operaciones.
| Operación | 0 hooks | 1 hook | 2 hooks |
|---|---|---|---|
createBatch (id nuevo, acuñación de 1000) | 264,096 | 283,831 | 299,396 |
safeTransferFrom (primera, en frío) | 80,687 | 108,266 | 136,682 |
safeTransferFrom (en caliente) | 66,790 | 91,166 | 119,582 |
transform: dividir 1 → 2 (dos ids nuevos) | 564,422 | 595,707 | 636,858 |
setFrozenTokens | 54,188 | 54,188 | 54,188 |
forcedTransfer | 80,152 | 89,909 | 99,569 |
burn | 66,621 | 83,001 | 93,395 |
Transferencia en caliente con un solo hook de cada tipo (costo marginal de cada regla):
xychart-beta horizontal
title "Gas adicional de un hook en una transferencia en caliente (miles)"
x-axis ["DeliveryHook", "Traceability (solo emite)", "EligibilityHook", "ExpiryHook", "SeasonalHook", "PerishabilityHook"]
y-axis "gas adicional (k)" 0 --> 35
bar [12.3, 17.4, 24.4, 25.2, 27.5, 31.0]En modo persistente, TraceabilityHook suma 184.5k; no aparece en el gráfico para no distorsionar la escala.
| Hook | Gas | Δ respecto de sin hook |
|---|---|---|
| EligibilityHook | 91,166 | +24,376 |
| ExpiryHook | 92,023 | +25,233 |
| SeasonalHook | 94,324 | +27,534 |
| PerishabilityHook | 97,813 | +31,023 |
| DeliveryHook (sin entrega activa) | 79,076 | +12,286 |
| TraceabilityHook, solo emite | 84,152 | +17,362 |
| TraceabilityHook, persistente | 251,273 | +184,483 |
Qué determina el costo
- Una transferencia en caliente sin hooks cuesta ~67k: ERC-1155, el seguimiento del suministro total, las lecturas de existencia, estado y congelamiento del lote, y la gestión interna de la cadena de reglas (datos temporales y bloqueo). Ese es el piso.
- Cada hook suma entre 25k y 30k aproximadamente a una transferencia: una llamada a
check(vista, entre contratos) y, en los hooks de habilitación, las llamadas acanSendycanReceive. El costo crece de forma lineal con la cantidad de hooks y con la cantidad de ids de una transferencia múltiple. Consultar consupportsInterfacecada capacidad de cada contrato de reglas antes de llamarlo agregaría varias llamadas estáticas por id. Ese sobrecosto no existe, porque los indicadores se guardan en caché al registrar el hook. - En
createBatchdomina el almacenamiento: el structBatch(seis palabras más un string) y los slots de saldo y de suministro total de ERC-1155. Es un costo único por lote. transformcrea ids nuevos: dividir un lote en dos implica pagar dos registros de lote, dos acuñaciones y una quema, más las llamadas a hooks en cada tramo (OP_TRANSFORM). Dividir en n cuesta aproximadamenten × createBatchmás una quema; fusionar n en uno cuesta aproximadamentenquemas más un createBatch.- Los controles de cumplimiento son baratos: congelar ocupa un solo slot. Una transferencia forzosa cuesta lo mismo que una transferencia, más la escritura que descongela y el evento.
transferCheck/canTransferson vistas: no cuestan nada fuera de la cadena y recorren el mismo código que la transacción.
Qué gana el diseño
| Diseño con consulta de capacidades | A-REX Core (ARexToken) | |
|---|---|---|
| Transferencia con n contratos de reglas | ids × módulos × (consultas a supportsInterface + llamada al hook) antes de ejecutar cualquier lógica | ids × hooks × 1 llamada (+2 en los hooks de habilitación); indicadores en caché desde el registro |
| Rechazo | revert con un string (se copia a memoria, no se puede indexar) | error personalizado con la dirección del hook y un código bytes32 |
| Linaje | no disponible | ~280k por lote de salida |
| Congelar / transferencia forzosa | no disponible | 54k / 80k |
La columna izquierda describe el diseño que A-REX Core reemplaza (ADR-004, ADR-005). Solo la columna derecha está medida.
Recomendaciones para el despliegue
- El costo en dinero es
gas × gas price × token price, y los dos últimos factores cambian en órdenes de magnitud según la cadena y el día. Solo como ejemplo: a 0.01 gwei y 3,000 USD por ETH, una transferencia en caliente con dos hooks (119,582 gas) cuesta unos 0.0036 USD y uncreateBatch(264,096 gas), unos 0.008 USD. A 20 gwei en la red principal de Ethereum, las mismas operaciones cuestan unos 7.2 y 15.8 USD. Mide en la cadena de destino antes de definir un modelo de costo unitario. - Transferencias múltiples:
safeBatchTransferFromreparte los costos fijos, pero el costo de los hooks sigue creciendo con la cantidad de ids. - Orden de los hooks: pon primero los hooks baratos que rechazan con más frecuencia. Un rechazo detiene la cadena de reglas antes de que corran los hooks más caros.
- Cantidad de hooks: de tres a cinco alcanzan para la mayoría de los emisores. Cada hook adicional es un costo medible en cada transferencia y un elemento más que gobernar.
- Metadatos: usa un
uricorto (un CID o un Digital Link), porque el string se guarda en la cadena. - Lecturas fuera de la cadena: llama a
transferCheckantes de enviar la transacción. Una transacción revertida también consume gas.
Capacidad de procesamiento
El cuello de botella no es el token, sino la cadena. Una transferencia es una transacción; una transformación con n salidas es una sola transacción con n acuñaciones. Para indexar el linaje basta con leer los eventos BatchTransformed, que se emiten una vez por operación con las listas completas de cantidades. Así, el indexador no tiene que reconstruir nada a partir de los eventos de cada transferencia.
Tamaño de los contratos
ARexToken ocupa 24,136 bytes desplegado (el límite es 24,576), compilado con optimizer.runs = 1. Los demás contratos son pequeños: ARexAttestations ocupa 2,708; los hooks, entre 1,358 (ExpiryHook) y 6,215 (DeliveryHook), con TraceabilityHook en 4,076; las extensiones, entre 3,716 y 4,588. El tamaño limita a propósito lo que se puede agregar al token. Para eso existen los hooks y los contratos complementarios.