Sui.

Пост

Поделитесь своими знаниями.

article banner.
harry phan.
Apr 10, 2025
Статья

Создание DApp нового поколения для лотерей NFT с помощью Sui Move и современного пользовательского интерфейса

🧩 Создание лотерейного приложения NFT нового поколения с помощью Sui Move и современного пользовательского интерфейса

Это ваше полное руководство по созданию геймифицированного децентрализованного лотерейного приложения на базе NFT с использованиемSui Moveс поддержкой нескольких раундов, реферальными системами, управлением DAO и системой проектирования, которая понравится поколению Z. От архитектуры контрактов до управления пользовательским интерфейсом — давайте сделаем все возможное.


📦 Разбивка фаз

Фаза 1 — Базовая лотерея

  • Многораундовый геймплей
  • Продажа билетов NFT
  • Система реферальных вознаграждений
  • Базовое голосование в DAO

Фаза 2 — Маркетплейс и геймификация

  • Интеграция с торговой площадкой NFT
  • Бустеры (увеличивают шанс выигрыша)
  • Система джекпотов
  • Скрытые раздачи

Фаза 3 — DAO и мультицепочка

  • Межсетевая совместимость
  • DAO с расширенными предложениями
  • Динамическое ценообразование
  • Ончейн-аналитика

🧠 Смарт-контракт: подробное описание Sui Move

Структура контракта

module nft_lottery_x::nft_lottery_x {
    use sui::object;
    use sui::balance::{Balance, zero};
    use sui::coin::{Self, Coin};
    use sui::clock::Clock;
    use sui::random::Random;
    use sui::event::emit;
    use sui::transfer;
    use sui::tx_context::TxContext;
    use std::option;
    use std::signer;

    const EGameNotStarted: u64 = 1000;
    const EGameAlreadyFinished: u64 = 1001;
    const EInvalidPayment: u64 = 1002;
    const ENoTickets: u64 = 1003;
    const EWinnerAlreadyChosen: u64 = 1004;
    const ENotWinner: u64 = 1005;

    public struct Game has key {
        id: UID,
        ticket_price: u64,
        start_time: u64,
        end_time: u64,
        total_tickets: u32,
        round: u32,
        winner: Option<u32>,
        balance: Balance<FAUCET_COIN>,
        referral_bonus: u64,
    }

    public struct Ticket has key {
        id: UID,
        game_id: ID,
        ticket_number: u32,
        buyer: address,
        referrer: Option<address>,
    }

    public struct GameCreated has copy, drop {
        game_id: ID,
        start_time: u64,
        end_time: u64,
        ticket_price: u64,
    }

    public struct TicketBought has copy, drop {
        game_id: ID,
        ticket_number: u32,
        buyer: address,
        referrer: Option<address>,
    }

    public struct WinnerAnnounced has copy, drop {
        game_id: ID,
        winner_ticket: u32,
        round: u32,
    }

    public struct RewardClaimed has copy, drop {
        game_id: ID,
        ticket_number: u32,
        amount: u64,
    }

    public fun create_game(
        start_time: u64,
        end_time: u64,
        ticket_price: u64,
        referral_bonus: u64,
        ctx: &mut TxContext
    ) {
        let game = Game {
            id: object::new(ctx),
            ticket_price,
            start_time,
            end_time,
            total_tickets: 0,
            round: 1,
            winner: option::none(),
            balance: zero(),
            referral_bonus,
        };

        emit<GameCreated>(GameCreated {
            game_id: object::id(&game),
            start_time,
            end_time,
            ticket_price,
        });

        transfer::share_object(game);
    }

    public fun buy_ticket(
        game: &mut Game,
        coin: Coin<FAUCET_COIN>,
        clock: &Clock,
        referrer: Option<address>,
        ctx: &mut TxContext
    ): Ticket {
        assert!(clock.timestamp_ms() >= game.start_time, EGameNotStarted);
        assert!(clock.timestamp_ms() < game.end_time, EGameAlreadyFinished);
        assert!(coin.value() == game.ticket_price, EInvalidPayment);

        game.total_tickets = game.total_tickets + 1;
        coin::put(&mut game.balance, coin);

        let ticket = Ticket {
            id: object::new(ctx),
            game_id: object::id(game),
            ticket_number: game.total_tickets,
            buyer: signer::address_of(ctx.sender()),
            referrer,
        };

        emit<TicketBought>(TicketBought {
            game_id: object::id(game),
            ticket_number: ticket.ticket_number,
            buyer: ticket.buyer,
            referrer: ticket.referrer,
        });

        ticket
    }

    public entry fun determine_winner(
        game: &mut Game,
        rand: &Random,
        clock: &Clock,
        ctx: &mut TxContext
    ) {
        assert!(clock.timestamp_ms() >= game.end_time, EGameNotStarted);
        assert!(game.winner.is_none(), EWinnerAlreadyChosen);
        assert!(game.total_tickets > 0, ENoTickets);

        let mut generator = rand.new_generator(ctx);
        let winning_ticket = generator.generate_u32_in_range(1, game.total_tickets);
        game.winner = option::some(winning_ticket);

        emit<WinnerAnnounced>(WinnerAnnounced {
            game_id: object::id(game),
            winner_ticket: winning_ticket,
            round: game.round,
        });
    }

    public fun claim_reward(
        ticket: Ticket,
        game: Game,
        ctx: &mut TxContext
    ): Coin<FAUCET_COIN> {
        assert!(object::id(&game) == ticket.game_id, EInvalidPayment);
        let ticket_num = ticket.ticket_number;
        assert!(game.winner.contains(&ticket_num), ENotWinner);

        let amount = game.balance.value();
        let reward = game.balance.into_coin(ctx);

        emit<RewardClaimed>(RewardClaimed {
            game_id: object::id(&game),
            ticket_number: ticket.ticket_number,
            amount,
        });

        object::delete(object::id(&game));

        reward
    }
}

Ключевые выводы:

  • Balance<FAUCET_COIN>обеспечивает безопасность типов и надлежащее обращение с монетами
  • Option<u32>четко сигнализирует о том, выбран ли победитель
  • ✅ События позволяют пользователям и исследователям отслеживать события

🛠 Использование команд интерфейса командной строки

sui client call   --package <PACKAGE_ID>   --module nft_lottery_x   --function create_game   --args <START_TIME> <END_TIME> <TICKET_PRICE> <REFERRAL_BONUS>   --gas-budget 10000000

Чтобы купить билет, определить победителя или получить вознаграждение, воспользуйтесь аналогичными процедурами командной строки.


🔮 Будущие дополнения

  • Автоматический сброс логики следующего раунда claim_reward
  • Запускайте больше событий, таких как ReferralRewardDistributed
  • Реорганизуйте джекпоты и рефералов в подмодули

Дайте мне знать, хотите ли вы во второй части создать пользовательский интерфейс и интегрировать его в тестовую сеть Sui!

  • Sui
5
Поделиться
Комментарии
.
0xduckmove.
Apr 30 2025, 02:48

Это серьезная энергия гейм-фантастики NFT нового поколения прямо здесь! <FAUCET_COIN>🚀 Мне нравится, как вы расширяете границы возможного с помощью Sui Move — модульную архитектуру, четкую организацию мероприятий и систему Balance safety flex 💪. Многоэтапное развертывание (от основной лотереи до управления DAO и межсетевого видения) также отражает реальное мышление о продукте, а не просто программирование ради кода. А что такое сенсорный интерфейс поколения Z? Скажи меньше 👀. В такое приложение DApp я бы на самом деле поиграл. Не могу дождаться, когда увижу эти дропы и бустеры в действии!