פרוקסי מינימלי: מדוע פריסת חוזים אוכלת את התקציב
כל מופע חדש של חוזה ב-EVM הוא כתיבה מחדש של כל הבייטקוד לאחסון הרשת. עלות פריסת החוזה אינה נגזרת מהלוגיקה אלא מהנפח: הרשת גובה תשלום עבור כל 32 בתים של קוד, ובפריסה באמצעות פקטורי של עשרות מופעים זהים אתם משלמים על אותו קוד שוב ושוב.
הפרוקסי המינימלי מסיר בדיוק את הכפילות הזו: במקום עותק של הלוגיקה נכתב לשרשרת חוזה-מצביע קצר, שמאציל את כל הקריאות למופע יחיד של המימוש שכבר נפרס.
המכניקה פשוטה. הפרוקסי שומר את כתובת המימוש ובאמצעות DELEGATECALL מריץ את הקוד שלה בהקשר שלו — עם האחסון שלו, היתרה שלו והכתובת שלו. הבייטקוד של המימוש נפרס פעם אחת, וכל מופע חדש עולה בדיוק כמו גודלו של הפרוקסי עצמו — עשרות בתים במקום קילובתים. הגז בעת הפריסה יורד בסדר גודל, וזו אינה חיסכון חד-פעמי: הוא מתרחב לכל ישות חדשה שאתם יוצרים.
סעיף ההוצאות השני — עדכונים. ללא פרוקסי כל תיקון לוגיקה משמעו פריסה חוזרת של הבייטקוד והגירת מצב: מופעים ישנים נשארים בגרסה הקודמת, חדשים חיים בנפרד, וההיסטוריה מתפצלת.
עם פרוקסי מינימלי אתם מחליפים את כתובת המימוש — כל המופעים מתחילים בבת אחת לעבוד לפי הלוגיקה החדשה, והמצב נשאר במקומו. פריסה חוזרת של הבייטקוד הופכת לעסקת עדכון אחת של המצביע.
גם מחיר הפתרון ידוע, ויש להביא אותו בחשבון מראש. הפרוקסי אינו יכול לכלול קונסטרקטור — פרמטרים ראשוניים מועברים בפונקציית אתחול נפרדת, שחובה להגן עליה מפני קריאה חוזרת.
האחסון של הפרוקסי ושל המימוש חי באותו סלוט, ולכן סדר המשתנים נקבע מהיום הראשון: שינוי סדר השדות בעת עדכון שובר את המצב. קריאות הקוד בסייר הרשת יורדת — רואים את הפרוקסי, ואת הלוגיקה יש לבדוק לפי כתובת המימוש.
בדיוק על הגבולות האלה לרוב נתקלות צוותים שמיישמים את התבנית ללא הבחנה. בהמשך נפרט כיצד זה נסגר: היכן פרוקסי מינימלי מניב את הרווח המקסימלי, והיכן זול יותר להשאיר פריסה ישירה.
כיצד פרוקסי מינימלי מפחית את עלות הפריסה
פרוקסי מינימלי הוא חוזה-עטיפה ששומר את כתובת הלוגיקה ומאציל לה את כל הקריאות דרך DELEGATECALL. הבייטקוד של פרוקסי כזה מסתכם בכמה עשרות בייטים לעומת אלפים בחוזה מלא, ולכן לבלוקצ'יין נכתב רק עטיפה קטנה, והלוגיקה הכבדה נפרסת פעם אחת. בעת שכפול החוזה, כל מופע חדש עולה בסדר גודל פחות גז מאשר פרסום חוזר ומלא של הקוד.
מה נותן הקיצור בבייטקוד
פריסה היא הפעולה היקרה ביותר ברשת: הגז מחושב עבור כל בייט שנכתב. תבנית הפרוקסי מסירה מהעסקה את גוף הלוגיקה ומשאירה רק הפניה אליה. בזרם של חוזים אחידים — טוקנים, מחסנים, מופעים לכל לקוח — הדבר מעביר את העלות מ"בכל פעם מחדש" ל"פעם אחת ועוד עותקים זולים".
מפעל חוזים ולוגיקה אחודה
מפעל חוזים פורס את הפרוקסי בכתובת צפויה מראש ומעביר לבנאי את הפרמטרים של המופע הספציפי. הלוגיקה נשארת משותפת: עדכון ההתנהגות נעשה במקום אחד ונלקח על ידי כל השכפולים. תופעת לוואי — כתובות צפויות מראש שנוחות להטמיע באינטגרציות מראש.
אפקט מדיד
| וריאנט | מתי מתאים | מגבלות |
|---|---|---|
| פריסה מלאה | לוגיקה ייחודית, חוזה בודד | עלות עסקה מקסימלית, כפילות קוד |
| תבנית פרוקסי (EIP-1167) | הרבה מופעים אחידים | הלוגיקה משותפת, המצב — לכל אחד משלו |
| מפעל חוזים | זרם שכפולים עם פרמטרים | נדרשת עבודה מדויקת עם כתובות והרשאות |
בפועל, מעבר לפרוקסי מינימלי מקצר את הבייטקוד של המופע בעשרות מונים ומפחית את גז הפריסה בסדר גודל — בזרם של מאות שכפולים ההבדל בעלות העסקה הופך למכריע. הגישה מתוקננת ב-EIP-1167, ולכן תואמת לרוב כלי הבנייה והאימות.
מה אם יש צורך לעדכן את הלוגיקה? הפרוקסי שומר את כתובת המימוש בנפרד, ולכן החלפת הלוגיקה אינה מחייבת הנפקה מחדש של השכפולים — מתעדכן חלק משותף אחד.
הטמעת פרוקסי מינימלי: שלבי עבודה הנדסית
הטמעת פרוקסי מינימלי מתחילה בביקורת חוזים: בוחנים אילו חוזים לוגיים ממוחזרים בין מופעים, היכן משוכפל בייט-קוד ואילו פונקציות חייבות להישאר בלתי ניתנות לשינוי. בסיכום קובעים מה מועבר למימוש משותף ומה נשאר בפרוקסי — מכך תלויים גם סכמת הפריסה וגם נפח האחסון לכל מופע.
לאחר מכן בונים את התהליך כך שהמפעל יהיה צפוי, ועדכון הלוגיקה לא ישבור מופעים שכבר נוצרו.
- ביקורת חוזים. מנתחים את הקוד הקיים, מזהים ספריות ופונקציות משותפות, בודקים היכן האחסון קשור לכתובת ואינו מועבר לפרוקסי ללא שינויים.
- תכנון סכמת הפריסה. קובעים כיצד הפרוקסי מפנה למימוש, כיצד מאותחל המצב וכיצד מופרדות ההרשאות לעדכון הלוגיקה ולניהול המופעים.
- הגדרת המפעל. בונים מפעל שפורס את הפרוקסי באופן דטרמיניסטי, עם בדיקת כתובת והגנה מפני אתחול חוזר.
- בדיקת הפרוקסי. מריצים תרחישים: יצירת מופע, קריאות דרך האצלה, עדכון המימוש, rollback והתנהגות במקרה של שגיאות בלוגיקה.
- בדיקה ברשת בדיקות. פורסים את המפעל ומספר מופעים, מאמתים כתובות, צריכת gas ליצירה ונכונות המצב לאחר הקריאות.
- מסירת הפתרון. מוסרים את המפעל המוגדר, סקריפטי הפריסה, סכמת האחסון והוראות להוספת מופעים חדשים.
- ליווי לאחר ההשקה. מסייעים בפריסה הראשונה בסביבת ייצור וקובעים את סדר עדכון הלוגיקה ללא מיגרציה של פרוקסי שכבר נוצרו.
סדר פעולות זה מסיר את הסיכון המרכזי — מצב שבו הפרוקסי נוצרו, אך לא ניתן לעדכן את הלוגיקה ללא הנפקה מחדש של כל המופעים. ביקורת ובדיקות בשלבים מוקדמים חושפות זאת לפני העלייה לייצור, ולא אחריה.
במה שונה פרוקסי מינימלי מפרוקסי רגיל?
פרוקסי מינימלי הוא חוזה שאינו מכיל לוגיקת אפליקציה כלל. כל מה שיש בו: הוראת האצלת קריאות וכתובת ממומשת צרובה. כל קריאה שמגיעה לכתובת הפרוקסי עוברת דרך DELEGATECALL אל חוזה המימוש, והקוד מבוצע בהקשר של אחסון הפרוקסי.
הפרוקסי עצמו אינו מאחסן משתנים או מצב — רק כתובת בלתי ניתנת לשינוי המוטבעת בקוד הבייט. בדיוק משום כך גודלו — עשרות בייטים, ולא קילובייטים.
פרוקסי רגיל הניתן לשדרוג בנוי אחרת. הוא מאחסן את כתובת המימוש בחריץ אחסון, ולכן — מסוגל לשנות אותה: להוסיף פונקציית upgradeTo, לבדוק הרשאות מנהל, לעדכן משתנה.
על כך יש לשלם: לוגיקת השדרוג היא קוד, קוד תופס מקום בקוד הבייט, ואת החריץ עם הכתובת יש להגן מפני התנגשויות עם אחסון המימוש. מכאן סכמות שלמות כמו EIP-1967, שבהן שמים את כתובת המימוש בחריץ פסאודו-אקראי כדי שלא תתנגש עם משתני הלוגיקה.
ההבדל בעלות הפריסה הוא תוצאה ישירה של ההבדל בגודל. פרוקסי מינימלי בעת פריסה כותב לבלוקצ'יין רק קוד בייט קצר, ולכן עולה בסדר גודל פחות מפרוקסי מלא הניתן לשדרוג עם לוגיקת שדרוג.
כאשר אתם מקימים מאות או אלפי מופעים זהים — למשל, חוזה אחד לכל משתמש או לכל מגרש — ההבדל הזה מצטבר פי כמה בעלויות הפריסה הכוללות.
הצד השני של המטבע: פרוקסי מינימלי הוא בלתי ניתן לשינוי. כתובת המימוש מוטבעת בקוד הבייט, ולא ניתן להחליף אותה לאחר הפריסה — לא ניתן יהיה לעדכן את הלוגיקה במופעים שכבר נוצרו.
פרוקסי הניתן לשדרוג מספק אפשרות כזו, אך מוסיף מעל ההאצלה שכבת ניהול: מי רשאי לשדרג, כיצד מוגנים החריצים, מה קורה בעת שגיאה במימוש החדש. זה לא "טוב יותר" או "גרוע יותר" — זו בחירה בין חיסכון בפריסה לבין גמישות השדרוג.
בפועל הבחירה היא כזו: אם הלוגיקה סופית ולא תשתנה, והמופעים רבים — פרוקסי מינימלי. אם החוזה יתפתח, והמופע הוא אחד-שניים — פרוקסי הניתן לשדרוג, שבו עלויות הניהול מוצדקות.
גם וריאנט משולב קיים: מימוש הניתן לשדרוג בתוספת פרוקסי מינימליים המצביעים עליו — ואז ניתן לעדכן את הלוגיקה פעם אחת עבור כל השכפולים.
מקרה בוחן: הפחתת עלות הפריסה פי 10 עבור רשת מרקטפלייסים
הלקוח — רשת מרקטפלייסים אזוריים — הוציא חוזה מסחר חדש עבור כל עיר וקטגוריה. כל פריסה גררה אחריה בייט-קוד מלא עם לוגיקת המכרזים, ההצעות והחישובים: אותו קוד בדיוק נשלח לרשת עשרות פעמים, ועל כך היה צריך לשלם בגז מחדש.
בחוזה העשירי כבר התגלה צוואר הבקבוק: הוצאת חלון ראווה חדש ארכה שעות והתנגשה בעלות כתיבת הקוד לבלוקצ'יין.
פירקנו את הלוגיקה לשני רבדים. החלק המשותף — כללי המסחר, העמלות, התפקידים — הוצא לחוזה-מימוש אחד ואחיד. כל חלון ראווה חדש הפך לפרוקסי מינימלי: חוזה קצר, ששומר את כתובת המימוש ומאציל אליה את הקריאות. פרוקסי כזה נכנס לכמה עשרות בייטים, ולכן לרשת נכתבת רק הפניה, ולא כל הקוד.
מה השתנה במספרים
| מדד | לפני | אחרי |
|---|---|---|
| גז לפריסת חלון ראווה | תקן | ~1/10 מהתקן |
| זמן הוצאת חלון ראווה חדש | שעות | דקות |
| תיקון לוגיקה בכל חלונות הראווה | לפי כל חוזה | מימוש אחד |
בנפרד סגרנו את סוגיית העדכניות: מאחורי הפרוקסי מוצמד בקר, שמחליף את כתובת המימוש. תיקון כללי המסחר מתפרס על כל חלונות הראווה בבת אחת, בלי פריסה מחדש ובלי מיגרציית מצב — הנתונים נשארים במקומם.
את המגבלה תיעדנו ביושר: הפרוקסי אינו שומר לוגיקה משל עצמו, ולכן כולה חייבת להתקיים במימוש, והקונסטרוקטור הוחלף באתחול מפורש.
התוצאה: הרשת מתרחבת לערים חדשות בלי גידול בהוצאות הגז ביחס למספר החוזים, והוצאת חלון ראווה הפסיקה להיות משימה הנדסית נפרדת. הגישה נשענת על EIP-1167 — תקן הפרוקסי המינימלי, שנבחן בשדה הקרב על אלפי חוזים.
ספקות והבטחות: תאימות, הגירה, חזרה לאחור
חוזה פרוקסי הוא שכבת כתובות נפרדת, והסיכון הטכני העיקרי כאן אינו בחיסכון בגז, אלא בעובדה שהלוגיקה והאחסון חיים בנפרד. Minimal proxy (EIP-1167) מעביר קריאות למימוש דרך delegatecall, ולכן המצב נשאר בכתובת הפרוקסי, והקוד — בכתובת הלוגיקה. מכאן שתי שאלות מעשיות: מה לעשות אם יש צורך לעדכן את המימוש, ומה יקרה לנתוני המשתמשים שכבר נצברו.
האם ניתן לעדכן את הלוגיקה בלי לאבד את המצב ואת כתובת החוזה?
כן, אם הארכיטקטורה תוכננה מראש. כתובת הפרוקסי אינה משתנה — רק המצביע למימוש משתנה, והאחסון נשאר במקומו. קריטי אחר: סדר המשתנים והטיפוסים בקוד החדש חייבים להתאים לישנים, אחרת הנתונים "יזוזו". לכן אנו מקבעים את סכמת האחסון לפני הפריסה הראשונה ובודקים כל עדכון ברשת בדיקה עם תמונת מצב אמיתית.
מה אם נדרשת חזרה לאחור? חזרה לאחור נעשית על ידי החזרת המצביע למימוש הקודם — בתנאי שכתובתו נשמרה, והמצב לא השתנה באופן בלתי הפיך. אנו שומרים היסטוריית מימושים ומתארים אילו פעולות ניתנות לחזרה לאחור ואילו לא.
כיצד מאורגנות ההרשאות? תפקידים לעדכון, השהיה ומשיכת כספים מופרדים ומועברים לחוזה ניהול נפרד. המפתחות — ריבוי חתימות, ולא ארנק אחד.
האם זה תואם לסטאק הנוכחי? Minimal proxy הוא תקן, וכל סורקי הבלוקים העיקריים, הארנקים וכלי האימות מבינים אותו. המגבלה היחידה: הלוגיקה לא צריכה להשתמש בקונסטרוקטור לאתחול — ערכי ההתחלה מוגדרים בקריאה נפרדת.
לאחר העבודה הלקוח מקבל לידיו:
- קוד המקור של הפרוקסי והמימוש עם הגדרות המהדר;
- סכמת האחסון עם כללי התאימות בעת עדכונים;
- חוזה הניהול עם מטריצת התפקידים וספי החתימות;
- סקריפטים לפריסה ולעדכון, הניתנים לשחזור ברשת בדיקה;
- נוהל חזרה לאחור עם רשימת הפעולות הניתנות לחזרה;
- דוח בדיקות ותוצאות בדיקות על תמונת מצב אמיתית;
- תיעוד של הכתובות, התפקידים וסדר הפעולות בעת תקרית.
תארו את המשימה שלכם — נחזור עם הערכה ותוכנית: אילו חוזים כבר ברשת, כיצד מאורגן האחסון ומה מתוכנן להשתנות.







