本文介绍了如何在以太坊上创建和部署一个荷兰拍卖的智能合约。首先,文中详细解释了荷兰拍卖的概念,然后展示了必要的准备工作,包括QuickNode账户、MetaMask钱包和ERC721合约的知识。接下来,描述了具体的代码实现和部署步骤,最后展示了如何进行拍卖,并总结了整个过程。
在两个不可信的参与者之间执行任何交易时,通常需要一个值得信赖的中介;智能合约完全消除了对中介的需求。在本指南中,我们将学习如何创建一个荷兰式拍卖智能合约,这将使整个拍卖过程无需信任。
拍卖是通过竞标过程公共出售商品的平台,通常出价最高的人赢得拍卖并获得物品的所有权。虽然这就是拍卖的主要形式,但拍卖还有其他一些规则。
拍卖的不同类型有:
英式拍卖(开放的递增价格拍卖):这是大家都熟知的传统拍卖,出价最高者为拍卖的赢家。
荷兰式拍卖(开放的递减价格拍卖):这种类型的拍卖主要用于易腐烂的物品,如花卉、食品、衣物等。它有一个起始价格,随着时间的推移,价格会按照一定比例下降,出价等于或高于当前价格的竞标者赢得拍卖,或者在预设的持续时间结束时,最后出价的竞标者获胜。
首次出价封闭拍卖(盲拍):在这种类型的拍卖中,所有竞标者在不知道彼此出价的情况下提交他们的出价,随后拍卖师打开信封,出价最高者获胜。
第二次出价封闭拍卖(维克雷拍卖):与首次出价封闭拍卖相同。唯一的区别是胜利者为第二高出价者。
在每一种类型的拍卖中,拍卖师是负责进行拍卖和确定获胜者的中介,通常会收取一部分费用,通常是成交总价的一定百分比。智能合约可以消除对拍卖师的需求,并使整个过程实现自动化、无需信任且安全,因为我们将在以太坊区块链上执行此操作。
荷兰式拍卖,也称为开放式递减拍卖,是一种拍卖形式,卖方首先设定一个起始价、持续时间和折扣率。随着时间的推移,物品的价格会不断下降,直到预设的持续时间结束。例如,假设有一个你想购买但超出预算的很好包。在这种情况下,随着时间的推移,包的价格会降低;首先是 10% 的折扣,然后是 30%,再然后是 50%,直到这个包的价格便宜到你可以购买的程度。这就是荷兰式拍卖的概念。为了在以太坊上实现这一点,我们需要创建并部署一个 ERC721 合约和一个荷兰式拍卖智能合约。
我们将使用 Remix IDE 和 MetaMask 钱包在以太坊的 Sepolia 测试网上部署我们的合约。我们可以使用 MetaMask 的默认 Sepolia 网络,但 MetaMask 是大多数以太坊开发者使用的流行工具,因此网络有时会变得拥堵。为此,我们将创建一个免费的 QuickNode 账户 这里,创建一个以太坊 Sepolia 端点,并在 MetaMask 中将 QuickNode 添加为自定义提供者。
现在,你可能想知道为什么我们要在 MetaMask 中设置自定义网络。虽然我们可以使用网络列表中找到的默认 Sepolia 测试网。我们将使用自定义网络,以防止受到速率限制,并享受到高达 8 倍的更快速度。
打开你的 MetaMask 钱包,点击右上角的省略号。点击 设置 --> 网络 --> 添加网络。一旦新页面打开,点击 MetaMask 扩展页面底部的 手动添加网络 按钮。
你将被要求输入以下字段:
YOUR QUICKNODE HTTP PROVIDER URL
我们需要获取一些测试 ETH,以便支付合约的部署和交互。
导航到 QuickNode 多链水龙头,连接你的钱包或粘贴你的钱包地址。你需要选择 Ethereum 链和 Sepolia 网络,然后请求资金。
注意:你需要在以太坊主网上至少有 0.001 ETH 才能使用水龙头。
钱包充实后,我们可以进入部署部分。
现在,导航到 Remix.IDE 并创建两个新的 Solidity 文件,一个名为 dutchAuction.sol,另一个名为 NFT.sol。我们首先需要创建并部署一个 NFT,这将是我们在荷兰式拍卖合约中使用的 ERC-721 代币。
打开 NFT.sol 文件并输入以下代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyToken is ERC721, Ownable {
constructor(address initialOwner)
ERC721("MyToken", "MTK")
Ownable(initialOwner)
{}
function safeMint(address to, uint256 tokenId) public onlyOwner {
_safeMint(to, tokenId);
}
}
上述代码来自 OpenZeppelin 的 Contracts Wizard,该工具提供了开发者可以使用的预配置代码模板。
该代码利用了 OpenZeppelin 库,特别是引入了 ERC721
和 Ownable
模块。ERC721 模块提供了创建和管理 NFT 所需的基本功能,而 Ownable 模块确保合约的某些功能只能由合约所有者访问。合约使用名称 MyToken 和符号 MTK 初始化。合约的构造函数接受一个 initialOwner 地址参数,将该地址设置为初始所有者。此合约的核心功能 safeMint
函数允许所有者安全地铸造新代币。该函数使用 onlyOwner
修饰符确保只有所有者可以铸造新的 NFT。当所有者决定铸造新代币时,将调用 ERC721 模块的内部 _safeMint
函数,处理实际的铸造过程。
继续在 Remix.IDE 中部署合约,首先在 Solidity 编译器选项卡上编译它,然后在部署和运行交易选项卡上部署它。请注意合约使用了构造函数,因此你需要在 initialOwner
字段中输入一个钱包地址。一旦部署,可以在智能合约上调用 safeMint
函数来铸造 NFT,该 NFT 将在下一部分中使用。请记得保存铸造的 NFT 的合约地址,因为在部署 dutchAuction
合约时需要它。
接下来,在 dutchAuction.sol 文件中粘贴以下代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC721 {
function transferFrom(
address _from,
address _to,
uint _nftId
) external;
}
contract dutchAuction {
uint private constant DURATION = 7 days;
IERC721 public immutable nft;
uint public immutable nftId;
address payable public immutable seller;
uint public immutable startingPrice;
uint public immutable discountRate;
uint public immutable startAt;
uint public immutable expiresAt;
constructor(
uint _startingPrice,
uint _discountRate,
address _nft,
uint _nftId
) {
seller = payable(msg.sender);
startingPrice = _startingPrice;
discountRate = _discountRate;
startAt = block.timestamp;
expiresAt = block.timestamp + DURATION;
require(_startingPrice >= _discountRate * DURATION, "起始价格太低");
nft = IERC721(_nft);
nftId = _nftId;
}
function getPrice() public view returns (uint) {
uint timeElapsed = block.timestamp - startAt;
uint discount = discountRate * timeElapsed;
return startingPrice - discount;
}
function buy() external payable {
require(block.timestamp < expiresAt, "这次拍卖已结束");
uint price = getPrice();
require(msg.value >= price, "发送的 ETH 数量少于代币价格");
nft.transferFrom(seller, msg.sender, nftId);
uint refund = msg.value - price;
if (refund > 0) {
payable(msg.sender).transfer(refund);
}
selfdestruct(seller);
}
}
上述合约的解释:
关键变量:
DURATION
:一个常量,表示拍卖的持续时间,设定为 7 天。nft
:一个不可变变量,表示被拍卖的 ERC721 代币。nftId
:待拍卖的 ERC721 代币的具体 ID。seller
:ERC721 代币的最初所有者,初始化为合约的部署者。startingPrice
:拍卖开始时代币的初始价格。discountRate
:代币价格随时间减小的比率。startAt
:拍卖开始的时间戳。expiresAt
:拍卖结束的时间戳,计算为开始时的时间加上 DURATION。关键函数:
1. constructor
该函数通过初始化卖家、起始价格和其他核心变量来设置合约的初始状态。此外,它检查起始价格是否相对于折扣率和持续时间来得不太低。
2. getPrice
一个公共视图函数,根据经过的时间和折扣率计算当前代币价格。使用的公式是起始价格减去随时间累计的折扣。
3. buy
此外部可支付函数允许用户购买代币。它包含检查以确保:
拍卖尚未结束。
发送的金额至少等于当前代币价格。
在成功购买后,使用 transferFrom 方法将 ERC721 代币转移给买方。任何多余的以太都将退还给买方,合约将被终止,将其余额转给卖方。
编译合约;如果在编译过程中出现错误,请确保选择了正确的 Solidity 编译器版本。
现在,转到 deploy 选项卡,在 ENVIRONMENT 选项下选择 Injected Web3,还要确保你看到Sepolia (11155111)网络的字样;如果没有,请选择之前添加的 QuickNode Sepolia 节点,并选择 dutchAuction 下拉菜单中的 CONTRACT 选项,然后点击部署按钮旁边的小箭头并填写以下详细信息。
NFT 的起始价格,单位为 gwei,此处设置为 10,000,000 gwei。
NFT 价格每秒减少的折扣率,直到七天或价格大于零,以最早为准。此处设置为 1。
我们之前部署的 NFT 合约的地址。
我们 NFT 的 ID。
现在点击“交易按钮”并从 MetaMask 批准交易。一旦交易被批准,我们的荷兰式拍卖合约就已部署。
现在我们的荷兰式拍卖合约已经部署,让我们看看它的实际运行。第一步是批准荷兰式拍卖合约能够转移 NFT。转到已部署的 NFT 合约,展开批准函数,将荷兰式拍卖合约的地址粘贴到第一个字段中,以及我们要出售的代币的 NFT ID。
现在,切换到 MetaMask 中的另一个账户,然后展开已部署的荷兰式拍卖合约并执行以下步骤:
点击“getPrice”按钮获取 NFT 的当前价格。
复制该值,并向上滚动并粘贴到我们在部署合约时选择的合约名称上方的值字段中。
现在向下滚动回到已部署的荷兰式拍卖合约中,点击购买。
一旦交易完成,NFT 的所有权将转移,荷兰式拍卖将完成。
此外,合约现在被销毁,它不会返回任何输出:
如果你已经阅读到此,恭喜你;你正在成为一名 Solidity 专家的路上。在本指南中,我们学习了智能合约的拍卖以及编写和部署一个智能合约以进行荷兰式拍卖。
订阅我们的 新闻通讯,了解更多以太坊的文章和指南。如果你有任何反馈,请随时通过 Twitter 与我们联系。你也可以随时在我们的 Discord 社区服务器上与我们聊天,那里有一些你见过的最酷的开发者 :)
让我们知道 如果你有任何反馈或新主题的请求。我们很乐意听到你的意见。
- 原文链接: quicknode.com/guides/eth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!