Sui.

Publicación

Comparte tu conocimiento.

article banner.
harry phan.
Apr 10, 2025
Artículo

Creación de una dApp de lotería NFT de próxima generación con Sui Move y una interfaz de usuario moderna

🧩 Creación de una dApp de lotería NFT de próxima generación con Sui Move y una interfaz de usuario moderna

Esta es tu guía definitiva para crear una dApp de lotería gamificada e impulsada por NFT utilizandoSui Move, con soporte para múltiples rondas, sistemas de recomendación, gobernanza de DAO y un sistema de diseño que encantará a la generación Z. Desde la arquitectura de contratos hasta el flujo de la interfaz de usuario, hagamos todo lo posible.


📦 Desglose de fases

Fase 1: Lotería básica

  • Juego de varias rondas
  • Venta de entradas en NFT
  • Sistema de recompensas por recomendación
  • Votación DAO básica

Fase 2: Mercado y gamificación

  • Integración con el mercado de NFT
  • Potenciadores (aumentan las posibilidades de ganar)
  • Sistema de premios
  • Lanzamientos aéreos ocultos

Fase 3: DAO y multicadena

  • Compatibilidad entre cadenas
  • DAO con propuestas avanzadas
  • Precios dinámicos
  • Análisis en cadena

🧠 Smart Contract profundiza en Sui Move

Estructura del contrato

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
    }
}

Conclusiones clave:

  • Balance<FAUCET_COIN>garantiza la seguridad tipográfica y el manejo adecuado de las monedas
  • Option<u32>indica claramente si se ha elegido un ganador
  • ✅ Los eventos ofrecen trazabilidad para usuarios finales y exploradores

🛠 Comandos de CLI Sui

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

Para comprar un boleto, determinar el ganador o reclamar una recompensa, siga flujos de CLI similares.


🔮 Futuras incorporaciones

  • Restablecimiento automático de la lógica para la próxima ronda claim_reward
  • Emite más eventos como ReferralRewardDistributed
  • Refactoriza los premios mayores y las referencias en submódulos

¡Avísame si quieres una parte 2 para crear una interfaz de usuario e integrarla en la red de pruebas de Sui!

  • Sui
5
Cuota
Comentarios
.
0xduckmove.
Apr 30 2025, 02:48

¡Aquí se encuentra una gran energía de juego de NFT de próxima generación! <FAUCET_COIN>🚀 Me encanta cómo superas los límites de lo que es posible con Sui Move: arquitectura modular, flujo de eventos limpio y ese Balance safety flex 💪. El despliegue en varias fases (desde la lotería básica hasta la gobernanza de la DAO y la visión entre cadenas) también demuestra que se piensa en el producto real, no solo en el código por el bien del código. ¿Y ese toque de interfaz de usuario de la generación Z? Di menos 👀. Este es el tipo de DApp al que realmente jugaría. ¡Me muero por ver esos airdrops y propulsores en acción!