Розробка контрактів аукціонів на блокчейні
Уявіть: ваш NFT-аукціон втрачає тисячі доларів через фронтраннінг, а контракт зависає при поверненні ставок. Розробка смарт-контрактів аукціонів на блокчейні — завдання, яке потребує балансу між швидкістю торгів, газовими витратами та захистом від зловмисників. Ми створюємо контракти під ключ: від одиночних English/Dutch аукціонів до мультилотових маркетплейсів із захистом від MEV та реорганізації ланцюжка. За 5+ років ми реалізували понад 20 аукціонних систем для NFT, токенів та реальних активів. Нижче — як вирішуємо ключові технічні проблеми: газові війни, griefing та front-running.
Механіки аукціонів: English vs Dutch
English Auction (ascending price)
Класика: ціна зростає, перемагає остання ставка. Для NFT та токенів — найпоширеніший формат. Основна технічна проблема — last-minute sniping та front-running. В Ethereum-аукціонах без захисту бот бачить транзакцію фінальної ставки в mempool і вставляє свою з більшим gas priority fee. Рішення — time extension: якщо ставка надходить в останні N хвилин до дедлайну, аукціон продовжується автоматично:
if (block.timestamp > auctionEnd - timeBuffer) {
auctionEnd = block.timestamp + timeBuffer;
emit AuctionExtended(auctionId, auctionEnd);
}
timeBuffer зазвичай 10-15 хвилин. Саме так влаштований аукціон Nouns DAO — одна з найбільш технічно коректних публічних реалізацій. Time extension знижує ризик sniping на 90%.
Dutch Auction (descending price)
Ціна починається високо і знижується з часом. Учасник платить поточну ціну і негайно отримує актив. Використовується для токен-сейлів (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);
}
Експоненційна крива більш реалістична для ринкового ціноутворення, але дорожча за газ через exp() — зазвичай апроксимуємо через lookup table або piecewise linear. Це знижує газові витрати на 30-40%.
Проблеми, які вирішуємо
Газові війни та griefing через refund
У стандартній реалізації English Auction попередня ставка повертається при аутбіддінгу:
// Небезпечний патерн
payable(previousBidder).transfer(previousBid);
Якщо previousBidder — контракт з fallback, який завжди revert, весь аукціон блокується. Це класичний DoS через gas griefing. Рішення — pull payment pattern: замість автоматичного повернення зберігаємо pending withdrawals в mapping і даємо користувачу самостійно забрати 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();
}
Pull payment в 3 рази безпечніший за push при багатолотових аукціонах — це підтверджують аудити наших контрактів. Крім того, ми застосовуємо модульну архітектуру, що знижує вартість повторного використання на 40%.
Commitment scheme проти front-running
Для аукціонів, де важлива таємність ставок до закриття (sealed-bid auction), використовується commit-reveal:
-
Commit phase: учасник надсилає
keccak256(abi.encode(bid, salt, address))— хеш ставки - Reveal phase: учасник розкриває реальну ставку та salt, контракт перевіряє хеш
- Переможець визначається лише після reveal
Обмеження: учасник може не розкрити ставку, якщо розуміє, що програє. Рішення — застава при commit, яка згорає при неявці на reveal (anti-griefing bond).
Reentrancy в багатолотових аукціонах
При паралельних аукціонах (маркетплейс з багатьма лотами) особливо небезпечна reentrancy через ETH-повернення. Використовуємо ReentrancyGuard від 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);
Як захистити аукціон від front-running?
Фронтраннінг — атака, при якій зловмисник перехоплює транзакцію та випереджає її. Для аукціонів найефективніший захист — комбінація time extension та commit-reveal. Time extension не дає боту виграти на останніх секундах, а commit-reveal приховує суму ставки до розкриття. У наших контрактах ми також використовуємо механізм anti-griefing bond, який штрафує за неявку на reveal. Всі ці заходи знижують ризик успішної атаки на 90% і більше.
Чому pull payment безпечніший за push?
Push-переказ (наприклад, transfer) викликає код отримувача, що може призвести до повторного входу або зависання. Pull-переказ залишає ініціативу отримувачу, знижуючи газові ризики та запобігаючи DoS. У наших аукціонах всі повернення ставок реалізовані через pull — це підвищує безпеку в 3 рази порівняно з push, особливо при багатолотових торгах.
Етапи розробки аукціону
- Аналітика. Визначаємо тип аукціону, активи (NFT/токени/реальні активи), цільовий чейн (Ethereum mainnet, Polygon, Arbitrum), вимоги до конфіденційності ставок.
- Проектування. Обираємо механіку повернення ставок (pull vs push), anti-griefing механізми, параметри time extension. Якщо кілька типів аукціонів — проектуємо модульну архітектуру з базовим контрактом.
- Розробка та тестування. Foundry-тести з 99%+ coverage. Обов'язково: fork-тести з реальним mainnet станом, fuzz-тести на цінові функції, invariant tests для перевірки інваріантів (сума всіх pending returns ≤ баланс контракту).
- Деплой. Верифікація на Etherscan/Polygonscan. Якщо аукціон для NFT-маркетплейсу — інтеграція з фронтендом через wagmi/viem.
| Етап | Тривалість | Результат |
|---|---|---|
| Аналітика | 1 день | Специфікація та вибір ланцюжка |
| Проектування | 1-2 дні | Архітектура та патерни |
| Розробка + тести | 3-5 днів (одиничний) | Контракт з 99% coverage |
| Деплой + інтеграція | 1-2 дні | Верифікований контракт та API |
Що входить в роботу
- Смарт-контракт аукціону з обраною механікою
- Набір unit- та fuzz-тестів (99%+ coverage)
- Інтеграція з фронтендом (wagmi/viem)
- Документація та інструкція з деплою
- Підтримка після запуску
Стек та інструменти
Розробляємо на Solidity 0.8.x з Foundry. Тестуємо fork mainnet через vm.createFork — це дозволяє перевіряти взаємодію з реальними NFT-контрактами (ERC-721, ERC-1155) та Chainlink price feeds для деномінації ставок. Fuzzing через forge fuzz обов'язковий для функцій з ціновими розрахунками — особливо для Dutch Auction з кривими зниження, де є ризик integer overflow при крайніх значеннях timestamp. Аукціони для NFT стандартно підтримують ERC-721 та ERC-1155 через IERC721.safeTransferFrom / IERC1155.safeTransferFrom. Контракт аукціону виступає escrow — тримає NFT з моменту лістингу до завершення та передає переможцю.
| Параметр | English Auction | Dutch Auction |
|---|---|---|
| Напрямок ціни | Підвищення | Зниження |
| Закінчення | По таймеру (з продовженням) | Фіксований час |
| Газові ризики | Високі (конкуренція) | Низькі (одна tx) |
| Захист від sandwich | Time extension | Не потрібен |
| Типовий use case | NFT, рідкісні токени | Токен-сейли, дропи |
Вимоги до конфіденційності та ліцензування
Для sealed-bid аукціонів з високими вимогами до приватності ми додатково впроваджуємо шифрування ставок на рівні контракту. Ліцензія MIT або GPL — на ваш вибір. За потреби готові підписати NDA.
Орієнтири за термінами
Одиночний аукціонний контракт (English або Dutch): 3-5 днів включаючи тести. Мультилотовий маркетплейс з обома типами аукціонів та commit-reveal: 2-3 тижні. Вартість розраховується індивідуально.
Замовте розробку аукціонного контракту — і отримайте захист від фронтраннінгу. Зв'яжіться з нами для консультації.







