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

QuickLotteryTON

Смарт-контракт для простой случайной лотереи на блокчейне TON

Адрес контракта: EQCsihC1Z6fHRsFbz9N3sCHC6sUfvnlz4RbelethC-6Q34y6 Скопировано!

Tonviewer | Verifier

Описание

QuickLotteryTON — это простой смарт-контракт в блокчейне TON. Участники входят в лотерею с помощью вызова enter, отправляя 1 или более TON (точные суммы, без дробных значений).

После того как 10+ участников войдут и пройдет 24 часа, любой участник может вызвать draw. Контракт случайным образом выбирает около 10% участников и распределяет между ними пул.

Вот и всё!

Исходный код см. ниже (перейти к исходнику).

Как участвовать

Чтобы войти в лотерею, просто отправьте 1 TON (или 2, 3, 4 и т.д.) на адрес контракта EQCsihC1Z6fHRsFbz9N3sCHC6sUfvnlz4RbelethC-6Q34y6 Скопировано! с комментарием enter (точно это слово, без пробелов и в нижнем регистре).

Ответы

Если вы отправите сумму с дробями (например, 1.002 TON), деньги вернутся с сообщением Can only enter with 1 TON or multiples of 1 TON.

Если вы уже участвовали в этом раунде, деньги вернутся с сообщением You have already entered this draw.

Если вы успешно вошли в розыгрыш, получите сообщение Successfully entered the lottery draw with 1 TON!

Розыгрыш

Только один участник должен запускать розыгрыш в каждом раунде. Обычно это не требуется — кто-то другой сделает это автоматически. Вам нужно лишь войти и ждать результатов через 24 часа!

Если вы хотите провести розыгрыш, отправьте 0.05 TON или больше (в зависимости от количества участников, достаточно для газа) на адрес контракта EQCsihC1Z6fHRsFbz9N3sCHC6sUfvnlz4RbelethC-6Q34y6 Скопировано! с комментарием draw.

Если предыдущий розыгрыш был менее 24 часов назад, деньги вернутся с сообщением Cannot draw yet, X seconds remaining until next draw.

Если в этом раунде меньше 10 участников, деньги вернутся с сообщением Not enough participants for draw.

Иначе код случайным образом выберет около 10% участников и распределит пул. 1% удерживается как комиссия за газ.

Победители получают средства и сообщение Congratulations! You won 10000000000 nanoTON in the lottery draw!

Методы

На Verifier или TonViewer вы также можете вызвать методы-геттеры контракта. Доступные методы:

Результаты в hex, требуется конвертация в десятичное значение.

Исходный код

Исходный код также можно проверить через 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. }