--- title: 'Solidity WTF 103 35 單元 荷蘭拍賣' lang: zh-tw --- Solidity WTF 103 35 單元 荷蘭拍賣 === :::info :date: 2024/10/13 ::: [TOC] # 荷蘭拍賣 是一種銷售拍賣方式,透過荷蘭拍賣發售ERC721的NFT。荷蘭拍賣也稱為減價拍賣,價格由高至低遞減的一種拍賣方式,直到競賣人應價(達到或是超過底價)時成交的一種拍賣。 >Azuki 和 World of Women 都是使用這種方式 使用這種方式有兩種原因: - 項目方獲益最大,因為價格是由高至低,想要這件NFT的用戶可能會在很高點應價。 - 拍賣時間很長,避免gas war。 ## 宣告變數 會有幾個狀態變量需要宣告 - NFT總量 - 起拍價,也是最高價 - 結束價,最低價/地板價 - 拍賣持續時間 - 每過多少時間,價格會衰減一次 - 拍賣開始時間 ```javascript! uint256 public constant COLLECTOIN_SIZE = 10000; // NFT總數 uint256 public constant AUCTION_START_PRICE = 1 ether; // 起拍價(最高價) uint256 public constant AUCTION_END_PRICE = 0.1 ether; // 結束價(最低價/地板價) uint256 public constant AUCTION_TIME = 10 minutes; // 拍賣時間,為了測試方便設為10分鐘 uint256 public constant AUCTION_DROP_INTERVAL = 1 minutes; // 每過多久時間,價格衰減一次 uint256 public constant AUCTION_DROP_PER_STEP = (AUCTION_START_PRICE - AUCTION_END_PRICE) / (AUCTION_TIME / AUCTION_DROP_INTERVAL); // 每次價格衰減步長 ``` :::warning `AUCTION_DROP_PER_STEP`: - 分子部分:`AUCTION_START_PRICE` - `AUCTION_END_PRICE` 這代表從拍賣開始到結束,價格總共下跌的總幅度。比如,從 `1 ether` 跌到 `0.1 ether`,總共下跌 `0.9 ether`。 - 分母部分:`AUCTION_TIME` / `AUCTION_DROP_INTERVAL` 這部分代表拍賣過程中,總共經歷了多少次價格衰減的步驟(`step`)。比如,如果總拍賣時間是 `10` 分鐘,而每分鐘價格下跌一次,那麼總共會有 `10` 步(步驟)。 ::: ## 函數 > 需要 set 起拍時間 ```javascript! function setAuctionStartTime(uint32 timestamp) external onlyOwner { auctionStartTime = timestamp; } ``` > 獲取拍賣當下的價格 >> 區塊時間若小於起始時間,價格為最高價。 >> 區塊時間若大於結束時間,價格為最低價。 >> 區塊時間若處於兩者之間,則計算當前衰減價格。 ```javascript! function getAuctionPrice() public view returns (uint256) { // 區塊時間小於起始時間 if (block.timestamp < auctionStartTime) { return AUCTION_START_PRICE; // 區塊時間大於結束時間 }else if (block.timestamp - auctionStartTime >= AUCTION_TIME) { return AUCTION_END_PRICE; } else { // steps 計算的是當前已經經過了多少個價格遞減的間隔(AUCTION_DROP_INTERVAL)。這是通過將已經過去的時間(block.timestamp - auctionStartTime)除以每次價格遞減的時間間隔來得到的。 uint256 steps = (block.timestamp - auctionStartTime) / AUCTION_DROP_INTERVAL; // 根據已經過了多少個間隔,計算總共應該降低多少價格(steps * AUCTION_DROP_PER_STEP),最後從起拍價中扣除這個金額來得到當前的價格。 return AUCTION_START_PRICE - (steps * AUCTION_DROP_PER_STEP); } } ``` ## mint 支付ETH參加拍賣並鑄造NFT >先檢查是否開始/鑄造是否超出NFT總量,通過getAuctionPrice()和鑄造數量計算拍賣成本,並檢查用戶支付ETH是否足夠; 如果足夠將NFT鑄造給用戶並退回多出的ETH,反之,退回交易(revert)。 ```javascript! function auctionMint(uint256 quantity) external payable{ uint256 _saleStartTime = uint256(auctionStartTime); // 建立 local 變數,減少 gas 消耗 require( _saleStartTime != 0 && block.timestamp >= _saleStartTime, "拍賣尚未開始" ); // 檢查是否已設置起拍時間,以及拍賣是否已開始 require( totalSupply() + quantity <= COLLECTOIN_SIZE, "剩餘拍賣預留的 NFT 數量不足,無法支持所需的鑄造數量" ); // 檢查是否超過 NFT 限量 uint256 totalCost = getAuctionPrice() * quantity; // 計算鑄造成本 require(msg.value >= totalCost, "需要支付更多的 ETH."); // 檢查用戶是否支付足夠的 ETH // 鑄造 NFT for (uint256 i = 0; i < quantity; i++) { uint256 mintIndex = totalSupply(); _mint(msg.sender, mintIndex); _addTokenToAllTokensEnumeration(mintIndex); } // 多餘的 ETH 退款 if (msg.value > totalCost) { payable(msg.sender).transfer(msg.value - totalCost); // 注意是否存在重入風險 } } ``` ## 提款函數 項目方通過此函數提走拍賣籌集的ETH。 ```javascript! function withdrawMoney() external onlyOwner { (bool success, ) = msg.sender.call{value: address(this).balance}(""); require(success, "Transfer failed."); } ``` :::success 這邊是簡化的荷蘭拍賣,透過此拍賣方式發售ERC721標準NFT。 :::
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up