wannabet 是一個鏈上打賭的合約應用,它的想法很簡單,程式碼也不難,是一個值得初學者入門研究的專案。
"escrow" 是指「第三方托管」,智能合約很適合做這類型的應用,我們可以把錢丟進合約,根據程式邏輯與條件,從合約取回託管的代幣。合約的邏輯在鏈上是固定的,用戶若懂得辨別地址與合約程式碼是否匹配、看懂合約的規則,以及確認合約的升級條件等等,將有助於了解鏈上應用的可信任程度。
A 想要跟 B 打賭,A 使用工廠合約 BetFactory.sol 部署一個 Bet.sol 合約,寫上要賭的內容,寫上打賭對象 (B 的地址),決定一個裁判的地址,決定下注 1 USDC,合約部署後,合約初始狀態為 Pending。
最後,裁判判定贏家出爐:
這裡放一個內容較完整的賭注,打賭內容大概是:
如果在太平洋時間 2024 年 11 月 5 日午夜(即 2024 年選舉日)之前,wannabet.cc 上的結算賭注超過 250 筆,則該賭注結果為「是」(0x...f812 勝出);否則,該賭注結果為「否」(0x...EF14 勝出)。
這部分我覺得挺有趣,特別挑出來講。
上述提到,A 想要跟 B 打賭,A 使用工廠合約 BetFactory.sol 部署一個 Bet.sol 合約。
如果 A 懂一點智能合約,它可以稍微改一下 Bet.sol,然後直接部署該合約,也能在鏈上跟 B 對賭,不必透過工廠合約。
工廠合約是由 wannatbet 的設計者所部署,讓用戶呼叫工廠合約來部署打賭合約,這可能在整體程式設計上會一定的好處,但開發者藉機設計了收小費的功能。
當 A 使用 BetFactory 部署 Bet.sol 的時候,以及當 B 呼叫 acceptBet 的時候,送出的交易的 value 需要滿足小費的金額,交易才會成功。
也就是說,A 和 B 在把 1 USDC 投入合約託管時,要塞一點 ETH 小費給 wannatbet 的擁有者,才能成功執行交易。
function createBet(
address _participant,
uint256 _amount,
address _token,
string memory _message,
address _judge,
uint256 _validFor
) public payable nonReentrant {
...
// Interactions: Token transfer
IERC20(_token).safeTransferFrom(
msg.sender,
address(newBet),
_amount
);
// Interactions: Send ETH fee
(bool feeSuccess, ) = payable(owner()).call{value: msg.value}("");
if (!feeSuccess) revert BET__FailedEthTransfer();
} catch {
revert("Deployment or token transfer failed");
}
function acceptBet() public payable onlyParticipant nonReentrant {
// Checks
if (msg.value < _BET_FACTORY.fee()) revert BET__FeeNotEnough();
if (_isExpired()) revert BET__Expired();
if (_status != Status.Pending) revert BET__InvalidStatus();
// Update state
_status = Status.Accepted;
// Emit event
emit BetAccepted();
// Interactions: Token transfer
_TOKEN.safeTransferFrom(msg.sender, address(this), _AMOUNT);
// Interactions: Send ETH fee
(bool feeSuccess, ) = payable(_BET_FACTORY.owner()).call{
value: msg.value
}("");
if (!feeSuccess) revert BET__FailedEthTransfer();
}