﻿---
title: "การชนกันของพื้นที่จัดเก็บพร็อกซี: การอัปเกรดทำลายสถานะสัญญาอย่างไร"
description: "พร็อกซีเก็บสถานะไว้ขณะที่โค้ด implementation เปลี่ยน เรียนรู้ว่า layout ที่ไม่เข้ากันเขียนทับยอดคงเหลือ owner และการควบคุมอย่างไร พร้อมวิธีตรวจสอบการอัปเกรด"
image: "https://wiki.fcontext.com/og.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://wiki.fcontext.com/llms.txt
> Use this file to discover all available pages before exploring further.

# การชนกันของพื้นที่จัดเก็บพร็อกซี: การอัปเกรดทำลายสถานะสัญญาอย่างไร

> จัดทำขึ้นเพื่อการศึกษาเท่านั้น ไม่ใช่คำแนะนำการลงทุน การลงทุนอาจทำให้สูญเสียเงินลงทุนได้

<a id="answer"></a>

## คำตอบโดยตรง

การชนกันของพื้นที่จัดเก็บพร็อกซีเกิดขึ้นเมื่อโค้ด implementation อ่านหรือเขียนสล็อตของพร็อกซีด้วยความหมายที่ต่างจาก layout ที่สร้างสถานะเดิม การอัปเกรดจึงอาจทำให้ยอดคงเหลือถูกตีความเป็นแอดเดรส ล้างค่า owner ทำลายสล็อตฐานของ mapping หรือเขียนทับข้อมูลควบคุมการอัปเกรด

เมื่อใช้ `delegatecall` ไบต์โค้ดของ implementation จะทำงานในบริบทของพร็อกซี พื้นที่จัดเก็บ ยอดคงเหลือ และ `address(this)` เป็นของพร็อกซี ชื่อตัวแปรไม่ได้ถูกเก็บไว้บนเชน ดังนั้น EVM จึงทำตามเฉพาะสล็อตและออฟเซ็ตไบต์ที่โค้ดใหม่คำนวณ

ด้วยเหตุนี้ การอัปเกรดต้องรักษา layout ที่ติดตั้งอยู่ ไม่ใช่เพียงคอมไพล์ผ่านหรือเปิดเผยฟังก์ชันเดิม ก่อนอนุมัติการอัปเกรด ให้เปรียบเทียบ layout ที่คอมไพเลอร์สร้างกับเวอร์ชันที่ติดตั้งจริงอย่างถูกต้อง

<a id="mechanism"></a>

## วิธีการทำงาน

โดยทั่วไป Solidity วางตัวแปรสถานะตั้งแต่สล็อต `0` ตามลำดับการประกาศหลังทำ C3 linearization ของการสืบทอด ค่าเล็กกว่า 32 ไบต์อาจใช้สล็อตร่วมกันได้ struct และ array มีกฎเพิ่มเติม ส่วน mapping และ dynamic array คำนวณตำแหน่งข้อมูลจากสล็อตฐาน หากสล็อตฐานเลื่อน ตำแหน่งข้อมูลที่คำนวณจากสล็อตนั้นก็เปลี่ยนด้วย

มีขอบเขตการชนกันสี่ประเภทที่ต้องตรวจสอบ:

- **พร็อกซีกับ implementation:** ฟิลด์ของพร็อกซี เช่น implementation หรือ administrator ต้องไม่ใช้สล็อตเดียวกับสถานะของแอปพลิเคชัน ERC-1967 กำหนดสล็อตมาตรฐานที่หลีกจากการจัดสรรปกติของคอมไพเลอร์สำหรับ implementation, beacon และ administrator
- **implementation เก่ากับใหม่:** ตัวแปรเดิมต้องคงสล็อต ออฟเซ็ต และชนิดที่เข้ากันได้ การเพิ่มตัวแปรต่อท้ายอาจปลอดภัย แต่การแทรก สลับลำดับ ลบ หรือเปลี่ยนชนิด อาจทำให้ word เดิมถูกตีความใหม่
- **การสืบทอด:** การเพิ่มสถานะในสัญญาฐานหรือเปลี่ยนลำดับการสืบทอดอาจเลื่อนพื้นที่จัดเก็บของสัญญาลูก แม้ซอร์สโค้ดของสัญญาลูกไม่เปลี่ยน
- **พื้นที่สำรองหรือแบบ namespace:** storage gap ที่ใช้อย่างถูกต้องสำรองพื้นที่ให้สัญญาฐานได้ และ namespace แบบ ERC-7201 แยก layout ได้ แต่ทั้งสองวิธีไม่อนุญาตให้แก้ไขภายใน layout เดิมโดยพลการ

<a id="example"></a>

## ตัวอย่าง

สมมติว่าเวอร์ชัน 1 มี layout ดังนี้:

```solidity
uint256 totalAssets; // slot 0
address owner;       // slot 1
```

เวอร์ชัน 2 แทรกตัวแปรไว้ด้านหน้าอย่างไม่ถูกต้อง:

```solidity
bool paused;         // slot 0, offset 0
uint256 totalAssets; // slot 1
address owner;       // slot 2
```

หลังอัปเกรด `paused` จะอ่านไบต์ล่างของ `totalAssets` เดิม `totalAssets` ใหม่จะอ่าน word ของ `owner` เดิมเป็นจำนวนเต็ม และ `owner` จะอ่านค่าที่มีอยู่ในสล็อต `2` ซึ่งมักเป็นศูนย์ word เดิมยังอยู่ในพื้นที่จัดเก็บ แต่โค้ดใหม่ให้ความหมายต่างออกไป ธุรกรรมอาจสำเร็จทั้งที่ใช้การอนุญาตหรือการบัญชีกับสถานะที่ถูกตีความผิด

<a id="risks"></a>

## ความเสี่ยงและการตรวจสอบการอัปเกรด

- สร้างผลลัพธ์ storage layout ของ implementation ทั้งสอง แล้วเปรียบเทียบสล็อต ออฟเซ็ต ชนิด และการสืบทอดกับสัญญาอ้างอิงที่ติดตั้งจริง
- สำหรับ layout เชิงเส้นแบบเดิม ให้เพิ่มตัวแปรใหม่ต่อท้ายเท่านั้น อย่าสลับฟิลด์ เปลี่ยนชนิด ลบแล้วนำกลับมาใช้ หรือแก้สัญญาฐานโดยไม่พิสูจน์ความเข้ากันได้
- เมื่อใช้ storage gap ให้ลดขนาดตามจำนวนสล็อตสำรองที่ใช้จริงอย่างแม่นยำ สำหรับ namespace ให้ใช้ตัวระบุที่ไม่ซ้ำและตรวจสอบการเปลี่ยนแปลงในทุก namespace เดิม
- อย่าถือว่า ERC-1967 ป้องกันได้ทั้งหมด มาตรฐานนี้แยก metadata ของพร็อกซีออกจากสล็อตแอปพลิเคชันที่คอมไพเลอร์จัดสรร แต่ไม่ได้ทำให้ layout ของ implementation สองชุดเข้ากันได้
- ทดสอบการอัปเกรดและ reinitializer บน fork หรือ snapshot ของสถานะ ตรวจสอบ owner, role, ยอดคงเหลือ, allowance, รายการ mapping, สถานะ pause, สล็อต implementation และการควบคุม rollback หรือฉุกเฉิน ทั้งก่อนและหลังทำงาน

หากสงสัยว่าการอัปเกรดทำให้เกิดการชนกันแล้ว ให้หยุดการอัปเกรดเพิ่มเติมและการเรียกที่เปลี่ยนสถานะเมื่อกลไกกำกับดูแลอนุญาต เก็บหมายเลขบล็อกและไบต์โค้ดก่อนอัปเกรด เปรียบเทียบข้อมูลดิบในสล็อตที่ได้รับผลกระทบ และให้วิศวกรสัญญาที่มีคุณสมบัติออกแบบการย้ายพร้อมรับการตรวจสอบอิสระ การอัปเกรดซ้ำโดยไม่มีแผนผังพื้นที่จัดเก็บที่พิสูจน์แล้วอาจทำลายสถานะที่ยังกู้คืนได้มากขึ้น

<a id="misconceptions"></a>

## ความเข้าใจผิดที่พบบ่อย

- **“ชื่อตัวแปรไม่เปลี่ยน ดังนั้น layout จึงปลอดภัย”** ชื่อไม่ได้กำหนดตำแหน่งพื้นที่จัดเก็บ ชนิด ลำดับ การแพ็ก การสืบทอด และกฎ namespace เป็นตัวกำหนด
- **“การลบตัวแปรทำให้สล็อตว่างสำหรับใช้ใหม่”** พื้นที่จัดเก็บของพร็อกซียังคงอยู่ การใช้ซ้ำคือการให้ความหมายใหม่แก่ word เดิม เว้นแต่การย้ายที่ผ่านการตรวจสอบจะล้างหรือแปลงค่านั้น
- **“ธุรกรรมทดสอบที่สำเร็จพิสูจน์ว่าอัปเกรดเข้ากันได้”** การทดสอบอาจแตะเพียงไม่กี่สล็อต การตรวจ layout และความต่างของสถานะต้องครอบคลุมฟิลด์สิทธิ์สูง ค่าที่แพ็ก mapping, array และพื้นที่จัดเก็บจากการสืบทอด

<a id="related"></a>

## หัวข้อที่เกี่ยวข้อง

- [ความเสี่ยงของพื้นที่จัดเก็บจาก delegatecall](/th/crypto/delegatecall-storage-risk/)
- [การยึด initializer](/th/crypto/initializer-takeover/)
- [สัญญาพร็อกซี](/th/crypto/proxy-contract/)
- [การติดตามการอัปเกรดพร็อกซี](/th/crypto/proxy-upgrade-monitoring/)
- [สัญญาที่อัปเกรดได้](/th/crypto/upgradeable-contract/)

<a id="sources"></a>

## แหล่งที่มา

- [บทนำสู่สัญญาอัจฉริยะ](https://docs.soliditylang.org/en/latest/introduction-to-smart-contracts.html) - Solidity Documentation (เข้าถึง: 2026-08-21)
- [Layout ของตัวแปรสถานะในพื้นที่จัดเก็บ](https://docs.soliditylang.org/en/latest/internals/layout_in_storage.html) - Solidity Documentation (เข้าถึง: 2026-08-21)
- [ERC-1967: สล็อตพื้นที่จัดเก็บพร็อกซี](https://eips.ethereum.org/EIPS/eip-1967) - Ethereum Improvement Proposals (เข้าถึง: 2026-08-21)
- [การเขียนสัญญาที่อัปเกรดได้](https://docs.openzeppelin.com/upgrades-plugins/writing-upgradeable) - OpenZeppelin Documentation (เข้าถึง: 2026-08-21)

Source: https://wiki.fcontext.com/th/crypto/proxy-storage-collision/index.mdx
