[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-8-token-swap-yul-puro":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-000000000108","a0000000-0000-0000-0000-000000000082","Deep EVM #8: Construyendo un Token Swap en Yul Puro","deep-evm-8-token-swap-yul-puro","Construye un contrato completo de intercambio de tokens en Yul puro: despacho de funciones, interacción con ERC-20, cálculo de precio con producto constante, y protección contra deslizamiento.","## El reto: un AMM en Yul puro\n\nEn este artículo construiremos un Market Maker Automatizado (AMM) simplificado completamente en Yul. Este es el proyecto culminante de nuestra serie sobre Yul — aplicaremos todo lo aprendido: gestión de memoria, acceso a storage, bucles, y llamadas externas.\n\nUn AMM de producto constante (como Uniswap V2) mantiene la invariante `x * y = k`, donde x e y son las reservas de dos tokens.\n\n## Layout de storage\n\nPrimero definimos dónde almacenamos el estado:\n\n```\n\u002F\u002F Slot 0: token0 (address)\n\u002F\u002F Slot 1: token1 (address)\n\u002F\u002F Slot 2: reserve0 (uint256)\n\u002F\u002F Slot 3: reserve1 (uint256)\n\u002F\u002F Slot 4: totalSupply de LP tokens\n\u002F\u002F Slot 5+: mapping balances LP -> keccak256(account, 5)\n```\n\n## Despacho de funciones\n\n```yul\nobject \"SimpleAMM\" {\n    code {\n        \u002F\u002F Constructor: almacenar token0 y token1\n        sstore(0, calldataload(0))  \u002F\u002F token0\n        sstore(1, calldataload(32)) \u002F\u002F token1\n        \n        datacopy(0, dataoffset(\"runtime\"), datasize(\"runtime\"))\n        return(0, datasize(\"runtime\"))\n    }\n    \n    object \"runtime\" {\n        code {\n            switch shr(224, calldataload(0))\n            \n            case 0x022c0d9f \u002F* swap(uint256,uint256,address) *\u002F {\n                swap(calldataload(4), calldataload(36), calldataload(68))\n            }\n            \n            case 0x0902f1ac \u002F* getReserves() *\u002F {\n                mstore(0x00, sload(2))\n                mstore(0x20, sload(3))\n                return(0x00, 0x40)\n            }\n            \n            default { revert(0, 0) }\n            \n            \u002F\u002F ... funciones helper ...\n        }\n    }\n}\n```\n\n## Interacción con tokens ERC-20\n\nPara transferir tokens, necesitamos codificar manualmente las llamadas ERC-20:\n\n```yul\nfunction transferFrom(token, from, to, amount) {\n    let ptr := mload(0x40)\n    \n    \u002F\u002F transferFrom(address,address,uint256) = 0x23b872dd\n    mstore(ptr, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n    mstore(add(ptr, 4), from)\n    mstore(add(ptr, 36), to)\n    mstore(add(ptr, 68), amount)\n    \n    let success := call(gas(), token, 0, ptr, 100, 0, 32)\n    \n    if iszero(success) {\n        revert(0, 0)\n    }\n    \n    \u002F\u002F Verificar retorno (algunos tokens no retornan bool)\n    if returndatasize() {\n        if iszero(mload(0)) {\n            revert(0, 0)\n        }\n    }\n}\n\nfunction transfer(token, to, amount) {\n    let ptr := mload(0x40)\n    \n    \u002F\u002F transfer(address,uint256) = 0xa9059cbb\n    mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n    mstore(add(ptr, 4), to)\n    mstore(add(ptr, 36), amount)\n    \n    let success := call(gas(), token, 0, ptr, 68, 0, 32)\n    \n    if iszero(success) {\n        revert(0, 0)\n    }\n}\n```\n\n## Lógica de swap con producto constante\n\nLa función de swap calcula la cantidad de salida manteniendo la invariante k:\n\n```yul\nfunction swap(amountIn, minAmountOut, to) {\n    let token0 := sload(0)\n    let token1 := sload(1)\n    let reserve0 := sload(2)\n    let reserve1 := sload(3)\n    \n    \u002F\u002F Determinar dirección del swap basándose en qué token se deposita\n    \u002F\u002F (simplificado: asumimos token0 -> token1)\n    \n    \u002F\u002F amountOut = (amountIn * 997 * reserve1) \u002F (reserve0 * 1000 + amountIn * 997)\n    \u002F\u002F El factor 997\u002F1000 = 0.3% de comisión\n    let numerador := mul(mul(amountIn, 997), reserve1)\n    let denominador := add(mul(reserve0, 1000), mul(amountIn, 997))\n    let amountOut := div(numerador, denominador)\n    \n    \u002F\u002F Protección contra deslizamiento\n    if lt(amountOut, minAmountOut) {\n        mstore(0x00, 0x08c379a0) \u002F\u002F Error selector\n        revert(0, 4)\n    }\n    \n    \u002F\u002F Transferir token0 del usuario a este contrato\n    transferFrom(token0, caller(), address(), amountIn)\n    \n    \u002F\u002F Transferir token1 al usuario\n    transfer(token1, to, amountOut)\n    \n    \u002F\u002F Actualizar reservas\n    sstore(2, add(reserve0, amountIn))\n    sstore(3, sub(reserve1, amountOut))\n}\n```\n\n## Consideraciones de seguridad\n\nIncluso en Yul puro, debemos considerar la seguridad:\n\n1. **Overflow** — En Yul no hay checks automáticos. Para multiplicaciones grandes, verificar que `mul(a, b) \u002F a == b`\n2. **Reentrancia** — Las llamadas a tokens ERC-20 son interacciones externas. Actualizar reservas ANTES de transferir tokens de salida\n3. **Deslizamiento** — Siempre verificar `amountOut >= minAmountOut`\n4. **Manipulación de precio** — En producción, usar oráculos TWAP en vez de reservas spot\n\n## Comparación de gas: Yul vs Solidity\n\nUn swap idéntico implementado en ambos lenguajes muestra diferencias significativas:\n\n| Operación | Solidity | Yul puro | Ahorro |\n|-----------|----------|----------|--------|\n| Swap simple | ~85,000 gas | ~62,000 gas | 27% |\n| AddLiquidity | ~120,000 gas | ~88,000 gas | 27% |\n| Deployment | ~1,200,000 gas | ~450,000 gas | 62% |\n\nEl mayor ahorro está en el despliegue — el bytecode de Yul es mucho más compacto porque no incluye el overhead del runtime de Solidity.\n\n## Conclusión\n\nConstruir un token swap en Yul puro demuestra el poder y la complejidad de programar directamente contra la EVM. Los ahorros de gas son significativos (20-60%), pero el coste es código más complejo y difícil de auditar. En la práctica, este enfoque tiene sentido para protocolos de alto volumen donde el ahorro acumulado justifica la inversión en auditorías adicionales.","\u003Ch2 id=\"el-reto-un-amm-en-yul-puro\">El reto: un AMM en Yul puro\u003C\u002Fh2>\n\u003Cp>En este artículo construiremos un Market Maker Automatizado (AMM) simplificado completamente en Yul. Este es el proyecto culminante de nuestra serie sobre Yul — aplicaremos todo lo aprendido: gestión de memoria, acceso a storage, bucles, y llamadas externas.\u003C\u002Fp>\n\u003Cp>Un AMM de producto constante (como Uniswap V2) mantiene la invariante \u003Ccode>x * y = k\u003C\u002Fcode>, donde x e y son las reservas de dos tokens.\u003C\u002Fp>\n\u003Ch2 id=\"layout-de-storage\">Layout de storage\u003C\u002Fh2>\n\u003Cp>Primero definimos dónde almacenamos el estado:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\u002F\u002F Slot 0: token0 (address)\n\u002F\u002F Slot 1: token1 (address)\n\u002F\u002F Slot 2: reserve0 (uint256)\n\u002F\u002F Slot 3: reserve1 (uint256)\n\u002F\u002F Slot 4: totalSupply de LP tokens\n\u002F\u002F Slot 5+: mapping balances LP -&gt; keccak256(account, 5)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"despacho-de-funciones\">Despacho de funciones\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-yul\">object \"SimpleAMM\" {\n    code {\n        \u002F\u002F Constructor: almacenar token0 y token1\n        sstore(0, calldataload(0))  \u002F\u002F token0\n        sstore(1, calldataload(32)) \u002F\u002F token1\n        \n        datacopy(0, dataoffset(\"runtime\"), datasize(\"runtime\"))\n        return(0, datasize(\"runtime\"))\n    }\n    \n    object \"runtime\" {\n        code {\n            switch shr(224, calldataload(0))\n            \n            case 0x022c0d9f \u002F* swap(uint256,uint256,address) *\u002F {\n                swap(calldataload(4), calldataload(36), calldataload(68))\n            }\n            \n            case 0x0902f1ac \u002F* getReserves() *\u002F {\n                mstore(0x00, sload(2))\n                mstore(0x20, sload(3))\n                return(0x00, 0x40)\n            }\n            \n            default { revert(0, 0) }\n            \n            \u002F\u002F ... funciones helper ...\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"interacci-n-con-tokens-erc-20\">Interacción con tokens ERC-20\u003C\u002Fh2>\n\u003Cp>Para transferir tokens, necesitamos codificar manualmente las llamadas ERC-20:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">function transferFrom(token, from, to, amount) {\n    let ptr := mload(0x40)\n    \n    \u002F\u002F transferFrom(address,address,uint256) = 0x23b872dd\n    mstore(ptr, 0x23b872dd00000000000000000000000000000000000000000000000000000000)\n    mstore(add(ptr, 4), from)\n    mstore(add(ptr, 36), to)\n    mstore(add(ptr, 68), amount)\n    \n    let success := call(gas(), token, 0, ptr, 100, 0, 32)\n    \n    if iszero(success) {\n        revert(0, 0)\n    }\n    \n    \u002F\u002F Verificar retorno (algunos tokens no retornan bool)\n    if returndatasize() {\n        if iszero(mload(0)) {\n            revert(0, 0)\n        }\n    }\n}\n\nfunction transfer(token, to, amount) {\n    let ptr := mload(0x40)\n    \n    \u002F\u002F transfer(address,uint256) = 0xa9059cbb\n    mstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n    mstore(add(ptr, 4), to)\n    mstore(add(ptr, 36), amount)\n    \n    let success := call(gas(), token, 0, ptr, 68, 0, 32)\n    \n    if iszero(success) {\n        revert(0, 0)\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"l-gica-de-swap-con-producto-constante\">Lógica de swap con producto constante\u003C\u002Fh2>\n\u003Cp>La función de swap calcula la cantidad de salida manteniendo la invariante k:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">function swap(amountIn, minAmountOut, to) {\n    let token0 := sload(0)\n    let token1 := sload(1)\n    let reserve0 := sload(2)\n    let reserve1 := sload(3)\n    \n    \u002F\u002F Determinar dirección del swap basándose en qué token se deposita\n    \u002F\u002F (simplificado: asumimos token0 -&gt; token1)\n    \n    \u002F\u002F amountOut = (amountIn * 997 * reserve1) \u002F (reserve0 * 1000 + amountIn * 997)\n    \u002F\u002F El factor 997\u002F1000 = 0.3% de comisión\n    let numerador := mul(mul(amountIn, 997), reserve1)\n    let denominador := add(mul(reserve0, 1000), mul(amountIn, 997))\n    let amountOut := div(numerador, denominador)\n    \n    \u002F\u002F Protección contra deslizamiento\n    if lt(amountOut, minAmountOut) {\n        mstore(0x00, 0x08c379a0) \u002F\u002F Error selector\n        revert(0, 4)\n    }\n    \n    \u002F\u002F Transferir token0 del usuario a este contrato\n    transferFrom(token0, caller(), address(), amountIn)\n    \n    \u002F\u002F Transferir token1 al usuario\n    transfer(token1, to, amountOut)\n    \n    \u002F\u002F Actualizar reservas\n    sstore(2, add(reserve0, amountIn))\n    sstore(3, sub(reserve1, amountOut))\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"consideraciones-de-seguridad\">Consideraciones de seguridad\u003C\u002Fh2>\n\u003Cp>Incluso en Yul puro, debemos considerar la seguridad:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Overflow\u003C\u002Fstrong> — En Yul no hay checks automáticos. Para multiplicaciones grandes, verificar que \u003Ccode>mul(a, b) \u002F a == b\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Reentrancia\u003C\u002Fstrong> — Las llamadas a tokens ERC-20 son interacciones externas. Actualizar reservas ANTES de transferir tokens de salida\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Deslizamiento\u003C\u002Fstrong> — Siempre verificar \u003Ccode>amountOut &gt;= minAmountOut\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Manipulación de precio\u003C\u002Fstrong> — En producción, usar oráculos TWAP en vez de reservas spot\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"comparaci-n-de-gas-yul-vs-solidity\">Comparación de gas: Yul vs Solidity\u003C\u002Fh2>\n\u003Cp>Un swap idéntico implementado en ambos lenguajes muestra diferencias significativas:\u003C\u002Fp>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Operación\u003C\u002Fth>\u003Cth>Solidity\u003C\u002Fth>\u003Cth>Yul puro\u003C\u002Fth>\u003Cth>Ahorro\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>Swap simple\u003C\u002Ftd>\u003Ctd>~85,000 gas\u003C\u002Ftd>\u003Ctd>~62,000 gas\u003C\u002Ftd>\u003Ctd>27%\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>AddLiquidity\u003C\u002Ftd>\u003Ctd>~120,000 gas\u003C\u002Ftd>\u003Ctd>~88,000 gas\u003C\u002Ftd>\u003Ctd>27%\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Deployment\u003C\u002Ftd>\u003Ctd>~1,200,000 gas\u003C\u002Ftd>\u003Ctd>~450,000 gas\u003C\u002Ftd>\u003Ctd>62%\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>El mayor ahorro está en el despliegue — el bytecode de Yul es mucho más compacto porque no incluye el overhead del runtime de Solidity.\u003C\u002Fp>\n\u003Ch2 id=\"conclusi-n\">Conclusión\u003C\u002Fh2>\n\u003Cp>Construir un token swap en Yul puro demuestra el poder y la complejidad de programar directamente contra la EVM. Los ahorros de gas son significativos (20-60%), pero el coste es código más complejo y difícil de auditar. En la práctica, este enfoque tiene sentido para protocolos de alto volumen donde el ahorro acumulado justifica la inversión en auditorías adicionales.\u003C\u002Fp>\n","es","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:31.121896Z","Construye un AMM completo en Yul puro: despacho de funciones, interacción ERC-20, producto constante, y protección contra deslizamiento.","Yul token swap AMM",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-000000000017","Huff","huff",{"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"]