จัดทำขึ้นเพื่อการศึกษาเท่านั้น ไม่ใช่คำแนะนำการลงทุน การลงทุนอาจทำให้ขาดทุนได้
คำตอบโดยตรง
delegatecall เรียกใช้โค้ดจากสัญญาเป้าหมายภายในบริบทของสัญญาผู้เรียก สัญญาผู้เรียกยังคงใช้พื้นที่จัดเก็บ ยอดคงเหลือ และ address(this) ของตนเอง ส่วน msg.sender และ msg.value ยังคงมีค่าจากการเรียกครั้งแรก
พฤติกรรมนี้ทำให้สร้างพร็อกซี ไลบรารี และโมดูลกระเป๋าเงินอัจฉริยะได้ แต่ก็ทำให้โค้ดที่ได้รับมอบสิทธิ์มีอำนาจโดยพฤตินัยของสัญญาผู้เรียกด้วย การเขียนข้อมูลลงพื้นที่จัดเก็บ การโอนสินทรัพย์ การอนุมัติ หรือการเรียกสัญญาภายนอกจะดำเนินการในนามของสัญญาผู้เรียก ไม่ใช่เป้าหมายที่ให้โค้ดมา
ให้ถือว่าทุกเป้าหมายที่เข้าถึงได้ผ่าน delegatecall เป็นโค้ดที่มีสิทธิ์ระดับสูง ความปลอดภัยขึ้นอยู่กับกฎการเลือกเป้าหมาย ความเข้ากันได้ของโครงสร้างพื้นที่จัดเก็บ สถานะการเริ่มต้นใช้งาน การควบคุมการอัปเกรด และอิมพลีเมนเทชันที่ทำงานจริงสำหรับธุรกรรมนั้น
วิธีการทำงาน
ในการเรียกสัญญาภายนอกตามปกติ สัญญาที่ถูกเรียกจะอ่านและเขียนพื้นที่จัดเก็บของตนเอง แต่เมื่อใช้ delegatecall ไบต์โค้ดของเป้าหมายจะทำงานกับพื้นที่จัดเก็บของสัญญาผู้เรียก กล่าวคือ คำสั่ง SSTORE จะเปลี่ยนสล็อตของสัญญาผู้เรียก ชื่อตัวแปรของเป้าหมายไม่มีผลขณะรันไทม์ มีเพียงตำแหน่งสล็อตที่คำนวณได้เท่านั้นที่สำคัญ
จึงมีขอบเขตสี่ด้านที่ต้องตรวจสอบ:
- การควบคุมเป้าหมาย: ตรวจสอบว่าปลายทางถูกกำหนดตายตัว เลือกโดยผู้ใช้ ค้นหาผ่านรีจิสทรี หรือเปลี่ยนแปลงได้โดยผู้ดูแลระบบ
- ความเข้ากันได้ของพื้นที่จัดเก็บ: เปรียบเทียบลำดับและชนิดของตัวแปร การสืบทอด ช่องว่างสำรองในพื้นที่จัดเก็บ รวมถึงสล็อตแบบตั้งชื่อหรือแบบมาตรฐานในอิมพลีเมนเทชันทุกเวอร์ชัน
- การเริ่มต้นใช้งานและการอนุญาต: ยืนยันว่าไม่สามารถเรียก initializer ซ้ำได้ และฟังก์ชันอัปเกรดหรือจัดการโมดูลบังคับใช้ผู้เรียกและระยะเวลาหน่วงของการกำกับดูแลตามที่กำหนด
- การจัดการค่าที่ส่งกลับ: ตรวจสอบว่าข้อผิดพลาดถูกส่งต่อ และข้อมูลที่ส่งกลับถูกถอดรหัสเป็นชนิดที่คาดไว้ การเรียกระดับต่ำไม่มีการตรวจสอบชนิดสัญญาตามปกติของ Solidity
ERC-1967 ลดการชนกันในพร็อกซีด้วยการเก็บที่อยู่ของอิมพลีเมนเทชัน beacon และผู้ดูแลระบบไว้ในสล็อตมาตรฐานที่อยู่นอกการจัดสรรตามปกติของคอมไพเลอร์ แต่ไม่ได้พิสูจน์ว่าอิมพลีเมนเทชันปลอดภัยหรือการอัปเกรดที่ได้รับอนุญาตไม่มีอันตราย
ตัวอย่าง
สมมติว่ากระเป๋าเงินเก็บ owner ไว้ใน slot 0 ปลั๊กอินที่คอมไพล์โดยมี counter อยู่ใน slot 0 จะเพิ่มค่าตัวนับเมื่อกระเป๋าเงินเข้าถึงปลั๊กอินผ่าน delegatecall
การเขียนข้อมูลนี้เปลี่ยนค่า owner ของกระเป๋าเงิน เพราะพื้นที่จัดเก็บเป็นของกระเป๋าเงิน หากค่าที่ได้เข้ารหัสที่อยู่ซึ่งผู้โจมตีควบคุม การตรวจสอบสิทธิ์ในภายหลังอาจยอมรับผู้โจมตีเป็นเจ้าของ แม้ว่าปลั๊กอินจะไม่เคยถือครองสินทรัพย์ของกระเป๋าเงินเลย
ใบรับธุรกรรมที่สำเร็จไม่ได้แยกความแตกต่างระหว่างการเปลี่ยนสถานะตามเจตนากับการเปลี่ยนแปลงที่เป็นอันตราย ดังนั้นการจำลองธุรกรรมควรตรวจสอบผลต่างของพื้นที่จัดเก็บ การเปลี่ยนแปลงสินทรัพย์และการอนุมัติ อีเวนต์ที่ถูกปล่อย และการเรียกที่ตามมา โดยอ้างอิงที่อยู่พร็อกซีและอิมพลีเมนเทชันที่ถูกต้องตรงกัน
ความเสี่ยง
- การเรียกใช้เป้าหมายใดก็ได้: เป้าหมายที่ผู้ใช้ควบคุมหรือผ่านการตรวจสอบไม่รัดกุมอาจเรียกใช้โค้ดอันตรายด้วยสิทธิ์ของสัญญาผู้เรียก
- การชนกันของพื้นที่จัดเก็บ: อิมพลีเมนเทชันอาจเขียนทับข้อมูลเจ้าของ ยอดคงเหลือ สถานะหยุดชั่วคราว หรือแม้แต่สล็อตที่ใช้เลือกอิมพลีเมนเทชันถัดไป
- การอัปเกรดที่ไม่ปลอดภัย: ผู้ดูแลระบบหรือกระบวนการกำกับดูแลที่ถูกเจาะระบบอาจแทนที่โค้ดที่เคยผ่านการตรวจสอบแล้ว หลังจากผู้ใช้ฝากสินทรัพย์หรือให้สิทธิ์อนุมัติ
- การเริ่มต้นใช้งานล้มเหลว: พร็อกซีหรืออิมพลีเมนเทชันที่ยังไม่ได้เริ่มต้นใช้งานอาจเปิดทางให้บัญชีอื่นยึดบทบาทที่มีสิทธิ์ระดับสูงหรือกำหนดค่าการพึ่งพาที่เป็นอันตราย
- การตรวจสอบที่ทำให้เข้าใจผิด: การตรวจสอบเฉพาะซอร์สของพร็อกซี อิมพลีเมนเทชันปัจจุบัน หรืออินเทอร์เฟซ อาจพลาด beacon การอัปเกรดที่รอดำเนินการ รีจิสทรีโมดูล หรือเส้นทางการทำงานอื่น
ก่อนลงนาม ให้ระบุอิมพลีเมนเทชัน ณ บล็อกล่าสุด ตรวจสอบว่าใครเปลี่ยนได้และต้องรอนานเท่าใด ตรวจสอบไบต์โค้ดที่ยืนยันแล้วและโครงสร้างพื้นที่จัดเก็บของเป้าหมาย จำลอง calldata ทั้งหมด และเปรียบเทียบพื้นที่จัดเก็บที่ละเอียดอ่อนกับการอนุมัติโทเค็นก่อนและหลังการดำเนินการ สำหรับกระเป๋าเงินอัจฉริยะ ให้ตรวจสอบด้วยว่าโมดูลถูกเปิดใช้ ปิดใช้ และได้รับอนุญาตให้เลือกเป้าหมายอย่างไร
ความเข้าใจผิดที่พบบ่อย
- “เป้าหมายไม่สามารถแตะต้องสินทรัพย์ของสัญญาผู้เรียกได้” โค้ดที่ได้รับมอบสิทธิ์ทำงานในนามของสัญญาผู้เรียก และสามารถเรียกสัญญาภายนอก โอนสินทรัพย์ หรือสร้างการอนุมัติได้ หากสัญญาผู้เรียกมีความสามารถเหล่านั้น
- “ชื่อตัวแปรที่ตรงกันป้องกันการชนกันได้” EVM ใช้สล็อตพื้นที่จัดเก็บ ไม่ใช่ชื่อในซอร์สโค้ด ลำดับโครงสร้าง การสืบทอด และชนิดต้องเข้ากันได้เสมอ
- “โค้ดพร็อกซีที่ได้รับการยืนยันหมายความว่าทั้งระบบได้รับการยืนยันแล้ว” อิมพลีเมนเทชันที่ทำงานอยู่ beacon ผู้ดูแลการอัปเกรด สถานะการเริ่มต้นใช้งาน และสิทธิ์ของโมดูล ล้วนเป็นส่วนแยกกันของขอบเขตความไว้วางใจ
หัวข้อที่เกี่ยวข้อง
แหล่งที่มา
- Introduction to Smart Contracts - Solidity Documentation (เข้าถึงเมื่อ: 2026-08-20)
- Units and Globally Available Variables - Solidity Documentation (เข้าถึงเมื่อ: 2026-08-20)
- ERC-1967: Proxy Storage Slots - Ethereum Improvement Proposals (เข้าถึงเมื่อ: 2026-08-20)