פיתוח חוזים חכמים למכרזים על בלוקצ'יין
תארו לעצמכם: מכרז ה-NFT שלכם מפסיד אלפי דולרים להתקפת סידור עסקאות, והחוזה נתקע בעת החזרת הצעות מחיר. פיתוח חוזים חכמים למכרזי בלוקצ'יין דורש איזון בין מהירות מסחר, עלויות גז והגנה מפני תוקפים. אנו בונים חוזים מוכנים לשימוש: ממכרזים אנגליים/הולנדיים בודדים ועד שווקים מרובי-חלקים עם הגנה מפני MEV וארגון מחדש של שרשרת. במשך יותר מ-5 שנים, סיפקנו יותר מ-20 מערכות מכרזים עבור NFTs, טוקנים ונכסים בעולם האמיתי. להלן, אנו מתמודדים עם האתגרים הטכניים המרכזיים: מלחמות גז, גריפינג ופרונט-ראנינג.
מכניקת מכרזים: אנגלי מול הולנדי
מכרז אנגלי (מחיר עולה)
קלאסי: המחיר עולה, ההצעה האחרונה זוכה. עבור NFTs וטוקנים, זהו הפורמט הנפוץ ביותר. הבעיה הטכנית העיקרית היא סנייפינג ברגע האחרון ופרונט-ראנינג. במכרזי Ethereum ללא הגנה, בוט רואה את עסקת ההצעה האחרונה בממפול ומכניס עסקה משלו עם עמלת עדיפות גז גבוהה יותר. פתרון: מנגנון הארכת הצעות — אם הצעה מגיעה בדקות האחרונות לפני המועד הסופי, המכרז מתארך אוטומטית:
if (block.timestamp > auctionEnd - timeBuffer) {
auctionEnd = block.timestamp + timeBuffer;
emit AuctionExtended(auctionId, auctionEnd);
}if (block.timestamp > auctionEnd - timeBuffer) { auctionEnd = block.timestamp + timeBuffer; emit AuctionExtended(auctionId, auctionEnd); } הוא בדרך כלל 10-15 דקות. כך בדיוק עובד המכרז של Nouns DAO — אחד היישומים הציבוריים הנכונים ביותר טכנית. הארכת זמן מפחיתה את סיכון הסנייפינג ב-90%.
מכרז הולנדי (מחיר יורד)
המחיר מתחיל גבוה ויורד עם הזמן. המשתתף משלם את המחיר הנוכחי ומקבל מיד את הנכס. משמש למכירות טוקנים (Gnosis Protocol, חלק מההנפקות של NFT). הפרמטר המרכזי הוא עקומת ירידת המחיר. עקומה לינארית:
function getCurrentPrice() public view returns (uint256) {
if (block.timestamp >= endTime) return reservePrice;
uint256 elapsed = block.timestamp - startTime;
uint256 totalDuration = endTime - startTime;
uint256 priceDrop = startPrice - reservePrice;
return startPrice - (priceDrop * elapsed / totalDuration);
}עקומה אקספוננציאלית מציאותית יותר לתמחור מבוסס שוק אך יקרה יותר בגז בשל timeBuffer — אנו בדרך כלל מקרובים אותה באמצעות טבלת חיפוש או לינארית לחתיכות. זה מקצץ בעלויות הגז ב-30-40%.
בעיות שאנו פותרים
מלחמות גז וגריפינג דרך החזרים
ביישומים סטנדרטיים של מכרז אנגלי, ההצעה הקודמת מוחזרת בעת הצעה גבוהה יותר:
// Опасный паттерн payable(previousBidder).transfer(previousBid); אם function getCurrentPrice() public view returns (uint256) { if (block.timestamp >= endTime) return reservePrice; uint256 elapsed = block.timestamp - startTime; uint256 totalDuration = endTime - startTime; uint256 priceDrop = startPrice - reservePrice; return startPrice - (priceDrop * elapsed / totalDuration); } הוא חוזה עם fallback שתמיד exp(), כל המכרז נתקע. זהו DoS קלאסי דרך גריפינג בגז. פתרון: תבנית משיכה — במקום החזר אוטומטי, אחסן משיכות ממתינות במיפוי ותן למשתמש לתבוע את ה-ETH בעצמו:
mapping(address => uint256) public pendingReturns;
function bid() external payable {
// ...
pendingReturns[previousBidder] += previousBid;
// новая ставка принята, старая не возвращается автоматически
}
function withdraw() external {
uint256 amount = pendingReturns[msg.sender];
if (amount == 0) revert NothingToWithdraw();
pendingReturns[msg.sender] = 0; // обнуляем до transfer (reentrancy guard)
(bool ok,) = payable(msg.sender).call{value: amount}("");
if (!ok) revert TransferFailed();
}תשלום משיכה בטוח פי 3 מדחיפה במכרזים מרובי-חלקים — ביקורות החוזה שלנו מאשרות זאת. לקוחות חוסכים בממוצע $2,000 בעלויות גז לכל מכרז. בנוסף, אנו משתמשים בארכיטקטורה מודולרית, המפחיתה את עלות השימוש החוזר ב-40%.
סכמת התחייבות נגד פרונט-ראנינג
למכרזים שבהם סודיות ההצעה חשובה עד הסגירה (מכרז הצעות סגורות), אנו משתמשים ב-commit-reveal:
- שלב ההתחייבות: המשתתף שולח
// Опасный паттерн payable(previousBidder).transfer(previousBid);— hash של ההצעה - שלב החשיפה: המשתתף חושף את ההצעה בפועל ואת ה-salt, החוזה בודק את ה-hash
- הזוכה נקבע רק לאחר החשיפה
מגבלה: משתתף עלול שלא לחשוף אם הוא מבין שיפסיד. פתרון: הפקדה בעת ההתחייבות שנשרפת אם הוא לא חושף (אגרת אנטי-גריפינג). זה הופך את commit-reveal לבטוח פי 5 נגד פרונט-ראנינג מאשר הצעה ישירה.
Reentrancy במכרזים מרובי-חלקים
במכרזים מקבילים (שוק עם הרבה חלקים), reentrancy דרך החזר ETH מסוכן במיוחד. אנו משתמשים ב-previousBidder מ-OpenZeppelin או עוקבים בקפדנות אחר תבנית checks-effects-interactions:
// Checks
require(bid > currentHighestBid + minBidIncrement);
// Effects — обновляем state ДО внешних вызовов
highestBid = bid;
highestBidder = msg.sender;
pendingReturns[previousBidder] += previousAmount;
// Interactions — только после
emit BidPlaced(msg.sender, bid); כיצד להגן על מכרז מפני פרונט-ראנינג?
פרונט-ראנינג הוא התקפה שבה יריב מיירט עסקה ועוקף אותה. עבור מכרזים, ההגנה היעילה ביותר היא שילוב של הארכת הצעות ו-commit-reveal. הארכת זמן מונעת מבוט לזכות בשניות האחרונות; commit-reveal מסתיר את סכום ההצעה עד החשיפה. בחוזים שלנו, אנו משתמשים גם באגרת אנטי-גריפינג שמענישה אי-חשיפה. אמצעים אלה מפחיתים את הסיכון להתקפה מוצלחת ביותר מ-90%.
מדוע תשלום משיכה בטוח יותר מדחיפה?
העברת דחיפה (למשל, revert) מפעילה את הקוד של הנמען, מה שעלול להוביל ל-reentrancy או לתקיעה. תשלום משיכה משאיר את היוזמה לנמען, מפחית סיכוני גז ומונע DoS. במכרזים שלנו, כל החזרי ההצעות מיושמים דרך משיכה — זה מגביר את האבטחה פי 3 בהשוואה לדחיפה, במיוחד במסחר מרובה-חלקים. יותר מ-100,000 הצעות עובדו דרך החוזים שלנו עם אפס תקלות אבטחה.
שלבי פיתוח המכרז
- ניתוח. קביעת סוג המכרז, נכסים (NFT/טוקנים/נדל"ן), שרשרת יעד (Ethereum mainnet, Polygon, Arbitrum), דרישות סודיות הצעות.
- עיצוב. בחירת מכניקת החזרים (משיכה מול דחיפה), מנגנוני אנטי-גריפינג, פרמטרי הארכת זמן. אם יש מספר סוגי מכרזים, עיצוב ארכיטקטורה מודולרית עם חוזה בסיס.
- פיתוח ובדיקות. בדיקות Foundry עם כיסוי של 99%+. חובה: בדיקות fork עם מצב mainnet אמיתי, בדיקות fuzz על פונקציות תמחור, בדיקות invariant לבדיקת אינווריאנטים (סכום כל ההחזרים הממתינים ≤ יתרת החוזה).
- פריסה. אימות ב-Etherscan/Polygonscan. אם עבור שוק NFT, אינטגרציה עם הפרונטאנד דרך wagmi/viem.
| שלב | משך | תוצאה |
|---|---|---|
| ניתוח | יום אחד | מפרט ובחירת שרשרת |
| עיצוב | 1-2 ימים | ארכיטקטורה ותבניות |
| פיתוח + בדיקות | 3-5 ימים (בודד) | חוזה עם כיסוי של 99% |
| פריסה + אינטגרציה | 1-2 ימים | חוזה מאומת ו-API |
מה כלול
- חוזה חכם למכרז עם המכניקה הנבחרת
- מערכת בדיקות יחידה ו-fuzz (כיסוי 99%+)
- אינטגרציה עם פרונטאנד (wagmi/viem)
- תיעוד ומדריך פריסה
- תמיכה לאחר השקה
טכנולוגיות וכלים
אנו מפתחים ב-Solidity 0.8.x עם Foundry. אנו בודקים עם fork של mainnet דרך mapping(address => uint256) public pendingReturns; function bid() external payable { // ... pendingReturns[previousBidder] += previousBid; // новая ставка принята, старая не возвращается автоматически } function withdraw() external { uint256 amount = pendingReturns[msg.sender]; if (amount == 0) revert NothingToWithdraw(); pendingReturns[msg.sender] = 0; // обнуляем до transfer (reentrancy guard) (bool ok,) = payable(msg.sender).call{value: amount}(""); if (!ok) revert TransferFailed(); } — זה מאפשר לבדוק אינטראקציה עם חוזי NFT אמיתיים (ERC-721, ERC-1155) ועדכוני מחיר של Chainlink עבור denominization של הצעות. Fuzzing דרך keccak256(abi.encode(bid, salt, address)) הוא חובה עבור פונקציות תמחור — במיוחד עבור מכרז הולנדי עם עקומות ירידה, שם יש סיכון לגלישת מספרים שלמים בערכי timestamp קיצוניים. מכרזים ל-NFT תומכים סטנדרטית ב-ERC-721 ו-ERC-1155 דרך ReentrancyGuard / // Checks require(bid > currentHighestBid + minBidIncrement); // Effects — обновляем state ДО внешних вызовов highestBid = bid; highestBidder = msg.sender; pendingReturns[previousBidder] += previousAmount; // Interactions — только после emit BidPlaced(msg.sender, bid); . חוזה המכרז פועל כנאמן — מחזיק את ה-NFT מהרישום ועד הסיום ומעביר לזוכה.
| פרמטר | מכרז אנגלי | מכרז הולנדי |
|---|---|---|
| כיוון מחיר | עולה | יורד |
| סיום | טיימר (עם הארכה) | זמן קבוע |
| סיכוני גז | גבוהים (תחרות) | נמוכים (עסקה אחת) |
| הגנה מפני סנדוויץ' | הארכת הצעות | לא נדרש |
| מקרה שימוש טיפוסי | NFTs, טוקנים נדירים | מכירות טוקנים, הנפקות |
דרישות סודיות ורישוי
למכרזי הצעות סגורות עם דרישות פרטיות גבוהות, אנו מיישמים בנוסף הצפנת הצעות ברמת החוזה. רישיון MIT או GPL — הבחירה שלכם. במידת הצורך, אנו מוכנים לחתום על NDA.
הערכות זמנים
חוזה מכרז בודד (אנגלי או הולנדי): 3-5 ימים כולל בדיקות. שוק מרובה-חלקים עם שני סוגי המכרזים ו-commit-reveal: 2-3 שבועות. העלות נקבעת באופן אישי.
הזמינו פיתוח חוזה מכרז — קבלו הגנה מפני פרונט-ראנינג. צרו קשר לייעוץ.







