Web Developer DeFi
Web Developer DeFi

Subscribe to Smithereens

Subscribe to Smithereens
Share Dialog
Share Dialog


<100 subscribers
<100 subscribers
今天来聊聊一个神奇的项目 PartyDAO,这个项目最近融资1640万美元,估值两亿美金,其来源于推特的一番对话,具体可以参考超哥的一篇文章:PartyDAO的故事,对话的后续是 Mirror 的创始人 Denis Nazarov 在推特上提出了一个想法并讲了基本的流程,我把整个逻辑翻译下如下: 如果部署自动一个 DAO 来竞标任何 NFT 拍卖,那不是很酷吗?给它起名 PartyBid,它包含以下功能:
构造函数:auction_id
任何人存入 ETH,取回 ERC20
合同自动投标拍卖
如果获胜,资助者将获得 NFT 的部分所有权
如果输了,资金退回
这个想法被一个程序员大佬 _anishagnihotri 看到了,于是他就在一个周末把这个功能实现了,在这里膜拜大佬,我们这里就看看整个代码是怎样实现的,首先大佬先把 Denis Nazarov 提出的想法扩展了下,想法毕竟是想法不是完整的 PM 需求,日常开发如果 PM 提出了这么个不完整的需求肯定是会挨程序员的骂的,但这里 anishagnihotri 显然是对这个想法感兴趣的,所以就把需求完善了下并描述了下该合约是如何工作的:
首先部署一个 PartyBidRA.sol 合约,构造函数有四个参数 (_ReserveAuctionV3Address, _auctionID, _bidAmount, _exitTimeout),其中 _ReserveAuctionV3Address 我们随后再讲,_auctionID 对应的是某 NFT 的 TokenId,bidAmount 对应的是出价,这里应该是 ETH 计价,exitTimeout 过期时间。
接下来,个人可以调用 join() 方法成为 DAO 的成员,发送他们的 ETH 来积累后续投标拍卖的资金。
于是后续会产生三种可能性:
这个 DAO 在 exitTimeout 时间之前参与人过少,筹集的 ETH 没有达到预订的 bidAmount, 此前参与的用户可以调用合约的 exit() 方法来提取他们投资的 ETH。
如果达到了预订的 bidAmount,DAO 成员可以调用 placeBid() 进行出价。如果 DAO 未能成功赢得 NFT,DAO 成员可以调用 exit() 来提取他们的资金。
筹集的资金达到了预订的 bidAmount,并且最终出价成功买入了该 NFT,后续会有如下的可能操作:
DAO 成员可以调用 DAOProposeZoraBid() 方法发起一个新的提案:在 Zora 市场将该 NFT 进行出价卖出。
DAO 成员可以调用 DAOVoteForZoraBidProposal() 对上述已提出的提案进行投票,提案需要超过 50% 的选票才能被执行。
DAO 成员可以调用 DAOExecuteZoraBid() 来执行一个提案,该提案:(1) 拥有超过 50% 的 DAO 投票权,并且 (2) 自投票发起以来没有改变(以防止不良行为者在投票期间改变他们的出价) 投票过程)。 这会将 NFT 转移给出价者,并接受他们的资金到合约中。
最后,一旦成功转售,DAO 成员可以调用 exit() 来收回他们在投标金额中的份额。
至此,整个功能的描述就完成了,当然代码也是如此完成的。上述整个流程对于不在这个圈子里面的人来说有点难以理解,这里我举个例子去描述下: 小明想买一套房子,该房子房主目前出价 100 万,小明买不起,于是发起了一个众筹,一伙人看到了也觉得这个房子100万价格合适,于是纷纷加入,最终10个人加入了,每人出资10万元,成功将该房子买入了,但这个区域是学区房,房价过了一段时间还在涨,一直涨到了150左右,其中小张坐不住了,说咱们把房子卖出去吧,于是就发起了一个提案说咱们以 150 万的价格卖出,众人觉得可以,纷纷投票赞同,投票数超过5个人的时候,小张就执行了该提议,把房子卖给了出价 150万 的那个人,于是最后每个人欢欢喜喜的拿走了自己的15万。
整个流程其实和上述描述的大差不差,唯一和现实不同的是整个操作流程是在链上完成的,都是公开透明的,资金也都是按照个人初始投资进行均分的。
OK,基本流程已经说完了,我们看下代码:
首先说下 _ReserveAuctionV3Address 这个是拍卖合约的地址,placeBid() 方法其实针对的是某一个拍卖发起的报价,所以这个流程执行的过程依赖拍卖方的,创建拍卖的方法如下:
function createAuction(
uint256 tokenId,
uint256 duration,
uint256 reservePrice,
uint8 curatorFeePercent,
address curator,
address payable fundsRecipient
) external nonReentrant whenNotPaused auctionNonExistant(tokenId) {
// Check basic input requirements are reasonable.
require(curator != address(0));
require(fundsRecipient != address(0));
require(curatorFeePercent < 100, "Curator fee should be < 100");
// 初始化拍卖结构体信息
auctions[tokenId] = Auction({
duration: duration,
reservePrice: reservePrice,
curatorFeePercent: curatorFeePercent,
curator: curator,
fundsRecipient: fundsRecipient,
amount: 0,
firstBidTime: 0,
bidder: address(0)
});
// Transfer the NFT into this auction contract, from whoever owns it.
// 将要拍卖的 NFT 转移到拍卖合约
IERC721(nftContract).transferFrom(
IERC721(nftContract).ownerOf(tokenId),
address(this),
tokenId
);
}
当一个拍卖发起后,才可以执行 placeBid 进行叫价,最终到达拍卖结束时间后叫价最高的账户将赢得拍卖,结束拍卖的方法如下:
function endAuction(uint256 tokenId)
external
nonReentrant
whenNotPaused
auctionComplete(tokenId)
{
// Store relevant auction data in memory for the life of this function.
address winner = auctions[tokenId].bidder;
uint256 amount = auctions[tokenId].amount;
address curator = auctions[tokenId].curator;
uint8 curatorFeePercent = auctions[tokenId].curatorFeePercent;
address payable fundsRecipient = auctions[tokenId].fundsRecipient;
// Remove all auction data for this token from storage.
delete auctions[tokenId];
// We don't use safeTransferFrom, to prevent reverts at this point,
// which would break the auction.
// 赢的拍卖的获取到该 NFT
IERC721(nftContract).transferFrom(address(this), winner, tokenId);
// First handle the curator's fee.
if (curatorFeePercent > 0) {
// Determine the curator amount, which is some percent of the total.
uint256 curatorAmount = amount.mul(curatorFeePercent).div(100);
// Send it to the curator.
transferETHOrWETH(curator, curatorAmount);
// Subtract the curator amount from the total funds available
// to send to the funds recipient and original NFT creator.
amount = amount.sub(curatorAmount);
// Emit the details of the transfer as an event.
emit CuratorFeePercentTransfer(tokenId, curator, curatorAmount);
}
// Get the address of the original creator, so that we can split shares
// if appropriate.
address payable nftCreator =
payable(
address(IMediaModified(nftContract).tokenCreators(tokenId))
);
// If the creator and the recipient of the funds are the same
// (and we expect this to be common), we can just do one transaction.
if (nftCreator == fundsRecipient) {
transferETHOrWETH(nftCreator, amount);
} else {
// Otherwise, we should determine the percent that goes to the creator.
// Collect share data from Zora.
uint256 creatorAmount =
// Call the splitShare function on the market contract, which
// takes in a Decimal and an amount.
IMarket(IMediaModified(nftContract).marketContract())
.splitShare(
// Fetch the decimal from the BidShares data on the market.
IMarket(IMediaModified(nftContract).marketContract())
.bidSharesForToken(tokenId)
.creator,
// Specify the amount.
amount
);
// 分红给 nftCreator
transferETHOrWETH(nftCreator, creatorAmount);
// 转移剩下的资金给拍卖方
transferETHOrWETH(fundsRecipient, amount.sub(creatorAmount));
}
}
最核心的代码就是上面两部分了,完整的代码可以这里查看partybid初始代码。但你会发现和官网的操作并不一样,因为这份代码是最原始的版本,随着项目的进展,他们后续引入了 PM 以及有合约开发自荐进行迭代开发,新合约代码地址。所以你会发现,现在 partybid官网 的操作是没有拍卖的,因为后续是接入了类似 Opensea 市场直接进行买入,简化了拍卖的这一步,侧重点放到了众筹买入这个功能,当然有时间也可以积极的去官网进行参与下,说不定后面有空投惊喜呢。
今天来聊聊一个神奇的项目 PartyDAO,这个项目最近融资1640万美元,估值两亿美金,其来源于推特的一番对话,具体可以参考超哥的一篇文章:PartyDAO的故事,对话的后续是 Mirror 的创始人 Denis Nazarov 在推特上提出了一个想法并讲了基本的流程,我把整个逻辑翻译下如下: 如果部署自动一个 DAO 来竞标任何 NFT 拍卖,那不是很酷吗?给它起名 PartyBid,它包含以下功能:
构造函数:auction_id
任何人存入 ETH,取回 ERC20
合同自动投标拍卖
如果获胜,资助者将获得 NFT 的部分所有权
如果输了,资金退回
这个想法被一个程序员大佬 _anishagnihotri 看到了,于是他就在一个周末把这个功能实现了,在这里膜拜大佬,我们这里就看看整个代码是怎样实现的,首先大佬先把 Denis Nazarov 提出的想法扩展了下,想法毕竟是想法不是完整的 PM 需求,日常开发如果 PM 提出了这么个不完整的需求肯定是会挨程序员的骂的,但这里 anishagnihotri 显然是对这个想法感兴趣的,所以就把需求完善了下并描述了下该合约是如何工作的:
首先部署一个 PartyBidRA.sol 合约,构造函数有四个参数 (_ReserveAuctionV3Address, _auctionID, _bidAmount, _exitTimeout),其中 _ReserveAuctionV3Address 我们随后再讲,_auctionID 对应的是某 NFT 的 TokenId,bidAmount 对应的是出价,这里应该是 ETH 计价,exitTimeout 过期时间。
接下来,个人可以调用 join() 方法成为 DAO 的成员,发送他们的 ETH 来积累后续投标拍卖的资金。
于是后续会产生三种可能性:
这个 DAO 在 exitTimeout 时间之前参与人过少,筹集的 ETH 没有达到预订的 bidAmount, 此前参与的用户可以调用合约的 exit() 方法来提取他们投资的 ETH。
如果达到了预订的 bidAmount,DAO 成员可以调用 placeBid() 进行出价。如果 DAO 未能成功赢得 NFT,DAO 成员可以调用 exit() 来提取他们的资金。
筹集的资金达到了预订的 bidAmount,并且最终出价成功买入了该 NFT,后续会有如下的可能操作:
DAO 成员可以调用 DAOProposeZoraBid() 方法发起一个新的提案:在 Zora 市场将该 NFT 进行出价卖出。
DAO 成员可以调用 DAOVoteForZoraBidProposal() 对上述已提出的提案进行投票,提案需要超过 50% 的选票才能被执行。
DAO 成员可以调用 DAOExecuteZoraBid() 来执行一个提案,该提案:(1) 拥有超过 50% 的 DAO 投票权,并且 (2) 自投票发起以来没有改变(以防止不良行为者在投票期间改变他们的出价) 投票过程)。 这会将 NFT 转移给出价者,并接受他们的资金到合约中。
最后,一旦成功转售,DAO 成员可以调用 exit() 来收回他们在投标金额中的份额。
至此,整个功能的描述就完成了,当然代码也是如此完成的。上述整个流程对于不在这个圈子里面的人来说有点难以理解,这里我举个例子去描述下: 小明想买一套房子,该房子房主目前出价 100 万,小明买不起,于是发起了一个众筹,一伙人看到了也觉得这个房子100万价格合适,于是纷纷加入,最终10个人加入了,每人出资10万元,成功将该房子买入了,但这个区域是学区房,房价过了一段时间还在涨,一直涨到了150左右,其中小张坐不住了,说咱们把房子卖出去吧,于是就发起了一个提案说咱们以 150 万的价格卖出,众人觉得可以,纷纷投票赞同,投票数超过5个人的时候,小张就执行了该提议,把房子卖给了出价 150万 的那个人,于是最后每个人欢欢喜喜的拿走了自己的15万。
整个流程其实和上述描述的大差不差,唯一和现实不同的是整个操作流程是在链上完成的,都是公开透明的,资金也都是按照个人初始投资进行均分的。
OK,基本流程已经说完了,我们看下代码:
首先说下 _ReserveAuctionV3Address 这个是拍卖合约的地址,placeBid() 方法其实针对的是某一个拍卖发起的报价,所以这个流程执行的过程依赖拍卖方的,创建拍卖的方法如下:
function createAuction(
uint256 tokenId,
uint256 duration,
uint256 reservePrice,
uint8 curatorFeePercent,
address curator,
address payable fundsRecipient
) external nonReentrant whenNotPaused auctionNonExistant(tokenId) {
// Check basic input requirements are reasonable.
require(curator != address(0));
require(fundsRecipient != address(0));
require(curatorFeePercent < 100, "Curator fee should be < 100");
// 初始化拍卖结构体信息
auctions[tokenId] = Auction({
duration: duration,
reservePrice: reservePrice,
curatorFeePercent: curatorFeePercent,
curator: curator,
fundsRecipient: fundsRecipient,
amount: 0,
firstBidTime: 0,
bidder: address(0)
});
// Transfer the NFT into this auction contract, from whoever owns it.
// 将要拍卖的 NFT 转移到拍卖合约
IERC721(nftContract).transferFrom(
IERC721(nftContract).ownerOf(tokenId),
address(this),
tokenId
);
}
当一个拍卖发起后,才可以执行 placeBid 进行叫价,最终到达拍卖结束时间后叫价最高的账户将赢得拍卖,结束拍卖的方法如下:
function endAuction(uint256 tokenId)
external
nonReentrant
whenNotPaused
auctionComplete(tokenId)
{
// Store relevant auction data in memory for the life of this function.
address winner = auctions[tokenId].bidder;
uint256 amount = auctions[tokenId].amount;
address curator = auctions[tokenId].curator;
uint8 curatorFeePercent = auctions[tokenId].curatorFeePercent;
address payable fundsRecipient = auctions[tokenId].fundsRecipient;
// Remove all auction data for this token from storage.
delete auctions[tokenId];
// We don't use safeTransferFrom, to prevent reverts at this point,
// which would break the auction.
// 赢的拍卖的获取到该 NFT
IERC721(nftContract).transferFrom(address(this), winner, tokenId);
// First handle the curator's fee.
if (curatorFeePercent > 0) {
// Determine the curator amount, which is some percent of the total.
uint256 curatorAmount = amount.mul(curatorFeePercent).div(100);
// Send it to the curator.
transferETHOrWETH(curator, curatorAmount);
// Subtract the curator amount from the total funds available
// to send to the funds recipient and original NFT creator.
amount = amount.sub(curatorAmount);
// Emit the details of the transfer as an event.
emit CuratorFeePercentTransfer(tokenId, curator, curatorAmount);
}
// Get the address of the original creator, so that we can split shares
// if appropriate.
address payable nftCreator =
payable(
address(IMediaModified(nftContract).tokenCreators(tokenId))
);
// If the creator and the recipient of the funds are the same
// (and we expect this to be common), we can just do one transaction.
if (nftCreator == fundsRecipient) {
transferETHOrWETH(nftCreator, amount);
} else {
// Otherwise, we should determine the percent that goes to the creator.
// Collect share data from Zora.
uint256 creatorAmount =
// Call the splitShare function on the market contract, which
// takes in a Decimal and an amount.
IMarket(IMediaModified(nftContract).marketContract())
.splitShare(
// Fetch the decimal from the BidShares data on the market.
IMarket(IMediaModified(nftContract).marketContract())
.bidSharesForToken(tokenId)
.creator,
// Specify the amount.
amount
);
// 分红给 nftCreator
transferETHOrWETH(nftCreator, creatorAmount);
// 转移剩下的资金给拍卖方
transferETHOrWETH(fundsRecipient, amount.sub(creatorAmount));
}
}
最核心的代码就是上面两部分了,完整的代码可以这里查看partybid初始代码。但你会发现和官网的操作并不一样,因为这份代码是最原始的版本,随着项目的进展,他们后续引入了 PM 以及有合约开发自荐进行迭代开发,新合约代码地址。所以你会发现,现在 partybid官网 的操作是没有拍卖的,因为后续是接入了类似 Opensea 市场直接进行买入,简化了拍卖的这一步,侧重点放到了众筹买入这个功能,当然有时间也可以积极的去官网进行参与下,说不定后面有空投惊喜呢。
No activity yet