[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-7-bucles-condicionales-eficientes-gas-yul":3},{"article":4,"author":58},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":7,"meta_description":16,"focus_keyword":17,"og_image":18,"canonical_url":18,"robots_meta":19,"created_at":15,"updated_at":15,"tags":20,"category_name":38,"related_articles":39},"d8000000-0000-0000-0000-000000000107","a0000000-0000-0000-0000-000000000082","Deep EVM #7: Bucles y Condicionales Eficientes en Gas en Yul","deep-evm-7-bucles-condicionales-eficientes-gas-yul","Domina el flujo de control en Yul: bucles for, switch\u002Fcase, salto condicional vs secuencial, desenrollado de bucles, y patrones para iterar sobre arrays de storage y calldata de forma eficiente.","## Flujo de control en Yul\n\nYul proporciona tres construcciones de flujo de control: `if`, `switch`, y `for`. Cada una compila a diferentes patrones de opcodes JUMP y JUMPI, con implicaciones distintas en el coste de gas.\n\n## El condicional if\n\nEl `if` en Yul no tiene `else`. Para bifurcaciones completas, usa `switch`:\n\n```yul\nassembly {\n    let x := calldataload(0)\n    \n    \u002F\u002F If simple — sin else\n    if gt(x, 100) {\n        x := 100 \u002F\u002F Cap en 100\n    }\n    \n    \u002F\u002F Para if\u002Felse, usa switch\n    switch gt(x, 50)\n    case 1 {\n        \u002F\u002F x > 50\n        mstore(0x00, 1)\n    }\n    default {\n        \u002F\u002F x \u003C= 50\n        mstore(0x00, 0)\n    }\n}\n```\n\n## Switch\u002Fcase para despacho de funciones\n\nEl switch es especialmente útil para despacho de funciones, que es el patrón más común en contratos Yul standalone:\n\n```yul\nswitch shr(224, calldataload(0))\ncase 0x70a08231 \u002F* balanceOf *\u002F {\n    returnUint(sload(mappingSlot(calldataload(4), 0)))\n}\ncase 0xa9059cbb \u002F* transfer *\u002F {\n    ejecutarTransfer(calldataload(4), calldataload(36))\n}\ncase 0x18160ddd \u002F* totalSupply *\u002F {\n    returnUint(sload(0))\n}\ndefault {\n    revert(0, 0)\n}\n```\n\nCada `case` compila a un PUSH4 + EQ + PUSH2 + JUMPI. Con N funciones, el peor caso es N comparaciones. Para contratos con muchas funciones, esto es O(N) — las jump tables de Huff son más eficientes.\n\n## Bucles for\n\nEl bucle `for` en Yul tiene la misma estructura que en C:\n\n```yul\nassembly {\n    \u002F\u002F for (init; cond; post) { body }\n    for { let i := 0 } lt(i, 10) { i := add(i, 1) } {\n        \u002F\u002F cuerpo del bucle\n        mstore(add(0x80, mul(i, 32)), i)\n    }\n}\n```\n\n### Optimización: incremento pre vs post\nEn Solidity, `++i` es más barato que `i++` porque evita una copia temporal. En Yul, esto no importa — `add(i, 1)` es todo lo que hay.\n\n### Optimización: cachear la longitud\n```yul\n\u002F\u002F Malo: lee array.length de storage en cada iteración\nfor { let i := 0 } lt(i, sload(lengthSlot)) { i := add(i, 1) } {\n    \u002F\u002F sload cada iteración = 100 gas (caliente) * N\n}\n\n\u002F\u002F Bueno: cachear en variable local\nlet len := sload(lengthSlot) \u002F\u002F 2100 gas una vez (frío)\nfor { let i := 0 } lt(i, len) { i := add(i, 1) } {\n    \u002F\u002F lt usa variable de stack = 3 gas * N\n}\n```\n\n## Iteración sobre arrays de storage\n\nLos arrays dinámicos en Solidity almacenan la longitud en el slot base y los elementos a partir de keccak256(slot):\n\n```yul\nassembly {\n    \u002F\u002F Array dinámico en slot 2\n    let slotBase := 2\n    let length := sload(slotBase)\n    \n    \u002F\u002F Los elementos comienzan en keccak256(slotBase)\n    mstore(0x00, slotBase)\n    let dataSlot := keccak256(0x00, 0x20)\n    \n    let suma := 0\n    for { let i := 0 } lt(i, length) { i := add(i, 1) } {\n        suma := add(suma, sload(add(dataSlot, i)))\n    }\n    \n    mstore(0x00, suma)\n    return(0x00, 0x20)\n}\n```\n\n## Iteración sobre calldata\n\nCalldata es la fuente de datos más barata para iterar:\n\n```yul\nassembly {\n    \u002F\u002F Función: sumarArray(uint256[] calldata nums)\n    \u002F\u002F Calldata layout: [selector(4)] [offset(32)] [length(32)] [elem0(32)] [elem1(32)] ...\n    let offset := add(calldataload(4), 4) \u002F\u002F Offset del array\n    let length := calldataload(offset)\n    let dataStart := add(offset, 32)\n    \n    let suma := 0\n    for { let i := 0 } lt(i, length) { i := add(i, 1) } {\n        suma := add(suma, calldataload(add(dataStart, mul(i, 32))))\n    }\n    \n    mstore(0x00, suma)\n    return(0x00, 0x20)\n}\n```\n\nCada `calldataload` cuesta solo 3 gas, comparado con 100+ gas para `sload` caliente.\n\n## Desenrollado de bucles\n\nPara bucles con conteo conocido y pequeño, el desenrollado manual puede ahorrar el overhead del condicional y el incremento:\n\n```yul\n\u002F\u002F Bucle normal: 5 iteraciones con overhead por iteración\nfor { let i := 0 } lt(i, 5) { i := add(i, 1) } {\n    \u002F\u002F lt(3) + add(3) + jumpi(10) = 16 gas overhead por iteración\n    proceso(sload(add(base, i)))\n}\n\n\u002F\u002F Desenrollado: sin overhead de bucle\nproceso(sload(base))\nproceso(sload(add(base, 1)))\nproceso(sload(add(base, 2)))\nproceso(sload(add(base, 3)))\nproceso(sload(add(base, 4)))\n```\n\nEl desenrollado ahorra ~16 gas por iteración eliminada, pero aumenta el tamaño del bytecode. Úsalo solo cuando el conteo de iteraciones es fijo y pequeño (\u003C 8).\n\n## Patrones avanzados\n\n### Búsqueda binaria en array ordenado\n```yul\nfunction busquedaBinaria(base, len, objetivo) -> encontrado, indice {\n    let bajo := 0\n    let alto := len\n    \n    for {} lt(bajo, alto) {} {\n        let medio := shr(1, add(bajo, alto))\n        let valor := sload(add(base, medio))\n        \n        switch lt(valor, objetivo)\n        case 1 { bajo := add(medio, 1) }\n        default {\n            switch gt(valor, objetivo)\n            case 1 { alto := medio }\n            default {\n                encontrado := 1\n                indice := medio\n                bajo := alto \u002F\u002F Salir del bucle\n            }\n        }\n    }\n}\n```\n\n## Conclusión\n\nEl flujo de control eficiente en Yul se reduce a minimizar opcodes JUMP (8 gas) y JUMPI (10 gas), cachear valores del storage, y elegir la fuente de datos correcta (calldata > memory > storage). Dominar estos patrones es fundamental para escribir contratos Yul de alto rendimiento.","\u003Ch2 id=\"flujo-de-control-en-yul\">Flujo de control en Yul\u003C\u002Fh2>\n\u003Cp>Yul proporciona tres construcciones de flujo de control: \u003Ccode>if\u003C\u002Fcode>, \u003Ccode>switch\u003C\u002Fcode>, y \u003Ccode>for\u003C\u002Fcode>. Cada una compila a diferentes patrones de opcodes JUMP y JUMPI, con implicaciones distintas en el coste de gas.\u003C\u002Fp>\n\u003Ch2 id=\"el-condicional-if\">El condicional if\u003C\u002Fh2>\n\u003Cp>El \u003Ccode>if\u003C\u002Fcode> en Yul no tiene \u003Ccode>else\u003C\u002Fcode>. Para bifurcaciones completas, usa \u003Ccode>switch\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">assembly {\n    let x := calldataload(0)\n    \n    \u002F\u002F If simple — sin else\n    if gt(x, 100) {\n        x := 100 \u002F\u002F Cap en 100\n    }\n    \n    \u002F\u002F Para if\u002Felse, usa switch\n    switch gt(x, 50)\n    case 1 {\n        \u002F\u002F x &gt; 50\n        mstore(0x00, 1)\n    }\n    default {\n        \u002F\u002F x &lt;= 50\n        mstore(0x00, 0)\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"switch-case-para-despacho-de-funciones\">Switch\u002Fcase para despacho de funciones\u003C\u002Fh2>\n\u003Cp>El switch es especialmente útil para despacho de funciones, que es el patrón más común en contratos Yul standalone:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">switch shr(224, calldataload(0))\ncase 0x70a08231 \u002F* balanceOf *\u002F {\n    returnUint(sload(mappingSlot(calldataload(4), 0)))\n}\ncase 0xa9059cbb \u002F* transfer *\u002F {\n    ejecutarTransfer(calldataload(4), calldataload(36))\n}\ncase 0x18160ddd \u002F* totalSupply *\u002F {\n    returnUint(sload(0))\n}\ndefault {\n    revert(0, 0)\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Cada \u003Ccode>case\u003C\u002Fcode> compila a un PUSH4 + EQ + PUSH2 + JUMPI. Con N funciones, el peor caso es N comparaciones. Para contratos con muchas funciones, esto es O(N) — las jump tables de Huff son más eficientes.\u003C\u002Fp>\n\u003Ch2 id=\"bucles-for\">Bucles for\u003C\u002Fh2>\n\u003Cp>El bucle \u003Ccode>for\u003C\u002Fcode> en Yul tiene la misma estructura que en C:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">assembly {\n    \u002F\u002F for (init; cond; post) { body }\n    for { let i := 0 } lt(i, 10) { i := add(i, 1) } {\n        \u002F\u002F cuerpo del bucle\n        mstore(add(0x80, mul(i, 32)), i)\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Optimización: incremento pre vs post\u003C\u002Fh3>\n\u003Cp>En Solidity, \u003Ccode>++i\u003C\u002Fcode> es más barato que \u003Ccode>i++\u003C\u002Fcode> porque evita una copia temporal. En Yul, esto no importa — \u003Ccode>add(i, 1)\u003C\u002Fcode> es todo lo que hay.\u003C\u002Fp>\n\u003Ch3>Optimización: cachear la longitud\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F Malo: lee array.length de storage en cada iteración\nfor { let i := 0 } lt(i, sload(lengthSlot)) { i := add(i, 1) } {\n    \u002F\u002F sload cada iteración = 100 gas (caliente) * N\n}\n\n\u002F\u002F Bueno: cachear en variable local\nlet len := sload(lengthSlot) \u002F\u002F 2100 gas una vez (frío)\nfor { let i := 0 } lt(i, len) { i := add(i, 1) } {\n    \u002F\u002F lt usa variable de stack = 3 gas * N\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"iteraci-n-sobre-arrays-de-storage\">Iteración sobre arrays de storage\u003C\u002Fh2>\n\u003Cp>Los arrays dinámicos en Solidity almacenan la longitud en el slot base y los elementos a partir de keccak256(slot):\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">assembly {\n    \u002F\u002F Array dinámico en slot 2\n    let slotBase := 2\n    let length := sload(slotBase)\n    \n    \u002F\u002F Los elementos comienzan en keccak256(slotBase)\n    mstore(0x00, slotBase)\n    let dataSlot := keccak256(0x00, 0x20)\n    \n    let suma := 0\n    for { let i := 0 } lt(i, length) { i := add(i, 1) } {\n        suma := add(suma, sload(add(dataSlot, i)))\n    }\n    \n    mstore(0x00, suma)\n    return(0x00, 0x20)\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"iteraci-n-sobre-calldata\">Iteración sobre calldata\u003C\u002Fh2>\n\u003Cp>Calldata es la fuente de datos más barata para iterar:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">assembly {\n    \u002F\u002F Función: sumarArray(uint256[] calldata nums)\n    \u002F\u002F Calldata layout: [selector(4)] [offset(32)] [length(32)] [elem0(32)] [elem1(32)] ...\n    let offset := add(calldataload(4), 4) \u002F\u002F Offset del array\n    let length := calldataload(offset)\n    let dataStart := add(offset, 32)\n    \n    let suma := 0\n    for { let i := 0 } lt(i, length) { i := add(i, 1) } {\n        suma := add(suma, calldataload(add(dataStart, mul(i, 32))))\n    }\n    \n    mstore(0x00, suma)\n    return(0x00, 0x20)\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Cada \u003Ccode>calldataload\u003C\u002Fcode> cuesta solo 3 gas, comparado con 100+ gas para \u003Ccode>sload\u003C\u002Fcode> caliente.\u003C\u002Fp>\n\u003Ch2 id=\"desenrollado-de-bucles\">Desenrollado de bucles\u003C\u002Fh2>\n\u003Cp>Para bucles con conteo conocido y pequeño, el desenrollado manual puede ahorrar el overhead del condicional y el incremento:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F Bucle normal: 5 iteraciones con overhead por iteración\nfor { let i := 0 } lt(i, 5) { i := add(i, 1) } {\n    \u002F\u002F lt(3) + add(3) + jumpi(10) = 16 gas overhead por iteración\n    proceso(sload(add(base, i)))\n}\n\n\u002F\u002F Desenrollado: sin overhead de bucle\nproceso(sload(base))\nproceso(sload(add(base, 1)))\nproceso(sload(add(base, 2)))\nproceso(sload(add(base, 3)))\nproceso(sload(add(base, 4)))\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>El desenrollado ahorra ~16 gas por iteración eliminada, pero aumenta el tamaño del bytecode. Úsalo solo cuando el conteo de iteraciones es fijo y pequeño (&lt; 8).\u003C\u002Fp>\n\u003Ch2 id=\"patrones-avanzados\">Patrones avanzados\u003C\u002Fh2>\n\u003Ch3>Búsqueda binaria en array ordenado\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yul\">function busquedaBinaria(base, len, objetivo) -&gt; encontrado, indice {\n    let bajo := 0\n    let alto := len\n    \n    for {} lt(bajo, alto) {} {\n        let medio := shr(1, add(bajo, alto))\n        let valor := sload(add(base, medio))\n        \n        switch lt(valor, objetivo)\n        case 1 { bajo := add(medio, 1) }\n        default {\n            switch gt(valor, objetivo)\n            case 1 { alto := medio }\n            default {\n                encontrado := 1\n                indice := medio\n                bajo := alto \u002F\u002F Salir del bucle\n            }\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"conclusi-n\">Conclusión\u003C\u002Fh2>\n\u003Cp>El flujo de control eficiente en Yul se reduce a minimizar opcodes JUMP (8 gas) y JUMPI (10 gas), cachear valores del storage, y elegir la fuente de datos correcta (calldata &gt; memory &gt; storage). Dominar estos patrones es fundamental para escribir contratos Yul de alto rendimiento.\u003C\u002Fp>\n","es","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:31.115739Z","Domina el flujo de control en Yul: bucles for, switch\u002Fcase, desenrollado de bucles y patrones para iterar sobre storage y calldata eficientemente.","Yul bucles gas eficiente",null,"index, follow",[21,26,30,34],{"id":22,"name":23,"slug":24,"created_at":25},"c0000000-0000-0000-0000-000000000016","EVM","evm","2026-03-28T10:44:21.513630Z",{"id":27,"name":28,"slug":29,"created_at":25},"c0000000-0000-0000-0000-000000000020","Gas Optimization","gas-optimization",{"id":31,"name":32,"slug":33,"created_at":25},"c0000000-0000-0000-0000-000000000014","Solidity","solidity",{"id":35,"name":36,"slug":37,"created_at":25},"c0000000-0000-0000-0000-000000000018","Yul","yul","Blockchain",[40,46,52],{"id":41,"title":42,"slug":43,"excerpt":44,"locale":12,"category_name":38,"published_at":45},"d0000000-0000-0000-0000-000000000614","La capa de interoperabilidad de Ethereum: Como 55+ L2s se convierten en una sola cadena","capa-interoperabilidad-ethereum-55-l2s-una-sola-cadena","Ethereum tiene 55+ rollups Layer 2, fragmentando la liquidez y la experiencia del usuario. La capa de interoperabilidad de Ethereum — combinando mensajeria cross-rollup, secuenciadores compartidos y based rollups — busca unificarlos en una red componible unica.","2026-03-28T10:44:45.451917Z",{"id":47,"title":48,"slug":49,"excerpt":50,"locale":12,"category_name":38,"published_at":51},"d0000000-0000-0000-0000-000000000613","Pruebas ZK mas alla de los rollups: Inferencia de IA verificable en Ethereum","pruebas-zk-mas-alla-rollups-inferencia-ia-verificable-ethereum","Las pruebas de conocimiento cero ya no son solo una herramienta de escalabilidad. En 2026, zkML permite la inferencia de IA verificable on-chain, los coprocesadores ZK mueven el calculo pesado off-chain con verificacion on-chain, y nuevos sistemas de prueba como SP1 y Jolt lo hacen practico.","2026-03-28T10:44:45.446211Z",{"id":53,"title":54,"slug":55,"excerpt":56,"locale":12,"category_name":38,"published_at":57},"d0000000-0000-0000-0000-000000000590","EIP-7702 en la practica: construir flujos de cuenta inteligente despues de Pectra","eip-7702-en-la-practica-construir-flujos-cuenta-inteligente-despues-pectra","EIP-7702 permite a cualquier EOA de Ethereum actuar temporalmente como contrato inteligente en una sola transaccion. Asi se implementan transacciones por lotes, patrocinio de gas y recuperacion social con la nueva primitiva de account abstraction.","2026-03-28T10:44:43.986612Z",{"id":13,"name":59,"slug":60,"bio":61,"photo_url":18,"linkedin":18,"role":62,"created_at":63,"updated_at":63},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]