English Русский فارسی العربية Türkçe Español

QuickLotteryTON

Contrato inteligente para una lotería aleatoria simple en la blockchain TON

Dirección del contrato: EQCsihC1Z6fHRsFbz9N3sCHC6sUfvnlz4RbelethC-6Q34y6 ¡Copiado!

Tonviewer | Verifier

Resumen

QuickLotteryTON es un contrato inteligente simple en la blockchain TON. Los participantes ingresan a la lotería mediante la llamada enter, enviando 1 o más TON (cantidades exactas, sin fracciones).

Una vez que 10+ participantes se hayan unido y hayan pasado 24 horas, cualquier participante puede ejecutar la llamada draw. Se selecciona aleatoriamente ~10% de los participantes y se distribuye el fondo entre ellos.

¡Eso es todo!

Puedes ver el código fuente abajo (ir al código).

Cómo participar

Para ingresar a la lotería, simplemente envía 1 TON (o 2, 3, 4, etc.) a la dirección del contrato EQCsihC1Z6fHRsFbz9N3sCHC6sUfvnlz4RbelethC-6Q34y6 ¡Copiado! con el comentario enter (usa exactamente esa palabra, sin espacios y en minúsculas).

Respuestas

Si envías un valor con decimales (ej. 1.002 TON), recibirás tu dinero de vuelta con el mensaje Can only enter with 1 TON or multiples of 1 TON.

Si ya has participado en esta ronda, recibirás tu dinero de vuelta con el mensaje You have already entered this draw.

Si tu entrada es correcta, recibirás el mensaje Successfully entered the lottery draw with 1 TON!

Sorteo

Sólo un participante necesita ejecutar el sorteo por ronda. Normalmente no hace falta, otro lo hará automáticamente. Sólo entra y espera los resultados en 24 horas.

Si quieres hacer el sorteo, envía 0.05 TON o más (según el número de participantes, suficiente para el gas) a la dirección del contrato EQCsihC1Z6fHRsFbz9N3sCHC6sUfvnlz4RbelethC-6Q34y6 ¡Copiado! con el comentario exacto draw.

Si el sorteo anterior fue hace menos de 24 horas, recibirás tu dinero con el mensaje Cannot draw yet, X seconds remaining until next draw.

Si han entrado menos de 10 personas en esta ronda, recibirás tu dinero con el mensaje Not enough participants for draw.

En otro caso, el código seleccionará aleatoriamente ~10% de los participantes y repartirá el fondo entre ellos. 1% se retiene como comisión de gas.

Los ganadores recibirán su premio con el mensaje Congratulations! You won 10000000000 nanoTON in the lottery draw!

Métodos

En el enlace de Verifier o TonViewer también puedes llamar a los métodos getter del contrato. Los métodos disponibles son:

Los resultados están en formato hex, debes convertirlos a decimal.

Código fuente

Este código fuente también se puede verificar en TON Verifier:

  1. /**
  2. QuickLotteryTON contract
  3. Explorers:
  4. - Tonviewer: https://tonviewer.com/EQCsihC1Z6fHRsFbz9N3sCHC6sUfvnlz4RbelethC-6Q34y6
  5. - Verifier: https://verifier.ton.org/EQCsihC1Z6fHRsFbz9N3sCHC6sUfvnlz4RbelethC-6Q34y6
  6. Usage:
  7. - Enter lottery:
  8. • Send ≥ 1 TON (multiples of 1 TON) with comment "enter".
  9. • Each address may enter once per draw.
  10. - Getters:
  11. • balance() → current contract balance (nanoTON).
  12. • betPool() → total TON in the current lottery pool (nanoTON).
  13. • participantCount() → number of unique participants.
  14. - Draw:
  15. • Anyone can trigger via sending any amount of TON with comment "draw".
  16. • At most once every 24h.
  17. • Requires ≥ 10 participants before allowing draw.
  18. - Payout:
  19. • 1% fee to deployer for transaction fees.
  20. • Remaining pool distributed proportionally among ~10% of participants, chosen randomly.
  21. */
  22. import "@stdlib/deploy";
  23. const NANO_COUNT: Int = 1_000_000_000;
  24. message TransferEvent {
  25. amount: Int as int64;
  26. recipient: Address;
  27. }
  28. message EntryEvent {
  29. sender: Address;
  30. amount: Int as int64;
  31. }
  32. contract QuickLotteryTON with Deployable {
  33. const DRAW_EVERY: Int = 24 * 60 * 60; // Once every day at most.
  34. const MIN_PARTICIPANTS: Int = 10;
  35. lastDrawTime: Int;
  36. participants: map<Address, Int>;
  37. participantCount: Int;
  38. betPool: Int;
  39. deployer: Address;
  40. init() {
  41. nativeReserve(ton("1.0"), ReserveAtMost | ReserveBounceIfActionFail);
  42. self.deployer = sender();
  43. self.participantCount = 0;
  44. self.betPool = 0;
  45. self.participants = emptyMap();
  46. self.lastDrawTime = now();
  47. }
  48. receive("enter") {
  49. let amount: Int = context().value;
  50. if (amount < NANO_COUNT || amount % NANO_COUNT != 0) {
  51. self.reply("Can only enter with 1 TON or multiples of 1 TON.".asComment());
  52. return;
  53. }
  54. let sender: Address = sender();
  55. if (self.participants.exists(sender)) {
  56. self.reply("You have already entered this draw.".asComment());
  57. return;
  58. }
  59. self.participants.set(sender, amount);
  60. self.participantCount += 1;
  61. self.betPool += amount;
  62. // Emit entry event
  63. emit(EntryEvent{sender: sender, amount: amount}.toCell());
  64. // Send confirmation message to participant with bet amount
  65. let sb: StringBuilder = beginString();
  66. sb.append("Successfully entered the lottery draw with ");
  67. sb.append((amount / NANO_COUNT).toString());
  68. sb.append(" TON!");
  69. send(SendParameters{
  70. to: sender,
  71. bounce: false,
  72. value: 0, // No TON refunded
  73. mode: SendIgnoreErrors | SendPayFwdFeesSeparately,
  74. body: sb.toString().asComment()
  75. });
  76. }
  77. receive("draw") {
  78. // Check time since last draw
  79. let remaining: Int = self.DRAW_EVERY - (now() - self.lastDrawTime);
  80. if (remaining > 0) {
  81. let sb: StringBuilder = beginString();
  82. sb.append("Cannot draw yet, ");
  83. sb.append(remaining.toString());
  84. sb.append(" seconds remaining until next draw.");
  85. self.reply(sb.toString().asComment());
  86. return;
  87. }
  88. if (self.participantCount < self.MIN_PARTICIPANTS) {
  89. self.reply("Not enough participants for draw.".asComment());
  90. return;
  91. }
  92. // Send 1% fee to owner
  93. let ownerFee: Int = self.betPool / 100;
  94. if (ownerFee > 0) {
  95. emit(TransferEvent{amount: ownerFee, recipient: self.deployer}.toCell());
  96. send(SendParameters{
  97. to: self.deployer,
  98. bounce: true,
  99. value: ownerFee,
  100. mode: SendIgnoreErrors
  101. });
  102. }
  103. // Determine prize pool and winner count
  104. let pool: Int = self.betPool - ownerFee;
  105. let targetWinners: Int = self.participantCount / 10; // 10% winners
  106. // Select winners
  107. let winners: map<Address, Int> = emptyMap();
  108. let winnerCount: Int = 0;
  109. let totalWinnerWeight: Int = 0;
  110. // First pass: try to select winners with 10% chance
  111. while (winnerCount == 0) {
  112. foreach (adr, winnerWeight in self.participants) {
  113. if (random(1, 100) <= 10 && winnerCount < targetWinners) {
  114. winners.set(adr, winnerWeight);
  115. totalWinnerWeight += winnerWeight;
  116. winnerCount += 1;
  117. }
  118. }
  119. }
  120. // Distribute prizes proportionally to bet amounts
  121. foreach (winnerAddress, winnerWeight in winners) {
  122. let prize: Int = pool * winnerWeight / totalWinnerWeight;
  123. if (prize > 0) {
  124. // Create notification message with prize amount
  125. let sb: StringBuilder = beginString();
  126. sb.append("Congratulations! You won ");
  127. sb.append((prize).toString());
  128. sb.append(" nanoTON in the lottery draw!");
  129. // Emit transfer event
  130. emit(TransferEvent{amount: prize, recipient: winnerAddress}.toCell());
  131. // Send prize with notification message in a single transaction
  132. send(SendParameters{
  133. to: winnerAddress,
  134. bounce: true,
  135. value: prize,
  136. mode: SendIgnoreErrors,
  137. body: sb.toString().asComment()
  138. });
  139. }
  140. }
  141. // Reset lottery
  142. self.participantCount = 0;
  143. self.betPool = 0;
  144. self.participants = emptyMap();
  145. self.lastDrawTime = now();
  146. }
  147. get fun participantCount(): Int {
  148. return self.participantCount;
  149. }
  150. get fun betPool(): Int {
  151. return self.betPool;
  152. }
  153. get fun balance(): Int {
  154. return myBalance();
  155. }
  156. }