[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-6-yul-speicherverwaltung-mstore-mload":3},{"article":4,"author":54},{"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":34,"related_articles":35},"d7000000-0000-0000-0000-000000000106","a0000000-0000-0000-0000-000000000072","Deep EVM #6: Yul-Speicherverwaltung — mstore, mload und Free Memory Pointer","deep-evm-6-yul-speicherverwaltung-mstore-mload","EVM-Speicherverwaltung in Yul meistern: der Free Memory Pointer, manuelle ABI-Kodierung, Aufbau externer Aufrufdaten und speichereffiziente Muster fuer gaskritische Contracts.","## Memory in der EVM\n\nEVM-Memory ist ein linearer Byte-Array, der bei Adresse 0 beginnt und nach Bedarf waechst. Im Gegensatz zu Storage ist Memory fluechtig — er existiert nur waehrend einer einzelnen Transaktionsausfuehrung. Memory ist billig im Vergleich zu Storage, aber die quadratische Erweiterungskostenformel bestraft grosse Allokationen.\n\n## Die grundlegenden Memory-Opcodes\n\n### MSTORE und MLOAD\n```yul\n\u002F\u002F 32 Bytes an Adresse 0x80 schreiben\nmstore(0x80, 0x42)\n\n\u002F\u002F 32 Bytes von Adresse 0x80 lesen\nlet value := mload(0x80)  \u002F\u002F value = 0x42\n```\n\nMSTORE schreibt immer 32 Bytes. Wenn Sie nur 1 Byte schreiben moechten, verwenden Sie MSTORE8:\n```yul\nmstore8(0x80, 0xff)  \u002F\u002F Schreibt nur 1 Byte\n```\n\n### CALLDATACOPY und RETURNDATACOPY\n```yul\n\u002F\u002F Calldata in Memory kopieren\ncalldatacopy(0x80, 4, 32)  \u002F\u002F 32 Bytes ab Calldata-Offset 4 nach Memory 0x80\n\n\u002F\u002F Rueckgabedaten in Memory kopieren\nreturndatacopy(0x80, 0, returndatasize())  \u002F\u002F Alle Rueckgabedaten nach 0x80\n```\n\n## Der Free Memory Pointer\n\nSolidity speichert einen Free Memory Pointer an Adresse 0x40. Dieser zeigt auf das naechste freie Byte im Memory:\n\n```yul\n\u002F\u002F Free Memory Pointer lesen\nlet ptr := mload(0x40)  \u002F\u002F Anfangswert: 0x80\n\n\u002F\u002F Memory allozieren (64 Bytes)\nlet newPtr := add(ptr, 64)\nmstore(0x40, newPtr)  \u002F\u002F Free Memory Pointer aktualisieren\n\n\u002F\u002F Jetzt koennen Sie ptr bis ptr+63 sicher verwenden\nmstore(ptr, someValue)\nmstore(add(ptr, 32), anotherValue)\n```\n\nWichtig: Wenn Sie den Free Memory Pointer nicht aktualisieren, kann nachfolgender Solidity-Code Ihre Daten ueberschreiben. Wenn Sie vollstaendig in Yul arbeiten (kein Solidity-Wrapper), koennen Sie den Pointer ignorieren und das Memory frei verwalten.\n\n## Memory-Layout von Solidity\n\n```\n\u002F\u002F Reservierte Bereiche:\n\u002F\u002F 0x00 - 0x3f: Scratch Space (64 Bytes) — temporaerer Arbeitsbereich fuer Hashing usw.\n\u002F\u002F 0x40 - 0x5f: Free Memory Pointer (32 Bytes)\n\u002F\u002F 0x60 - 0x7f: Zero Slot (32 Bytes) — immer Null\n\u002F\u002F 0x80 - ...:  Freier Bereich — hier beginnen Allokationen\n```\n\nDer Scratch Space (0x00-0x3f) ist besonders nuetzlich: Sie koennen ihn fuer temporaere Berechnungen verwenden, ohne den Free Memory Pointer zu beruehren:\n\n```yul\n\u002F\u002F keccak256 eines einzelnen Werts (kostenguenstig):\nmstore(0x00, someValue)\nlet hash := keccak256(0x00, 32)\n\u002F\u002F Kein Pointer-Update noetig!\n```\n\n## Manuelle ABI-Kodierung\n\nIn Yul muessen Sie Funktionsaufrufe manuell ABI-kodieren:\n\n```yul\n\u002F\u002F ERC-20 transfer(address,uint256) aufrufen\nlet ptr := mload(0x40)\n\n\u002F\u002F Funktionsselektor: bytes4(keccak256(\"transfer(address,uint256)\"))\nmstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n\n\u002F\u002F Erstes Argument: Adresse (links auf 32 Bytes gepolstert)\nmstore(add(ptr, 4), and(recipient, 0xffffffffffffffffffffffffffffffffffffffff))\n\n\u002F\u002F Zweites Argument: Betrag\nmstore(add(ptr, 36), amount)\n\n\u002F\u002F Externen Aufruf durchfuehren\nlet success := call(\n    gas(),          \u002F\u002F Alles verbleibendes Gas weitergeben\n    tokenAddress,   \u002F\u002F Ziel-Contract\n    0,              \u002F\u002F Kein ETH-Wert\n    ptr,            \u002F\u002F Input-Daten Start\n    68,             \u002F\u002F Input-Daten Laenge (4 + 32 + 32)\n    0x00,           \u002F\u002F Output Start\n    32              \u002F\u002F Output Laenge\n)\n```\n\n## Speichererweiterungskosten optimieren\n\nDie quadratische Kostenerweiterung bedeutet, dass Sie Memory sparsam verwenden sollten:\n\n```\n\u002F\u002F Kosten fuer Memory-Erweiterung:\n\u002F\u002F Bis 724 Bytes:  ~linear (3 Gas pro Wort)\n\u002F\u002F 1 KB:           ~99 Gas\n\u002F\u002F 10 KB:          ~1.000 Gas\n\u002F\u002F 100 KB:         ~100.000 Gas\n\u002F\u002F 1 MB:           ~3.000.000 Gas\n```\n\nBest Practices:\n\n1. **Memory wiederverwenden** — Wenn Sie temporaere Puffer brauchen, verwenden Sie den Scratch Space oder denselben Memory-Bereich mehrmals.\n2. **Nicht mehr allozieren als noetig** — Lesen Sie nur die Bytes, die Sie brauchen.\n3. **Grosse Rueckgabedaten vermeiden** — Jedes Byte kostet Erweiterung.\n\n## Fortgeschrittene Muster\n\n### Mehrere externe Aufrufe mit gemeinsamem Puffer\n```yul\n\u002F\u002F Einen Memory-Bereich fuer mehrere Aufrufe wiederverwenden\nlet buf := 0x80  \u002F\u002F Fester Puffer statt Allokation\n\n\u002F\u002F Aufruf 1\nmstore(buf, selector1)\nmstore(add(buf, 4), arg1)\npop(staticcall(gas(), target1, buf, 36, buf, 32))\nlet result1 := mload(buf)\n\n\u002F\u002F Aufruf 2 — gleicher Puffer!\nmstore(buf, selector2)\nmstore(add(buf, 4), arg2)\npop(staticcall(gas(), target2, buf, 36, buf, 32))\nlet result2 := mload(buf)\n```\n\n### Memory-effiziente Hashberechnung\n```yul\n\u002F\u002F Zwei Werte hashen, ohne neuen Memory zu allozieren\nmstore(0x00, value1)\nmstore(0x20, value2)\nlet hash := keccak256(0x00, 64)\n```\n\n## Fazit\n\nDie Speicherverwaltung in Yul erfordert ein tiefes Verstaendnis des EVM-Memory-Layouts, der Erweiterungskosten und des Free Memory Pointers. Durch effiziente Nutzung des Scratch Space, Wiederverwendung von Puffern und minimale Allokationen koennen Sie den Gasverbrauch Ihrer Contracts erheblich reduzieren. Im naechsten Artikel werden wir diese Konzepte anwenden, um gaseffiziente Schleifen und Verzweigungen in Yul zu implementieren.","\u003Ch2 id=\"memory-in-der-evm\">Memory in der EVM\u003C\u002Fh2>\n\u003Cp>EVM-Memory ist ein linearer Byte-Array, der bei Adresse 0 beginnt und nach Bedarf waechst. Im Gegensatz zu Storage ist Memory fluechtig — er existiert nur waehrend einer einzelnen Transaktionsausfuehrung. Memory ist billig im Vergleich zu Storage, aber die quadratische Erweiterungskostenformel bestraft grosse Allokationen.\u003C\u002Fp>\n\u003Ch2 id=\"die-grundlegenden-memory-opcodes\">Die grundlegenden Memory-Opcodes\u003C\u002Fh2>\n\u003Ch3>MSTORE und MLOAD\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F 32 Bytes an Adresse 0x80 schreiben\nmstore(0x80, 0x42)\n\n\u002F\u002F 32 Bytes von Adresse 0x80 lesen\nlet value := mload(0x80)  \u002F\u002F value = 0x42\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>MSTORE schreibt immer 32 Bytes. Wenn Sie nur 1 Byte schreiben moechten, verwenden Sie MSTORE8:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">mstore8(0x80, 0xff)  \u002F\u002F Schreibt nur 1 Byte\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>CALLDATACOPY und RETURNDATACOPY\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F Calldata in Memory kopieren\ncalldatacopy(0x80, 4, 32)  \u002F\u002F 32 Bytes ab Calldata-Offset 4 nach Memory 0x80\n\n\u002F\u002F Rueckgabedaten in Memory kopieren\nreturndatacopy(0x80, 0, returndatasize())  \u002F\u002F Alle Rueckgabedaten nach 0x80\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"der-free-memory-pointer\">Der Free Memory Pointer\u003C\u002Fh2>\n\u003Cp>Solidity speichert einen Free Memory Pointer an Adresse 0x40. Dieser zeigt auf das naechste freie Byte im Memory:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F Free Memory Pointer lesen\nlet ptr := mload(0x40)  \u002F\u002F Anfangswert: 0x80\n\n\u002F\u002F Memory allozieren (64 Bytes)\nlet newPtr := add(ptr, 64)\nmstore(0x40, newPtr)  \u002F\u002F Free Memory Pointer aktualisieren\n\n\u002F\u002F Jetzt koennen Sie ptr bis ptr+63 sicher verwenden\nmstore(ptr, someValue)\nmstore(add(ptr, 32), anotherValue)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Wichtig: Wenn Sie den Free Memory Pointer nicht aktualisieren, kann nachfolgender Solidity-Code Ihre Daten ueberschreiben. Wenn Sie vollstaendig in Yul arbeiten (kein Solidity-Wrapper), koennen Sie den Pointer ignorieren und das Memory frei verwalten.\u003C\u002Fp>\n\u003Ch2 id=\"memory-layout-von-solidity\">Memory-Layout von Solidity\u003C\u002Fh2>\n\u003Cpre>\u003Ccode>\u002F\u002F Reservierte Bereiche:\n\u002F\u002F 0x00 - 0x3f: Scratch Space (64 Bytes) — temporaerer Arbeitsbereich fuer Hashing usw.\n\u002F\u002F 0x40 - 0x5f: Free Memory Pointer (32 Bytes)\n\u002F\u002F 0x60 - 0x7f: Zero Slot (32 Bytes) — immer Null\n\u002F\u002F 0x80 - ...:  Freier Bereich — hier beginnen Allokationen\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Der Scratch Space (0x00-0x3f) ist besonders nuetzlich: Sie koennen ihn fuer temporaere Berechnungen verwenden, ohne den Free Memory Pointer zu beruehren:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F keccak256 eines einzelnen Werts (kostenguenstig):\nmstore(0x00, someValue)\nlet hash := keccak256(0x00, 32)\n\u002F\u002F Kein Pointer-Update noetig!\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"manuelle-abi-kodierung\">Manuelle ABI-Kodierung\u003C\u002Fh2>\n\u003Cp>In Yul muessen Sie Funktionsaufrufe manuell ABI-kodieren:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F ERC-20 transfer(address,uint256) aufrufen\nlet ptr := mload(0x40)\n\n\u002F\u002F Funktionsselektor: bytes4(keccak256(\"transfer(address,uint256)\"))\nmstore(ptr, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)\n\n\u002F\u002F Erstes Argument: Adresse (links auf 32 Bytes gepolstert)\nmstore(add(ptr, 4), and(recipient, 0xffffffffffffffffffffffffffffffffffffffff))\n\n\u002F\u002F Zweites Argument: Betrag\nmstore(add(ptr, 36), amount)\n\n\u002F\u002F Externen Aufruf durchfuehren\nlet success := call(\n    gas(),          \u002F\u002F Alles verbleibendes Gas weitergeben\n    tokenAddress,   \u002F\u002F Ziel-Contract\n    0,              \u002F\u002F Kein ETH-Wert\n    ptr,            \u002F\u002F Input-Daten Start\n    68,             \u002F\u002F Input-Daten Laenge (4 + 32 + 32)\n    0x00,           \u002F\u002F Output Start\n    32              \u002F\u002F Output Laenge\n)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"speichererweiterungskosten-optimieren\">Speichererweiterungskosten optimieren\u003C\u002Fh2>\n\u003Cp>Die quadratische Kostenerweiterung bedeutet, dass Sie Memory sparsam verwenden sollten:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\u002F\u002F Kosten fuer Memory-Erweiterung:\n\u002F\u002F Bis 724 Bytes:  ~linear (3 Gas pro Wort)\n\u002F\u002F 1 KB:           ~99 Gas\n\u002F\u002F 10 KB:          ~1.000 Gas\n\u002F\u002F 100 KB:         ~100.000 Gas\n\u002F\u002F 1 MB:           ~3.000.000 Gas\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Best Practices:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>Memory wiederverwenden\u003C\u002Fstrong> — Wenn Sie temporaere Puffer brauchen, verwenden Sie den Scratch Space oder denselben Memory-Bereich mehrmals.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Nicht mehr allozieren als noetig\u003C\u002Fstrong> — Lesen Sie nur die Bytes, die Sie brauchen.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Grosse Rueckgabedaten vermeiden\u003C\u002Fstrong> — Jedes Byte kostet Erweiterung.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2 id=\"fortgeschrittene-muster\">Fortgeschrittene Muster\u003C\u002Fh2>\n\u003Ch3>Mehrere externe Aufrufe mit gemeinsamem Puffer\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F Einen Memory-Bereich fuer mehrere Aufrufe wiederverwenden\nlet buf := 0x80  \u002F\u002F Fester Puffer statt Allokation\n\n\u002F\u002F Aufruf 1\nmstore(buf, selector1)\nmstore(add(buf, 4), arg1)\npop(staticcall(gas(), target1, buf, 36, buf, 32))\nlet result1 := mload(buf)\n\n\u002F\u002F Aufruf 2 — gleicher Puffer!\nmstore(buf, selector2)\nmstore(add(buf, 4), arg2)\npop(staticcall(gas(), target2, buf, 36, buf, 32))\nlet result2 := mload(buf)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Memory-effiziente Hashberechnung\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-yul\">\u002F\u002F Zwei Werte hashen, ohne neuen Memory zu allozieren\nmstore(0x00, value1)\nmstore(0x20, value2)\nlet hash := keccak256(0x00, 64)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"fazit\">Fazit\u003C\u002Fh2>\n\u003Cp>Die Speicherverwaltung in Yul erfordert ein tiefes Verstaendnis des EVM-Memory-Layouts, der Erweiterungskosten und des Free Memory Pointers. Durch effiziente Nutzung des Scratch Space, Wiederverwendung von Puffern und minimale Allokationen koennen Sie den Gasverbrauch Ihrer Contracts erheblich reduzieren. Im naechsten Artikel werden wir diese Konzepte anwenden, um gaseffiziente Schleifen und Verzweigungen in Yul zu implementieren.\u003C\u002Fp>\n","de","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:29.933826Z","EVM-Speicherverwaltung in Yul meistern: Free Memory Pointer, manuelle ABI-Kodierung und speichereffiziente Muster fuer gaskritische Contracts.","Yul Speicherverwaltung",null,"index, follow",[21,26,30],{"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-000000000018","Yul","yul","Blockchain",[36,42,48],{"id":37,"title":38,"slug":39,"excerpt":40,"locale":12,"category_name":34,"published_at":41},"d0000000-0000-0000-0000-000000000611","Die Ethereum-Interoperabilitaetsschicht: Wie 55+ L2s zu einer Chain werden","ethereum-interoperabilitaetsschicht-55-l2s-eine-chain","Ethereum hat 55+ Layer-2-Rollups, die Liquiditaet und Nutzererfahrung fragmentieren. Die Ethereum-Interoperabilitaetsschicht — bestehend aus Cross-Rollup-Messaging, Shared Sequencern und Based Rollups — zielt darauf ab, sie zu einem einzigen komponierbaren Netzwerk zu vereinen.","2026-03-28T10:44:45.264352Z",{"id":43,"title":44,"slug":45,"excerpt":46,"locale":12,"category_name":34,"published_at":47},"d0000000-0000-0000-0000-000000000610","ZK-Beweise jenseits von Rollups: Verifizierbare KI-Inferenz auf Ethereum","zk-beweise-jenseits-von-rollups-verifizierbare-ki-inferenz-ethereum","Zero-Knowledge-Beweise sind nicht mehr nur ein Skalierungswerkzeug. Im Jahr 2026 ermoeglicht zkML verifizierbare KI-Inferenz on-chain, ZK-Coprozessoren verlagern schwere Berechnungen off-chain mit On-Chain-Verifizierung, und neue Beweissysteme wie SP1 und Jolt machen es praktikabel.","2026-03-28T10:44:45.257775Z",{"id":49,"title":50,"slug":51,"excerpt":52,"locale":12,"category_name":34,"published_at":53},"d0000000-0000-0000-0000-000000000587","EIP-7702 in der Praxis: Smart-Account-Flows nach Pectra erstellen","eip-7702-in-der-praxis-smart-account-flows-nach-pectra-erstellen","EIP-7702 ermoeglicht jedem Ethereum-EOA, innerhalb einer einzelnen Transaktion voruebergehend als Smart Contract zu agieren. So implementieren Sie Batch-Transaktionen, Gas-Sponsoring und Social Recovery mit dem neuen Account-Abstraction-Primitiv.","2026-03-28T10:44:43.781201Z",{"id":13,"name":55,"slug":56,"bio":57,"photo_url":18,"linkedin":18,"role":58,"created_at":59,"updated_at":59},"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"]