אינטגרציה של EIP-712: חתימות טיפוסיות לחוזים חכמים

ללא חתימות טיפוסיות, פעולות בחוזים חכמים נדחות, וניתוח הסיבות גוזל זמן מהצוות. אנו מתכננים ומטמיעים אינטגרציית EIP-712 מהיסוד: מסכימת הנתונים ועד לאימות בצד החוזה. התוצאה — חתימות צפויות ושיעור נמוך יותר של פעולות שנדחו.

שירותי פיתוח בלוקצ'יין

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1376
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1350
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    795
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1088
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1034
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1130

אינטגרציה של EIP-712: מה נשבר ללא חתימות טיפוסיות

חתימה על האש גולמי נראית אמינה בדיוק עד לתקרית הראשונה. הארנק מציג למשתמש מחרוזת בייטים בלתי קריאה, הוא מאשר בלי להסתכל — וחותם על קריאה שרירותית לחוזה.

אינטגרציה של EIP-712 סוגרת את הפער הזה: במקום האש מוצגת על המסך מבנה עם שם הדומיין, כתובת החוזה המאמת ופרמטרים מפורקים לפי שדות — הסכום, כתובת הנמען, תוקף.

סוג שני של כשלים — החלפת המטען השימושי. אם החתימה נבנית מנתונים שהודבקו ידנית ללא מפרידים, שתי פעולות שונות מפיקות תקציר זהה, והתוקף מחליף את מיקום השדות: העברה חתומה לכתובת אחת מבוצעת כהעברה לכתובת אחרת.

חתימות טיפוסיות מקבעות את הטיפוס של כל שדה ואת סדר הסריאליזציה שלהם, ולכן המבנה או תואם במלואו, או שאימות החתימה אינו עובר.

כשל שלישי — דחיית העסקה בשלב אימות החתימה. החוזה משחזר את כתובת החותם דרך ecrecover, אך הדומיין באפליקציה ובחוזה נפרדו: chainId אחר, שם אחר, כתובת מאמת אחרת. החתימה תקפה פורמלית, אך האימות מחזיר כתובת זרה — העסקה מתבטלת, והמשתמש חוזר על החתימה בעיניים עצומות. בנפרד עולה אי-התאמה של nonce ושל מועד אחרון: ללא תוקף מפורש, ניתן להריץ מחדש הרשאה חתומה שוב ושוב.

הפרקטיקה מראה: רוב התקריות הללו אינן שגיאה של קריפטוגרפיה, אלא חוסר סנכרון של תיאור המבנה בין הפרונטאנד, הבקאנד והחוזה. להלן נבחן כיצד זה נסגר ברמת האינטגרציה.

מה נותנת חתימה טיפוסית בעבודת המוצר

כאשר משתמש חותם על פעולה "בעיוורון" — רואה האש של תווים אקראיים במקום סכום, כתובת וייעוד — גדל שיעור האישורים הנדחים והשגויים. האדם או מבטל את הפעולה מתוך זהירות, או מאשר בלי להסתכל ואחר כך מערער על התוצאה. חתימה טיפוסית מסירה את הסיבה עצמה: הארנק מציג שדות מובנים — מה בדיוק, למי ובאילו תנאים נחתם.

ההשפעה מדידה בשלוש רמות. במסך האישור, במקום מחרוזת בלתי קריאה — כרטיס פעולה מובן, ולכן שיעור הפעולות הנדחות יורד: המשתמש רואה משמעות ומאשר בשקט. בתוך המוצר, פורמט חתימה אחד משמש גם את ממשק הווב, גם את הקריאות השירותיות וגם את משימות הרקע — אין יותר אי-התאמות ביניהם.

בצד החוזה, הבדיקה מתבצעת לפי מבנה שהוגדר מראש, ולא לפי קבוצת בתים שרירותית, ולכן החלפת שדות או שימוש חוזר בחתימה בהקשר אחר נחסמים בכניסה.

ערך נפרד — הפחתת שגיאות משתמש בפעולות עם מספר פרמטרים: העברה, קביעת מגבלה, אישור עסקה. כאשר כל שדה חתום במפורש והחתימה קשורה לחוזה ולרשת מסוימים, אישור מקרי של פעולה זרה או פעולה מסביבה אחרת הוא בלתי אפשרי טכנית. זה אותו סוג כשל שבעבר צף רק לאחר חיוב הכספים.

עבור הצוות זה גם צפיות בתמיכה: פורמט חתימה אחיד משמעו שניתוח תקלה מצטמצם להשוואת שדות, ולא לשחזור מה בדיוק חתם המשתמש. מתודות חדשות של החוזה מתווספות בתיאור המבנה, בלי לבנות מחדש את לוגיקת הבדיקה בצד הלקוח.

מפרקים כיצד זה נסגר ברמת האינטגרציה: תיאור מבנים, דומיין החתימה, בדיקה בחוזה ותאימות עם ארנקים שכבר פועלים.

דרכי אינטגרציה: השוואת גישות

אינטגרציה של EIP-712 לעיתים רחוקות נעשית "מאפס": לרוב קיים חוזה עובד, ארנק ופרונטאנד שאי אפשר לעצור. לכן הבחירה מצטמצמת לשאלה היכן למקם את לוגיקת החתימות הטיפוסיות — בתוך החוזה הקיים, כשכבה נפרדת מעליו, או להעביר אותה לשירות עצמאי.

אפשרות מתי מתאימה מגבלות הגישה
סכימה ידנית בחוזה חוזה אחד, מעט סוגי הודעות, החתימות נבדקות במקום אחד הדומיין והסוגים משוכפלים בקוד ובקליינט — אי-התאמה מתגלה רק בפרודקשן; תיקונים דורשים פריסה מחדש
עטיפה מעל החוזה החוזה כבר בפרודקשן ואסור לשנותו, נדרשת תאימות לקריאות הנוכחיות העטיפה מוסיפה שכבת האצלה: צריכת הגז גדלה, וחלק מהמתודות יש לנתב ידנית
שירות חתימה נפרד מספר חוזים, דומיין אחיד, החתימות נדרשות מחוץ לרשת השירות הופך לנקודת כשל ודורש הרשאה משלו; בהגדרה שגויה של הדומיין החתימה לא עוברת בדיקה

ההבדל המרכזי — היכן חי הגדרת הדומיין והסוגים. בסכימה הידנית היא מפוזרת בין החוזה לקליינט, בעטיפה היא קשורה לחוזה המקורי, בשירות היא מועברת למקום אחד ומשמשת מחדש את כל הצרכנים.

ככל שיש יותר חוזים וקליינטים, כך יקרה יותר הטעות בהגדרה זו: אי-התאמה בשם, בגרסה או ב-chainId הופכת את החתימה ללא תקפה בלי שגיאה ברורה.

בפועל הבחירה אינה בינארית. את העטיפה לעיתים קרובות מציבים כפתרון מעבר כדי לא לגעת בפרודקשן, ואת שירות החתימה מציגים מאוחר יותר — כשמופיע חוזה שני-שלישי והשכפול של הסוגים הופך מורגש. מפרטים כיצד זה נסגר בכל אפשרות ומה בדיוק בודקים בעת הקבלה.

איך מתבצעת ההטמעה: שלבי העבודה ההנדסית

ההטמעה של EIP-712 מתחילה לא בקוד, אלא בניתוח של מה שנחתם כרגע. אם בפרויקט כבר קיימת חתימה של טרנזקציות או הודעות, קודם כל מתעדים: אילו שדות נכנסים לחתימה, היכן נבדקת תקפותה, מי מחזיק במפתחות ומה יקרה בעת שינוי הפורמט. זהו ביקורת חתימה — היא מראה היכן הסכמה הנוכחית מאפשרת החלפת נתונים או שימוש חוזר בחתימה.

לאחר מכן מתכננים את סכמת הטיפוסים: מתארים את מבנה ההודעה, הדומיין (שם, גרסה, chainId, כתובת חוזה המאמת) ואת הכללים שלפיהם השדות מקושרים זה לזה. סכמת הטיפוסים היא חוזה בין הלקוח לחוזה: אם היא מתוארת באופן חלקי, הבדיקה בצד החוזה תעביר חתימה שלא הייתה אמורה לעבור.

  1. ביקורת החתימה הנוכחית: אוספים את כל המקומות שבהם נוצרת ונבדקת חתימה, מתעדים סיכוני שימוש חוזר והחלפת שדות.
  2. תכנון סכמת הטיפוסים: מתארים את הדומיין ואת מבנה ההודעה, מתאמים אותה עם לוגיקת החוזה ועם צד הלקוח.
  3. הגדרת הבדיקה בצד החוזה: מיישמים שחזור חותם והשוואת דומיין, כך שהחתימה תתקבל רק עבור הרשת והחוזה הנכונים.
  4. תרחישי בדיקה: מכסים חתימה תקינה, תוקף שפג, דומיין זר, שימוש חוזר והחלפת שדות.
  5. מסירת הפתרון: מוסרים את הקוד, תיאור סכמת הטיפוסים והוראות להוספת שדות חדשים בלי לשבור תאימות.

בנפרד בודקים תאימות לאחור: אם בפרויקט כבר קיימות חתימות בפורמט ישן, צריך להחליט אם לתמוך בהן במקביל או לבצע מיגרציה. תרחישי הבדיקה כאן חשובים יותר מהמהירות — הם מתעדים את התנהגות החוזה לפני שהפתרון יוצא לפרודקשן.

מסירת הפתרון כוללת לא רק קוד, אלא גם תיעוד של סכמת הטיפוסים: איך להוסיף שדה, איך לעדכן את הדומיין, מה יקרה לחתימות ישנות. כך צוות הלקוח יכול לפתח את האינטגרציה בלעדינו, ואנחנו נשארים בקשר למקרים מורכבים.

מה הלקוח מקבל לידיו לאחר ההטמעה

הטמעת EIP-712 היא לא רק קוד בחוזה, אלא גם סט ארטיפקטים שהצוות ממשיך לעבוד איתם בלעדינו. להלן מה בדיוק אנחנו מוסרים לידיים.

  • תיאור סכמת הטיפוסים. רשימה פורמלית של המבנים EIP712Domain, Mail, Order וטיפוסים מקוננים עם שדות, סדר וכללי האשינג — מקור אמת יחיד לחוזה ולקוח.
  • מודול אימות חתימות. רכיב מוכן שמשחזר את כתובת החותם מתוך v, r, s ומשווה אותה לזו הצפויה, כולל בדיקת chainId וכתובת חוזה המאמת.
  • נוהל הוצאת טיפוסים חדשים. סדר פעולות שלב אחר שלב: כיצד להוסיף מבנה, לעדכן את הדומיין, לתאם גרסה ולא לשבור חתימות שכבר הונפקו.
  • תיעוד אינטגרציה. דוגמאות קריאה בצד הלקוח, וקטורי בדיקה של חתימות וניתוח שגיאות אופייניות — כדי שהצוות יתחבר בלעדינו.
  • סט בדיקות. כיסוי תרחישים: חתימה תקפה, תוקף שפג, chainId שגוי, החלפת טיפוס — רגרסיה נתפסת לפני שחרור.
  • סכמת פריסה. סדר הפריסה של חוזה המאמת, הגירת הדומיין וחזרה לגרסת הטיפוסים הקודמת.
  • צ'ק-ליסט קבלה. קריטריונים פורמליים: החתימה עוברת ברשת הבדיקה, החזרה משוחזרת, התיעוד מעודכן.

כל ארטיפקט מכסה סוג סיכון משלו: סכמת הטיפוסים — חוסר סנכרון בין הלקוח לחוזה, מודול האימות — זיוף חתימה, הנוהל — שבירה בעת הוספת שדות. תארו את המשימה — נחזור עם הערכה ותוכנית לפי הסטאק שלכם.

מקרה בוחן: כיצד צומצם שיעור הפעולות הנדחות

מה היה לפני השינויים

הפרויקט — שירות לא-משמורן (non-custodial) עם חתימות פעולות בצד הלקוח. החתימה נבנתה "ידנית": ההאש נוצר על ידי שרשור שדות, שסדרם וסוגיהם פורשו באופן שונה על ידי הפרונטאנד והחוזה. כל עוד הגרסאות שוחררו בסנכרון, לא היו אי-התאמות.

ברגע שהבקאנד והחוזה החלו להיות מופצים באופן בלתי תלוי, החלו להופיע דחיות: invalid signature על פעולות שהיו תקפות מנקודת מבטו של המשתמש.

שיעור הפעולות הנדחות עמד על כ-7% מכלל הפעולות הנחתמות. חקירת כל תקלה ארכה בממוצע 3-4 שעות: היה צורך לשחזר את סדר השדות, להשוות סוגים, ולמצוא היכן בדיוק נפרדה הסריאליזציה. התמיכה קיבלה 40-60 פניות בשבוע בנוסח "חתמתי — לא עבר".

מה שינו

הוטמע EIP-712: כל הודעה נחתמת קיבלה מבנה טיפוסי קפדני — דומיין עם name, version, chainId ו-verifyingContract, בתוספת סכמת שדות עם טיפוסים מפורשים. ההאש מחושב כעת באופן דטרמיניסטי לפי התקן, ולא נבנה ידנית בשני הצדדים.

הדומיין נקשר לכתובת החוזה ולרשת — חתימה שנבנתה עבור סביבה אחת אינה עוברת פיזית באחרת. סכמת הטיפוסים הוצאה לארטיפקט משותף שמיובא על ידי הפרונטאנד, הבקאנד ובדיקות החוזה. הארנקים מציגים למשתמש ייצוג קריא של השדות במקום מחרוזת hex בלתי מובנת — חלק מהדחיות נעלמו כבר בשלב זה.

מה נהיה

שיעור הפעולות הנדחות ירד ל-0.4% — היתרה נובעת מתפוגות תוקף ומחוסר באמצעים, כלומר מסיבות לגיטימיות, ולא מחוסר סנכרון בין פורמטים. זמן חקירת התקלה הצטמצם משעות ל-15-20 דקות: אי-ההתאמה נראית כעת לפי אי-התאמת דומיין או טיפוס שדה, ולא מתגלית על ידי חיפוש ממצה.

פניות לתמיכה בנושא חתימות ירדו בערך פי שישה. אפקט נפרד — צפיות בעת הוספת טיפוסי פעולות חדשים: הסכמה מתרחבת במקום אחד, ושני הצדדים מקבלים שינוי מתואם ללא סנכרון ידני.

תרגלנו זאת על סטאק OpenZeppelin — הספרייה מספקת פרימיטיבים מוכנים לדומיין ולחישוב האש, מה שמסיר סוג שלם של שגיאות עוד בשלב כתיבת הקוד.

במה שונה חתימה טיפוסית מחתימה רגילה

חתימה רגילה עובדת עם האש הגולמי: החוזה מקבל 32 בייטים וצריך לשחזר בעצמו מה בדיוק החתים המשתמש. הבעיה היא שאי אפשר לחלץ זאת מהאש — הנתונים מקופלים באופן בלתי הפיך, והאימות מצטמצם להשוואת מספרים.

אם תוקף יחליף את הקונטקסט של הקריאה, החתימה תישאר תקפה: היא מאשרת את עצם ההסכמה, אך אינה אומרת דבר על תוכנה.

EIP-712 מוסיף תיאור מובנה: דומיין (שם החוזה, גרסה, רשת, כתובת המאמת) וסכמת הודעה טיפוסית. לפני החתימה הארנק מרכיב מכך ייצוג דטרמיניסטי ומגבב אותו לפי כללים קבועים. בפלט — אותה חתימה קריפטוגרפית, אך מקושרת לחוזה מסוים, לרשת ולקבוצת שדות. חתימה שהונפקה עבור פרוטוקול אחד לא תעבור אימות באחר.

ההבדל בין הגישות מתבטא בשלושה מקומות. הראשון — קריאות הנתונים: המשתמש רואה בארנק לא מחרוזת hex, אלא שדות מובנים עם טיפוסים וערכים, ומאשר את הפעולה במודע. השני — הגנה מפני שימוש חוזר: הדומיין וה-nonce חוסמים העברת חתימה לחוזה אחר או חזרה על אותה פעולה.

השלישי — פענוח בצד החוזה: במקום הרכבה ידנית של בייטים, המהנדס מתאר את המבנה פעם אחת, והאימות מתבצע לפיו.

עבור אינטגרציה משמעות הדבר היא שיש לקבע את סכמת הטיפוסים לפני הביקורת ולא לשנותה לאחר השחרור: כל שינוי שם של שדה או שינוי סדר שובר חתימות שכבר הונפקו.

אנו מוציאים את התיאור למודול נפרד, מסנכרנים אותו עם הפרונטאנד ומכסים בבדיקות לשחזור הכתובת — אז אי-התאמה בין הלקוח לחוזה נתפסת לפני העלייה לאוויר, ולא בפרודקשן.

ספקות טכניים נפוצים לפני ההתחלה

לפני תחילת האינטגרציה כמעט תמיד עולים אותם שאלות: האם לא נשבור את מה שכבר עובד, מה ירגישו המשתמשים הקיימים וכמה קוד יהיה צורך לשכתב. להלן ניתוח של כל אחד מהם.

האם זה תואם לחוזים שכבר בפרודקשן?

כן, אם נוסיף בדיקת חתימה כמתודה נפרדת, ולא נשכתב את הקיימות. קריאות ישנות ממשיכות לעבוד לפי הסכימה הקודמת, ומסלול החתימה החדש מופעל במקביל. החוזה מקבל דומיין וסוג מבנה, והלוגיקה של הבדיקה נשארת מבודדת — החזרה לאחור מסתכמת בהשבתה של מתודה אחת ללא מיגרציית מצב.

מה ירגישו המשתמשים הקיימים?

עבור אלה שאינם משתמשים בתרחיש החדש, שום דבר לא משתנה — העסקאות שלהם עוברות באותו מסלול. אלה שעוברים לחתימות מפסיקים לשלם על אישורים מיותרים: חתימה אחת במקום כמה עסקאות. חשוב לתכנן הודעות שגיאה ברורות, אחרת המשתמש לא יבין מדוע החתימה נדחתה.

כמה גדול היקף השיפורים?

העבודה העיקרית — לתאר את מבנה הנתונים והדומיין, לסנכרן ביניהם בין החוזה ללקוח, להוסיף בדיקה בצד החוזה ובדיקות לזיוף חתימה. אם הממשק כבר מחולק לשכבות, השינויים מקומיים. ככל שהלוגיקה שזורה יותר בבדיקות, כך יהיה צורך לפרום יותר.

מהם סיכוני המעבר וכיצד אנו סוגרים אותם?

הסיכון העיקרי — חוסר סנכרון של הדומיין בין החוזה ללקוח: החתימה מפסיקה לעבור. סוגרים זאת על ידי קיבוע הדומיין בבדיקות ובדיקה בשלב הבנייה. הסיכון השני — שימוש חוזר בחתימה, אותו מסיר ה-nonce. השלישי — אי-התאמה של סוגי שדות, נתפסת בבדיקות על ערכי גבול.

תארו את החוזים והתרחיש הנוכחיים — נחזור עם הערכת היקף השיפורים ותוכנית מעבר.

נדון במשימה שלך ונחזור עם תוכנית

כדי לחזור עם הערכה של הטמעת EIP-712, אנחנו צריכים להבין את סכמת החתימה הנוכחית שלך: היכן בדיוק נוצרת החתימה, מה נכנס לנתונים החתומים וכיצד החוזה מאמת אותם. תאר את המשימה בצורה חופשית — סוג הפעולה הנחתמת, השרשרת שאתה עובד איתה, ומה כבר מיושם. זה מספיק כדי להעריך את היקף העבודה ולהציע תוכנית.

מה כדאי לציין כדי שההערכה תהיה מדויקת:

  • סכמת החתימה הנוכחית: eth_sign, personal_sign, eth_signTypedData או הרכבה עצמית של ה-hash;
  • אילו פרמטרים משתתפים בחתימה וכיצד הם מועברים לחוזה;
  • מבנה הדומיין: name, version, chainId, verifyingContract — מוגדרים או שיש להציגם;
  • היכן נבדקת החתימה: on-chain, ב-relayer או ב-backend;
  • הארנקים והלקוחות הנתמכים שכבר נבדקו איתם;
  • האם יש multichain או שדרוג חוזים בתוכניות;
  • מגבלות זמנים ומה קריטי לא לשבור בזרימה הנוכחית.

לפי הנתונים האלה נחזור עם תוכנית עבודה: שלבי המיגרציה, סדר הפריסה ללא השבתה, סכמת החזרה לאחור ונקודות בדיקה. אם חלק מהסכמה עדיין לא מוגדר — נציע אפשרויות וננמק את הבחירה.

תאר את המשימה — נחזור עם הערכה ותוכנית.