Nota técnica · 9 de octubre de 2026

Misma sal, otra dirección: revisa el inicializador

Los bytes del inicializador de un proxy pueden formar parte de los datos usados para crearlo. Cambiar un argumento puede cambiar su dirección CREATE2, aunque la sal siga siendo la misma.

Alcance: cálculo educativo con datos inventados. No desplegamos contratos, reproducimos transacciones ajenas ni verificamos una migración.

Argumentos del inicializadorDatos de creaciónDirección CREATE2

Un dato distinto, dos direcciones

En este ejemplo se mantienen la fábrica, la sal, la dirección de implementación y el propietario inicial. Solo cambia la segunda dirección del inicializador.

Dato / resultadoEjemplo AEjemplo B
Segunda dirección del inicializador0x50505050505050505050505050505050505050500x5151515151515151515151515151515151515151
Hash del código de inicialización completo0x82c940f3425d5b6d300b9d83fce6e4c179e5bbb6e5ba86621abdeb37c6a2cdbe0x34566302fe22f91ca08557b9368dbc4feac958f6cae978794a30cc30386e77de
Dirección calculada0xc4d56ea9fd41fb2741b8697848436dd26eaa66b60x02f2329a1c562d24384b03dc96ee2f53e1380c28

El bytecode es un ejemplo sintético, no el de un proxy de OpenZeppelin. Demuestra la dependencia de los datos; no es una receta de despliegue.

Control: con los mismos datos y otra etiqueta descriptiva de cadena, la dirección no cambia. Esa etiqueta no entra en CREATE2; esto no demuestra compatibilidad de despliegue entre cadenas.

Datos sintéticos completos para reproducir el cálculo
{
  "hash_function": "Keccak-256, not SHA3-256",
  "formula": "last_20_bytes(keccak256(0xff || factory_20_bytes || salt_32_bytes || keccak256(init_code)))",
  "stub_bytecode": "0x6001600c60003960016000f300",
  "constructor_abi": "(address,address,bytes)",
  "initializer_signature": "initialize(address,address,address,address)",
  "examples": [
    {
      "label": "A",
      "factory": "0x1010101010101010101010101010101010101010",
      "salt": "0x000000000000000000000000000000000000000000000000000000000000002a",
      "implementation": "0x2020202020202020202020202020202020202020",
      "initial_owner": "0x3030303030303030303030303030303030303030",
      "initializer_arguments": [
        "0x4040404040404040404040404040404040404040",
        "0x5050505050505050505050505050505050505050",
        "0x6060606060606060606060606060606060606060",
        "0x7070707070707070707070707070707070707070"
      ],
      "init_code": "0x6001600c60003960016000f3000000000000000000000000002020202020202020202020202020202020202020000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084f8c8765e000000000000000000000000404040404040404040404040404040404040404000000000000000000000000050505050505050505050505050505050505050500000000000000000000000006060606060606060606060606060606060606060000000000000000000000000707070707070707070707070707070707070707000000000000000000000000000000000000000000000000000000000",
      "predicted_address": "0xc4d56ea9fd41fb2741b8697848436dd26eaa66b6"
    },
    {
      "label": "B",
      "factory": "0x1010101010101010101010101010101010101010",
      "salt": "0x000000000000000000000000000000000000000000000000000000000000002a",
      "implementation": "0x2020202020202020202020202020202020202020",
      "initial_owner": "0x3030303030303030303030303030303030303030",
      "initializer_arguments": [
        "0x4040404040404040404040404040404040404040",
        "0x5151515151515151515151515151515151515151",
        "0x6060606060606060606060606060606060606060",
        "0x7070707070707070707070707070707070707070"
      ],
      "init_code": "0x6001600c60003960016000f3000000000000000000000000002020202020202020202020202020202020202020000000000000000000000000303030303030303030303030303030303030303000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084f8c8765e000000000000000000000000404040404040404040404040404040404040404000000000000000000000000051515151515151515151515151515151515151510000000000000000000000006060606060606060606060606060606060606060000000000000000000000000707070707070707070707070707070707070707000000000000000000000000000000000000000000000000000000000",
      "predicted_address": "0x02f2329a1c562d24384b03dc96ee2f53e1380c28"
    }
  ]
}

Por qué puede ocurrir con un proxy

El código examinado de Hardhat Upgrades 3.9.1 construye los datos del inicializador a partir de los argumentos y los pasa al constructor del proxy. El proxy transparente v5.4.0 recibe esos bytes explícitamente. CREATE2 usa el código de inicialización completo, no solo el código final que quedará desplegado.

Qué comparar en un caso real

Compara la fábrica real, la sal de 32 bytes y el hash completo del código de inicialización. Si cambia el hash, revisa los argumentos del constructor y los datos del inicializador antes de atribuirlo a un fallo del proveedor.

Comprobaciones del cálculo

Coincidieron seis ejemplos publicados en EIP-1014. En la comparación sintética cambiaron 20 bytes de un bloque de argumentos de 288 bytes. Esto verifica estos cálculos, no una biblioteca criptográfica de producción ni un despliegue real.

Fuentes primarias

  1. EIP-1014: definición de la dirección CREATE2
  2. Plugin de OpenZeppelin 3.9.1: código de la versión examinada
  3. OpenZeppelin Contracts v5.4.0: constructor del proxy transparente