Whitepaper · 13
Análisis de rendimiento
Gas medido de la implementación de referencia con cero, uno y dos hooks; qué determina el coste; y orientaciones de despliegue.
Método
Las cifras siguientes proceden de scripts/gas-report-standard.js, ejecutado en la red en proceso de Hardhat contra la implementación de referencia compilada (solc 0.8.22, viaIR, optimizer.runs = 1 para ARexToken), en septiembre de 2026. «En caliente» significa el mismo par remitente/destinatario/id que la transferencia anterior (slots de almacenamiento ya tocados en el bloque). Hooks: EligibilityHook (CHECK + ELIGIBILITY) y ExpiryHook (CHECK). Para reproducirlo:
npx hardhat run scripts/gas-report-standard.js
Resultados
xychart-beta
title "Gas de una transferencia en caliente según el número de hooks (miles)"
x-axis ["sin hooks", "Eligibility", "Eligibility + Expiry"]
y-axis "gas (k)" 0 --> 140
bar [66.8, 91.2, 119.6]El coste crece linealmente con el número de hooks: cada uno supone una llamada externa a check por id, más canSend y canReceive en el caso de un hook de elegibilidad. La tabla indica cada operación.
| 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 división 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 exactamente un hook de cada tipo (coste marginal de la regla):
xychart-beta horizontal
title "Gas marginal de un hook en una transferencia en caliente (miles)"
x-axis ["DeliveryHook", "Traceability (emisión)", "EligibilityHook", "ExpiryHook", "SeasonalHook", "PerishabilityHook"]
y-axis "gas añadido (k)" 0 --> 35
bar [12.3, 17.4, 24.4, 25.2, 27.5, 31.0]TraceabilityHook en modo persistente añade 184.5k y se omite del gráfico por razones de escala.
| Hook | Gas | Δ frente a 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 emisión | 84,152 | +17,362 |
| TraceabilityHook, persistente | 251,273 | +184,483 |
Qué determina el coste
- Una transferencia sin hooks cuesta ~67k en caliente, es decir, ERC-1155 más el seguimiento del suministro más las lecturas de existencia, estado y congelación del lote y la gestión interna de la canalización (almacenamiento temporal de datos, bloqueo). Este es el mínimo.
- Cada hook añade aproximadamente 25–30k a una transferencia: una llamada a
check(vista, entre contratos) más, en el caso de los hooks de elegibilidad, las llamadas acanSendycanReceive. El coste es lineal en el número de hooks y en el número de ids de una transferencia múltiple. Sondear cada contrato de reglas para cada capacidad consupportsInterfaceantes de llamarlo añadiría varias llamadas estáticas por id; esa sobrecarga no existe porque los indicadores se almacenan en caché en el registro. createBatchestá dominado por el almacenamiento: el structBatch(seis palabras más una cadena de texto) y los slots de saldo y de suministro de ERC-1155. Es un coste único por lote.transformcrea ids nuevos: una división en dos paga dos registros de lote, más dos acuñaciones, más una quema, más las llamadas a hooks en cada tramo (OP_TRANSFORM). Una división en n cuesta aproximadamenten × createBatchmás una quema; fusionar n en uno cuesta aproximadamentenquemas más un createBatch.- La ejecución forzosa es barata: congelar es un solo slot; una transferencia forzosa cuesta lo que cuesta una transferencia más la escritura de descongelación y el evento.
transferCheck/canTransferson vistas: gratuitas fuera de la cadena y con la misma ruta de código que la transacción.
Lo que aporta el diseño
| Diseño con sondeo de capacidades | A-REX Core (ARexToken) | |
|---|---|---|
| Transferencia con n contratos de reglas | ids × módulos × (sondeos de supportsInterface + llamada al hook) antes de que se ejecute ninguna lógica | ids × hooks × 1 llamada (+2 para los hooks de elegibilidad); indicadores en caché en el registro |
| Rechazo | reversión con cadena de texto (copiada a memoria, no indexable) | error personalizado con la dirección del hook y un código bytes32 |
| Linaje | no disponible | ~280k por lote de salida |
| Congelación / transferencia forzosa | no disponible | 54k / 80k |
La columna izquierda describe el diseño que A-REX Core sustituye (ADR-004, ADR-005); solo la columna derecha está medida.
Orientaciones de despliegue
- El coste en dinero es
gas × gas price × token price, y los dos últimos factores cambian en órdenes de magnitud entre cadenas y entre días. Solo a modo ilustrativo, 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. Mídase en la cadena de destino antes de comprometerse con un modelo de economía unitaria. - Transferencias múltiples:
safeBatchTransferFromamortiza los costes fijos; el coste de los hooks sigue escalando con el número de ids. - Orden de los hooks: colóquense primero los hooks baratos y de alto rechazo; un rechazo detiene la canalización antes de que se ejecuten hooks más caros.
- Número de hooks: entre tres y cinco bastan para la mayoría de los emisores; cada hook adicional supone un coste medible por transferencia y una superficie de gobernanza.
- Metadatos: manténgase
uricorta (un CID o un Digital Link); la cadena de texto se almacena en la cadena. - Lecturas fuera de la cadena: úsese
transferCheckantes de enviar; una transacción revertida sigue costando gas.
Capacidad de procesamiento
El cuello de botella no es el token, sino la cadena. Una transferencia individual es una transacción; una transformación con n salidas es una transacción con n acuñaciones. Indexar el linaje requiere leer los eventos BatchTransformed, que se emiten una vez por operación con las listas completas de cantidades, de modo que un indexador no necesita reconstruir nada a partir de los eventos por transferencia.
Tamaño de los contratos
ARexToken se despliega con 24,136 bytes (límite 24,576), compilado con optimizer.runs = 1. Los demás contratos son pequeños: ARexAttestations 2,708; hooks entre 1,358 (ExpiryHook) y 6,215 (DeliveryHook), TraceabilityHook 4,076; extensiones entre 3,716 y 4,588. Añadir funcionalidad al propio token está limitado por el tamaño por diseño: para eso sirven los hooks y los contratos complementarios.