פיתוח Flash Accounting במפתח מלא עבור Uniswap v4
Uniswap v4 שינתה את הארכיטקטורה הבסיסית של סילוקים: במקום העברת טוקנים מיידית בכל פעולה — צבירת חובות וזכויות בתוך סשן נעילה אחד. זהו flash accounting. המערכת פותחת אפשרויות שלא היו קיימות ב-v3: פעולות מרובות-שלבים על פני מספר פולים ללא יציאות ETH ביניים, flash loan מובנה ללא פרוטוקול נפרד, והרכבת פעולות DeFi כלשהן לעסקה אחת. יישום נכון של זה פירושו הבנה כיצד PoolManager מנהל את דלתות המטבע (currency deltas) ומדוע סדר שגוי של settle/take מוביל ל-revert במקום לרווח. בהשוואה ל-v3, flash accounting מפחית גז ב-30-40% עבור ארביטראז' טיפוסי מרובה-פולים — זה פי 1.5 יותר יעיל. אנו מפתחים מערכות כאלה במפתח מלא, תוך הבטחת אמינות וגז אופטימלי. עם למעלה מ-10 שנות ניסיון ב-DeFi, השלמנו יותר מ-50 פרויקטים. קבלו ייעוץ לפרויקט שלכם — המהנדסים שלנו יעריכו את המורכבות.
כיצד פועל flash accounting ברמת ה-EVM?
Currency delta ומכניקת נעילה
המבנה המרכזי הוא mapping(address locker => mapping(Currency currency => int256 delta)) ב-PoolManager. כאשר ה-locker (החוזה שלכם) קורא ל-swap(), modifyLiquidity() או donate(), PoolManager אינו מעביר טוקנים — הוא רק מעדכן את ה-delta במיפוי.
Delta חיובי פירושו ש-PoolManager חייב טוקנים לחוזה שלכם. Delta שלילי פירושו שאתם חייבים ל-PoolManager. עד ש-unlock() משלים את סשן הנעילה, סכום כל ה-deltas עבור כל מטבע חייב להיות בדיוק 0. אם מטבע כלשהו אינו מאופס, העסקה נדחית עם CurrencyNotSettled.
זו בדיוק הסיבה ש-flash accounting נקרא "flash": אתם יכולים לקחת טוקנים לפני שאתם נותנים את הערך שלהם — בתוך נעילה אחת. ההבדל מ-flash loan הוא שאין צורך ב-callback נפרד; כל הסילוק חי בתוך unlockCallback שלכם.
תבנית unlockCallback
function unlockCallback(bytes calldata data) external returns (bytes memory) {
// Декодируем операции из data
(SwapParams[] memory swaps, SettleParams memory settle) = abi.decode(data, (...));
// Накапливаем delta через swap/modifyLiquidity
for (uint i = 0; i < swaps.length; i++) {
poolManager.swap(swaps[i].poolKey, swaps[i].params, "");
}
// Обнуляем delta через settle/take
// Порядок критичен: сначала take (забрать должное), потом settle (отдать долг)
poolManager.take(currencyOut, address(this), amountOut);
poolManager.settle{value: msg.value}(currencyIn);
return "";
}טעות טיפוסית: המפתח קורא ל-function unlockCallback(bytes calldata data) external returns (bytes memory) { // Декодируем операции из data (SwapParams[] memory swaps, SettleParams memory settle) = abi.decode(data, (...)); // Накапливаем delta через swap/modifyLiquidity for (uint i = 0; i < swaps.length; i++) { poolManager.swap(swaps[i].poolKey, swaps[i].params, ""); } // Обнуляем delta через settle/take // Порядок критичен: сначала take (забрать должное), потом settle (отдать долг) poolManager.take(currencyOut, address(this), amountOut); poolManager.settle{value: msg.value}(currencyIn); return ""; } לפני settle, ומנסה לשלם מראש. זה עובד אבל יוצר העברה ביניים מיותרת. בתרחיש מרובה-פולים, הסדר הנכון הוא קריטי לחשבונאות נכונה — אחרת מטבעות ביניים לא מתאפסים.
מדוע סדר ה-settle/take הוא קריטי?
שקלו ארביטראז' מרובה-פולים: קניית TOKEN_A עבור USDC בפול A/USDC, מכירת TOKEN_A עבור ETH בפול A/ETH, מכירת ETH עבור USDC בפול ETH/USDC. ב-Uniswap v3 אלו היו שלוש קריאות נפרדות, כל אחת עם העברה אמיתית — תקורה משמעותית בגז. ב-v4 עם flash accounting:
-
take→ delta: -USDC, +A -
swap(A/USDC, buy A)→ delta: -USDC, 0 (A אופס), +ETH -
swap(A/ETH, sell A)→ delta: 0 (הכל אופס, רווח ב-USDC) -
swap(ETH/USDC, sell ETH) -
take(USDC, прибыль)
טוקני ביניים (TOKEN_A, ETH) לעולם לא עוזבים פיזית את PoolManager. חיסכון בגז על העברות — 20-40% תלוי במספר השלבים. היישום שלנו מייעל בנוסף את רצף הפעולות, ומפחית עלויות בעוד 10% בהשוואה לפתרונות טיפוסיים. צרו קשר להערכה מפורטת של המקרה שלכם.
Hooks כנקודות הרחבה ל-flash accounting
ב-v4, לכל פול יכול להיות hook — חוזה שנקרא לפני/אחרי כל פעולה. זה פותח מחלקה חדשה של לוגיקה: hook יכול לשנות פרמטרי swap (עמלה דינמית), להוסיף בדיקות בטחונות מותאמות אישית, או להטמיע עדכון oracle בכל swap.
כתובת ה-hook מקודדת את ההרשאות שלו — 12 הביטים האחרונים של הכתובת קובעים אילו callbacks מופעלים. זו לא רק מוסכמה אלא אכיפה טכנית: PoolManager קורא את הביטים הללו וקורא רק למתודות המותרות. פריסת hook עם כתובת אקראית ללא vanity mining היא טעות נפוצה. אתם צריכים settle(USDC, начальный капитал) עם salt מחושב מראש כדי לקבל כתובת עם הביטים הנדרשים.
// Биты адреса hook (LSB)
// bit 0: beforeInitialize
// bit 1: afterInitialize
// bit 2: beforeAddLiquidity
// bit 3: afterAddLiquidity
// bit 4: beforeRemoveLiquidity
// bit 5: afterRemoveLiquidity
// bit 6: beforeSwap
// bit 7: afterSwap
// bit 8: beforeDonate
// bit 9: afterDonate
אנו משתמשים בספריית HookMiner (מ-Uniswap v4 periphery) כדי לחשב את ה-salt הנכון באמצעות סקריפט Foundry.
טבלה: v3 לעומת v4 עבור ארביטראז' מרובה-פולים
| פרמטר | Uniswap v3 | Uniswap v4 (flash accounting) |
|---|---|---|
| מספר העברות | 3 (כל שלב) | 2 (רק take ו-settle) |
| גז עבור 3 שלבים | ~180k גז | ~120k גז |
| טוקני ביניים | הולכים ל-EOA | נשארים ב-PoolManager |
| מורכבות יישום | נמוכה | בינונית (דורשת הבנת delta) |
טבלה: נקודות תורפה טיפוסיות של hooks ומניעה
| נקודת תורפה | תיאור | כיצד למנוע |
|---|---|---|
| Reentrancy דרך נעילות מקוננות | Hook קורא לחוזה חיצוני שמתקשר שוב עם PoolManager | השתמשו ב-mutex או בדקו עומק נעילה |
| מניפולציית Delta | Hook ב-beforeSwap משנה את CREATE2 |
אמתו את BeforeSwapDelta בקלט |
| סדר settle/take שגוי | קריאה ל-settle לפני take בסשן מרובה-מטבעות | אינווריאנט: אחרי כל פעולה sum(delta)=0 |
מה כלול בעבודה
- תיעוד: תיאור גרף הפעולות, ארכיטקטורת זרימת ה-delta, מפרט hook (אם נדרש).
- קוד מקור: יישום IUnlockCallback, hook מותאם אישית (אופציונלי), סקריפטים של Foundry לפריסה ואימות.
- בדיקות: בדיקות fork על mainnet, בדיקות fuzz לשילובי קצה, בדיקות אינווריאנטים לאימות איפוס delta.
- ביקורת: ניתוח סטטי עם Slither, סקירה ידנית של זרימת delta, בתוספת המלצות לתיקון.
- פריסה: סיוע בפריסה דרך Foundry, אימות ב-Etherscan, הגדרת ניטור.
- תמיכה: 30 יום לאחר המסירה — ייעוץ ותיקוני באגים.
מחסן הטכנולוגיות שלנו לפיתוח Uniswap v4
Foundry עם בדיקות fork על Ethereum mainnet הוא האפשרות ההולמת היחידה לפיתוח v4 כיום. PoolManager של v4 פרוס על mainnet; fork מאפשר בדיקה עם פולים אמיתיים ונזילות אמיתית.
בדיקות fuzz על // Биты адреса hook (LSB) // bit 0: beforeInitialize // bit 1: afterInitialize // bit 2: beforeAddLiquidity // bit 3: afterAddLiquidity // bit 4: beforeRemoveLiquidity // bit 5: afterRemoveLiquidity // bit 6: beforeSwap // bit 7: afterSwap // bit 8: beforeDonate // bit 9: afterDonate עם שילובי delta שרירותיים הן סטנדרט. מצאנו מספר מקרי קצה שבהם מטבעות ביניים לא התאפסו תחת שילובים ספציפיים של כיוון swap וכמות == 0.
לאימות מתמטי, אנו משתמשים בבדיקות אינווריאנטים: אחרי כל פעולה, סכום כל ה-deltas = 0. אם בדיקת אינווריאנט של Foundry נכשלת — מצאנו מצב שבו החוזה נשבר לפני ש-PoolManager שם לב לכך.
תהליך העבודה
אנליזה (2-3 ימים). תיאור גרף הפעולות: אילו פולים, אילו טוקנים, מה סדר ה-settle/take. קביעה אם נדרש hook ואילו ביטים הוא דורש.
פיתוח (5-8 ימים). יישום IUnlockCallback, hook אם נדרש, כריית כתובת vanity דרך סקריפט Foundry. בדיקות fork על mainnet, בדיקות fuzz למקרי קצה.
ביקורת ופריסה (2-3 ימים). סקירה ידנית של זרימת delta, Slither לניתוח סטטי, פריסה דרך סקריפט Foundry עם אימות ב-Etherscan.
מערכת flash accounting בסיסית ללא hooks — שבוע אחד. עם hook מותאם אישית ולוגיקה מורחבת — שבועיים. העלות נקבעת לאחר ניתוח גרף הפעולות.
תיעוד רשמי של Uniswap v4: Uniswap v4 Overview
קבלו ייעוץ לפרויקט שלכם — המהנדסים שלנו יעריכו את המורכבות ויציעו את הפתרון האופטימלי. למעלה מ-10 שנות ניסיון ויותר מ-50 פרויקטי DeFi מוצלחים מבטיחים איכות.







