[00] — Hợp đồng thông minh Onetappy / Sepolia(Testnet) / v0.1

Kiến trúc hợp đồng
Công bằng có thể kiểm chứng

Mã nguồn mở hoàn toàn · Có thể kiểm chứng trên chuỗi
Giữ ký quỹ, ràng buộc cam kết, sinh số ngẫu nhiên, phân phối quỹ — tất cả đều được hợp đồng thông minh thực thi tự động, không cần trung gian đáng tin cậy.

Địa chỉ hợp đồng

0x0717DEB12839FE8D0DA8c6869E66d658ECc0b240 Etherscan ↗

[01] — Kiến trúc mô-đun

Treasury.partnerDeposit()

Mô-đun ký quỹ

01 Đối tác nộp đủ số tiền (giá vé × số lượng) khi tạo trò chơi
02 Khoản ký quỹ được hợp đồng khóa theo cách nguyên tử; không ai có thể rút sớm
03 Tự động hoàn trả sau khi quyết toán hoặc bị cắt như hình phạt
04 Số tiền ký quỹ đủ để bao trùm lợi ích tiềm năng từ thao túng, loại bỏ động cơ kinh tế
SessionConfig.sessionCommitment

Mô-đun cam kết

01 Commit của đối tác = keccak256(revealData, salt)
02 Người chơi có một hành động nguyên tử duy nhất: tiền vé + số ngẫu nhiên được ghi đồng thời
03 Người chơi không cần thực hiện bước Reveal (Player One Tappy)
04 Reveal của đối tác kích hoạt việc tính toán số ngẫu nhiên cuối cùng
Session.reveal(revealData, salt)

Mô-đun quyết toán

01 Trộn lặp toàn bộ số ngẫu nhiên của người chơi và dữ liệu khối trên chuỗi thông qua keccak256
02 Kết hợp seed của đối tác để tạo ra số ngẫu nhiên cuối cùng không thể đảo ngược
03 Người thắng được xác định trên chuỗi; bất kỳ ai cũng có thể tự kiểm chứng kết quả
04 Tự động phân phối: phần thưởng người thắng + doanh thu đối tác + hoàn trả ký quỹ
Deposit
Commit
Reveal

[02] — Các hàm cốt lõi

createSession(SessionConfig)

Đối tác tạo một trò chơi mua chung. Khóa toàn bộ khoản ký quỹ (giá vé × số lượng) và đồng thời gửi một cam kết băm — cả hai được ràng buộc nguyên tử và không thể tách rời.

← Khóa toàn bộ ký quỹ
← Cam kết và ký quỹ được ghi nguyên tử
← Tham số trò chơi là bất biến
OntappyFactory.sol
function createSession(SessionConfig memory partialConfig) returns (address)  {
    if (partialConfig.sessionCommitment == bytes32(0)) revert MissingCommitment();
    uint16 totalFee = partialConfig.partnerShareBps + platformFeeBps;
    if (totalFee > BPS_DENOMINATOR) revert FeeTooHigh(totalFee);

    uint256 requiredDeposit = partialConfig.ticketPrice * partialConfig.totalTickets;
    uint256 currentBalance = treasury.balances(msg.sender);
    if (currentBalance < requiredDeposit) {
        revert InsufficientDeposit(currentBalance, requiredDeposit);
    }

    partialConfig.admin = admin;
    partialConfig.creator = msg.sender;
    partialConfig.treasury = payable(address(treasury));
    partialConfig.platformFeeBps = platformFeeBps;
    partialConfig.creatorAbsentPartnerDepositSlashBps = creatorAbsentPartnerDepositSlashBps;

    CommitRevealSession session = new CommitRevealSession(partialConfig);

    treasury.registerSession(address(session), msg.sender);
    treasury.lockPartnerDeposit(address(session), msg.sender, requiredDeposit);

    emit SessionCreated(msg.sender, address(session), partialConfig);

    return address(session);
}

playerBuyAndCommitTicket()

Người chơi tham gia trò chơi. Một hành động nguyên tử duy nhất sẽ đồng thời gửi tiền vé và số ngẫu nhiên cá nhân. Không cần bước Reveal thứ hai, loại bỏ hoàn toàn cửa sổ tấn công hậu thủ.

← Tiền vé + số ngẫu nhiên được gửi nguyên tử
← Người chơi không cần Reveal
← Được ghi vào nguồn entropy trên chuỗi
OnetappySession.sol
function playerBuyAndCommitTicket(uint256 quantity, bytes32 secret, bool useBalance) external payable {
    uint256 endTimestamp = unlockTimestamp + commitDurationSeconds;
    if (block.timestamp < unlockTimestamp) {
        revert TimeConstraintError(block.timestamp, unlockTimestamp);
    }
    if (block.timestamp > endTimestamp) {
        revert TimeConstraintError(block.timestamp, endTimestamp);
    }
    if (quantity == 0) revert InvalidZeroInput();
    if (nextTicketIndex + quantity > totalTickets) revert SoldOut(nextTicketIndex, quantity);

    uint256 cost = ticketPrice * quantity;
    
    if (useBalance) {
        uint256 treasuryBalance = treasury.balances(msg.sender);
        if (treasuryBalance < cost) revert IncorrectETHAmount(cost, treasuryBalance);
        treasury.playerPayTicketUseBalance(msg.sender, cost);
    } else {
        if (msg.value != cost) revert IncorrectETHAmount(cost, msg.value);
        treasury.playerPayTicket{value: msg.value}(msg.sender);
    }

    for (uint256 i = 0; i < quantity; i++) {
        ticketToPlayer[nextTicketIndex + i] = msg.sender;
    }
    nextTicketIndex += quantity;
    ticketCounts[msg.sender] += quantity;

    playerCommitment ^= secret ^ blockhash(block.number - 1);

    emit TicketsPurchased(msg.sender, quantity, nextTicketIndex);
}

reveal()

Đối tác reveal seed gốc, kích hoạt việc trộn số ngẫu nhiên và quyết toán cuối cùng. Nếu không reveal trong thời hạn, bất kỳ ai cũng có thể gọi hàm phạt để cắt khoản ký quỹ.

← Xác minh keccak256(revealData) == commitHash
← Kích hoạt tính toán trộn đa entropy
← Tự động phạt nếu quá thời gian mà không reveal
OnetappySession.sol
function reveal(bytes calldata revealData, bytes32 salt) external onlyCreator {
    bytes32 calculatedResult = keccak256(abi.encodePacked(revealData, salt));
    if (calculatedResult != sessionCommitment) revert InvalidReveal(sessionCommitment, calculatedResult);

    _settle(SettlementType.Normal);

    bytes32 combinedResult = keccak256(abi.encodePacked(revealData, playerCommitment));
    uint256 winnerIndex = uint256(combinedResult) % totalTickets;

    uint256 totalAmount = ticketPrice * totalTickets;
    uint256 partnerShare = (totalAmount * partnerShareBps) / BPS_DENOMINATOR;
    uint256 platformFee = (totalAmount * platformFeeBps) / BPS_DENOMINATOR;
    uint256 rewardAmount = totalAmount - partnerShare - platformFee;

    address[] memory users = new address[](3);
    users[0] = creator;
    users[1] = admin;
    users[2] = ticketToPlayer[winnerIndex];
    uint256[] memory amounts = new uint256[](3);
    amounts[0] = partnerShare;
    amounts[1] = platformFee;
    amounts[2] = rewardAmount;

    treasury.distributeFundsBatch(users, amounts);

    treasury.unlockPartnerDeposit(ticketPrice * totalTickets);

    emit WinnerSelected(ticketToPlayer[winnerIndex], winnerIndex);
    emit SessionSettled(SettlementType.Normal);
}

[03] — Phân tích bảo mật

# Vector đe dọa Trạng thái Cơ chế phòng vệ
01 Second-Mover Attack Mitigated Nếu đối tác không Reveal trong thời hạn, hợp đồng sẽ tự động kích hoạt hình phạt, cắt toàn bộ khoản ký quỹ và phân phối cho những người chơi bị ảnh hưởng. Lợi nhuận kỳ vọng của cuộc tấn công E=G−D≤0
02 Random Number Manipulation Mitigated Số ngẫu nhiên cuối cùng được tạo bằng cách trộn lặp seed của đối tác + dữ liệu khối + N số ngẫu nhiên của người chơi thông qua keccak256; không một bên đơn lẻ nào có thể tự mình kiểm soát kết quả
03 Miner / Validator Block Manipulation Mitigated blockhash của nhiều khối độc lập được dùng như một nguồn entropy; chi phí để miner chọn lọc bỏ khối cao hơn bất kỳ lợi ích thao túng tiềm năng nào
04 Partner Refuses to Reveal Mitigated createSession() bắt buộc msg.value == ticketPrice × ticketCount, bảo đảm khoản ký quỹ D ≥ lợi ích thao túng tối đa G
05 Sybil Attack Mitigated mapping trong playerBuyAndCommitTicket ngăn cùng một địa chỉ mua vé nhiều hơn một lần
06 Reentrancy Attack Mitigated Quyết toán sử dụng mẫu CEI (ghi trạng thái trước khi chuyển) + ReentrancyGuard để ngăn reentrancy
07 Integer Overflow Mitigated Solidity ^0.8 có kiểm tra tràn số tích hợp áp dụng toàn cục; không cần SafeMath

[04] — Tài nguyên

Tài liệu ABI & giao diện

Hỗ trợ đầy đủ kiểu TypeScript

Xem tài liệu
Onetappy · Hợp đồng thông minh · v0.1