Язык Solidity: Структуры (Урок 5)


Язык Solidity: Структуры (Урок 5)

Предыдущие уроки:

Язык Solidity: Неllo World (Урок 1)
Язык Solidity: Типы данных (Урок 2)
Язык Solidity: Переменные состояния контракта (Урок 3)
Язык Solidity: Массивы и соответствия (Урок 4)


Структура User

В языках программирования структуры подразумневают отдельные (комплексный) типы данных. Опять же возвращаясь к аналогии с нашим PHP и БД можно сравнить с описанием таблицы, а каждая переменная типа этой структуры - запись в этой таблице.
Так же как записи в таблицах могут ссылаться на записи в других таблицах по внешнему ключу, можно определить связи между структурами внутри контракта.

Как мы знаем - контракты довольно часто применяются для сбора средств (ICO).
Предлагаю как обычно начать с простых примеров дабы въехать в курс дела :)

Вообще любой программист должен воспринимать структуры без особых затруднений, так как большинство приложений вводят и выводят данные именно в виде данных структур, отформатированными по определенным правилам.

Первым примером будет такой контракт:

pragma solidity ^0.4.0;

contract MyStruct {
    struct User {
        string name;
        uint8 age;
        uint256 balance; // тип можно просто uint - псевдоним
    }

    // ID нового юзера
    uint lastUserId;
    // Создаем соответствие (массив), где будем держать юзеров
    mapping(uint => User) users;

    // И как обычно добавляем функции для взаимодействия
    // с контрактом

    // Добавление юзера
    function addUser(string name, uint8 age, uint256 balance) returns (uint thisId) {
        thisId = lastUserId++; // увеличиваем счетчик на 1
        users[thisId] = User(name, age, balance);
    }

}

Теперь в наш контракт можно добавлять пользователей. Например ввод:

{
    "string name": "Ivan I",
    "uint8 age": "34",
    "uint256 balance": "215"
}

То наш вывод функции (ID пользователя) будет такой:

{
    "uint256 thisId": "0"
}

здесь у нас не 1, потому что мы используем постинкремент: thisId = lastUserId++
в данном случае сначала выполняется присваивание, а затем переменная увеличивается на 1.

Если мы добавим нового пользователя, например:

{
    "string name": "Ivan I",
    "uint8 age": "34",
    "uint256 balance": "215"
}

то получим соответственно:

{
    "uint256 thisId": "1"
}

и так далее.

Работа с балансом пользователя (учебный пример - как в реальности надо разберем позже)

Для работы с балансом, пишем 3 функции: проверка, зачисление и снятие.
Понятное дело, поскольку мы еще не обсуждали контроль доступа, в реальности так никто не делает (поскольку любой может изменять эти данные). Но нам важно понять принцип.

Просмотр текущего баланса

// Просмотр баланса
    function getUserBalanceById(uint userId) returns (uint) {
        // Проверка корректности ID
        if(userId < 0 && userId > lastUserId) return 0;
        // Возвращаем баланс
        return users[userId].balance;
    }

Тут первая строчка проверяет, что такой аккаунт есть в хранилище данных контракта.
Вторая возвращает его. Так как мы используем массив структур - мы передаем в качестве индекса id пользователя, а для извлечения баланса используем точечный синтаксис.

Пополнение баланса

// Пополнение баланса
    function addUserAmount(uint userId, uint amount) {
        if(userId > 0 && userId <= lastUserId && amount > 0) {
            users[userId].balance += amount;
        }
    }

Теперь например, если мы добавим пользователя с id=1 и балансом 500 токенов, и вызовем эту функцию с параметрами:

{
    "uint256 userId": "1",
    "uint256 amount": "30"
}

то getUserBalanceById(1) вернет 530.

Снятие токенов с баланса

Аналогичным образом токены снимаются

// Это совсем неправильная функция 
    // Просто вычитает c баланса :)
    function daj(uint userId, uint many) {
        if(userId > 0 && userId <= lastUserId && many > 0) {
            users[userId].balance -= many;
            // TODO send
        }
    }

При транзакции с вызовом этой функции баланс пользователя аналогичным как и при пополнении образом уменьшится на значение many.

Полный код контракта MyStruct

pragma solidity ^0.4.0;

contract MyStruct {
    struct User {
        string name;
        uint8 age;
        uint256 balance; // тип можно просто uint - псевдоним
    }

    // ID нового юзера
    uint lastUserId;
    // Создаем соответствие (массив), где будем держать юзеров
    mapping(uint => User) users;

    // И как обычно добавляем функции для взаимодействия
    // с контрактом

    // Добавление юзера
    function addUser(string name, uint8 age, uint256 balance) returns (uint thisId) {
        thisId = lastUserId++; // увеличиваем счетчик на 1
        users[thisId] = User(name, age, balance);
    }

    // Просмотр баланса
    function getUserBalanceById(uint userId) returns (uint) {
        // Проверка корректности ID
        if(userId < 0 && userId > lastUserId) return 0;
        // Возвращаем баланс
        return users[userId].balance;
    }

    // Пополнение баланса
    function addUserAmount(uint userId, uint amount) {
        if(userId > 0 && userId <= lastUserId && amount > 0) {
            users[userId].balance += amount;
        }
    }

    // Это совсем неправильная функция 
    // Просто вычитает c баланса :)
    function daj(uint userId, uint many) {
        if(userId > 0 && userId <= lastUserId && many > 0) {
            users[userId].balance -= many;
            // TODO send
        }
    }

}

В следующем уроке мы ознакомимся с системными переменными и функциями контрактов, которые позволят производить контроль доступа: получать информацию о блоке, транзакциях и адреса пользователей.


Comments 2