Смарт-контракт для airdrop events.
Скажем некое сообщество X планирует сделать раздачу eth для своих подписчиков. Чтобы каждый из них в течении месяца после определенных дат смог забрать свой дроп равный сумме, которую владелец паблика внесет на депозит (смарт-контракта) xBank, поделенное на количество участников события.
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17;
contract AirdropEvent {
uint8 public totalEligible; /* всего eligible участников (переменная
uint8 говорит о том, что возможно до 255 человек*/
uint256 public xBank; /* внесенная общая сумма средств (владельцем), доступная
для распределения (может отличаться от текущего баланса контракта)*/
address public owner; // адрес владельца контракта (владельца сообщества тг)Cтруктура для хранения данных о каждом участнике, включая их username в telegram, дату и время разблокировки eth и информацию о том, получили ли они уже средства.
struct EligibleMember {
string userNameTelegram;
uint256 unlocking;
bool alreadyGotMoney;
bool exist;
} address[] public arrEligibleMembers; /* массив для хранения адресов участников,
имеющих право на получение токенов.*/
mapping(address => EligibleMember) public eligibleMembers;
/* использует для хранения информации об участниках,
имеющих право на получение токенов, на основе их адреса кошелька.*/ constructor(){
owner = msg.sender;
totalEligible = 0;
} function addEligibleMember( /* функция используется владельцем контракта
для добавления участников, имеющих право на получение дропа.*/
address walletAddress, // адрес кошелька участника
string memory userNameTelegram, // username в телеграме
uint256 unlocking // дата и время разблокировки в Unix Timestamp https://www.unixtimestamp.com/
) public onlyOwner {
require(unlocking > 0, "The unlock date has not arrived");
require( /* проверка что дата разблокировки больше нуля
и что пользователя еще нет в списке участников.
Если обе проверки пройдены успешно, участник добавляется в
список и общее количество увеличивается*/
eligibleMembers[walletAddress].exist == false,
"Error! There is such a user"
);
eligibleMembers[walletAddress] = (
EligibleMember(userNameTelegram, unlocking, false, true)
);
arrEligibleMembers.push(walletAddress);
totalEligible++;
emit NewEligibleMember(walletAddress, userNameTelegram, unlocking);
} /*генерируется событие "NewEligibleMember"о добавлении нового участника.
Модификатор "onlyOwner" гарантирует, что только владелец контракта
может вызвать эту функцию. */Функция для чтения определенного диапазона участников из массива.
function readEligibleMembersArray(uint cursor, uint length) public view returns (address[] memory) {
address[] memory array = new address[](length);
uint totalEligible2 = 0;
for (uint i = cursor; i < cursor+length; i++) {
array[totalEligible2] = arrEligibleMembers[i];
totalEligible2++;
}
return array;
}Функция claim эфира для участников, имеющих право на получение токенов после разблокировки.
function claim() public {
address payable walletAddress = payable(msg.sender); require(
eligibleMembers[walletAddress].exist == true,
"No such participant!"
); /* Проверка - существует ли участник с данным кошельком.
Если параметр exist (существует) равен false,
то будет выведено сообщение "Нет такого участника!"
и выполнение функции будет остановлено.*/
require(
block.timestamp > eligibleMembers[walletAddress].unlocking,
"Unlocking hasn't happened yet!"
); /* Проверяет произошло ли уже разблокирование для данного участника.
Если текущее время (block.timestamp) не наступило, то будет выведено сообщение
"Разблокирование ещё не произошло!" и выполнение функции будет остановлено.*/
require(
eligibleMembers[walletAddress].alreadyGotMoney == false,
"You have already received eth!"
); /* Получил ли участник уже эфир. Если параметр
alreadyGotMoney (уже получил) для участника с заданным кошельком
равен true, то будет выведено сообщение "Вы уже получили эфир!"
и выполнение функции будет остановлено.
Таким образом, эти три проверки обеспечивают, что участник имеет право
получить свой дроп, иначе функция вызовет ошибку
и завершит своё выполнение.*/ uint256 amount = xBank / totalEligible;
eligibleMembers[walletAddress].alreadyGotMoney = true;
(bool success, ) = walletAddress.call{value: amount}("");
require(success);
emit GotMoney(walletAddress);
} Проверка текущего баланса контракта.function currentBalance() public view returns (uint256) {
return address(this).balance;
} modifier onlyOwner() { /* модификатор для ограничения доступа к определенным функциям
(только для владельца контракта)*/
require(msg.sender == owner, "You are not the owner");
_;
} receive() external payable { /*"запасная" функция для приема и сохранения поступающего эфира в xBank
для последующего распределения.*/
xBank += msg.value;
} //события
event NewEligibleMember(address indexed walletAddress, string userNameTelegram, uint256 unlocking);
event GotMoney(address indexed walletAddress);
}Конструкция event в Solidity используется для объявления событий, которые могут быть инициированы в ходе выполнения функций в смарт-контракте. 1. event NewEligibleMember(address indexed walletAddress, string userNameTelegram, uint256 unlocking);
* Это событие NewEligibleMember инициируется при добавлении нового участника в список.
* Он принимает три параметра: адрес кошелька, имя пользователя в Telegram и дату разблокировки.
* Ключевое слово indexed используется для обеспечения возможности эффективного поиска и фильтрации событий по указанному параметру.
2. event GotMoney(address indexed walletAddress);
* Это событие GotMoney, которое будет вызвано в случае, если участник получит деньги.
* Он принимает один параметр - адрес кошелька.
* Также используется ключевое слово indexed для этого параметра.
Эти события могут быть подписаны во внешних приложениях или интерфейсах, чтобы отслеживать и реагировать на изменения в смарт-контракте.
[1] Компиляция, деплой и функционал.
Видим оранжевые и синие кнопки, через addEligibleMember можем добавить данные подходящих участников: адрес кошелька, telegram username и дату unlocking например 18.12.2023 17:21:01 преобразованную в Unix Timestamp 1702909261 т.к. все вносится в ручную количество участников небольшое до 255.
[2] Добавим 3х человек с наступившей датой и 2х с разлоком на 25.12.2023
username1
0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
18.12.2023 17:21:011702909261
______________________________________
username2
0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db
______________________________________
username3
0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB
_______________________________________
username4
0x617F2E2fD72FD9D5503197092aC168c91465E7f2
25.12.2023 01.11.111703455871
________________________________________
username5
0x17F6AD8Ef982297579C203069C1DbfFE4348c372
*Функция для чтения определенного диапазона участников из массива.
Username1 успешно заклеймил 0,2 eth, на балансе контракта осталось 0,8. username4 и username5 смогут это сделать (25.12.)
[4] Вывод: Смарт-контракт предоставляет базовую структуру для проведения аирдропа среди подписчиков (также возможно адаптировать под различные активности и мероприятия, добавлять больше данных о пользователях / ранжировать на группы по анлоку), где участники могут получить свои токены после определенной даты разблокировки.