Binance Coin - разбираемся в коде смарт-контракта - HappyHodler.ru - обучающие статьи о биржах криптовалют, биткоине и трейдинге

Разбираем смарт-контракт Binance Coin

0
(0)

Binance

В этой статье рассмотрим код токена Binance Coin (BNB), принадлежащий популярной бирже Binance.

Биржа Binance создала в свое время токен Binance Coin, который со временем обрел большую популярность. Токен BNB создан в блокчейне Ethereum на Solidity версии 0.4.8. Токен соответствует стандарту ERC20, плюс имеет дополнительные функции: функции заморозки и разморозки токенов, функцию сжигания токенов, а также может хранить на своем адресе токены и эфиры и позволяет создателю выводить их. Состоит из двух контрактов-классов. Код токена можно посмотреть на Etherscan.io или ниже с комментариями.

Внимание! Информация в этой статье была валидна до тех пор, пока токен BNB существовал в блокчейне Ethereum. В настоящее время токен благополучно мигрировал в собственный блокчейн биржи Бинанс «Binance Chain» и живет только там, к Ethereum он больше никакого отношения не имеет.

 

Как торговать BNB (Binance Coin) смотри здесь

/**
 * Смарт-контракт токена Binance Coin (BNB)
 */

pragma solidity ^0.4.8;

/**
 * Безопасные математические операции
 */
 
	
contract SafeMath {
	// умножение
  function safeMul(uint256 a, uint256 b) internal returns (uint256) {
    uint256 c = a * b;
    assert(a == 0 || c / a == b);
    return c;
  }
	//деление
  function safeDiv(uint256 a, uint256 b) internal returns (uint256) {
    assert(b > 0);
    uint256 c = a / b;
    assert(a == b * c + a % b);
    return c;
  }
	//вычитание
  function safeSub(uint256 a, uint256 b) internal returns (uint256) {
    assert(b <= a);
    return a - b;
  }
	//сложение
  function safeAdd(uint256 a, uint256 b) internal returns (uint256) {
    uint256 c = a + b;
    assert(c>=a && c>=b);
    return c;
  }

  function assert(bool assertion) internal {
    if (!assertion) {
      throw;
    }
  }
}

	//Контракт BNB наследник контракта SafeMath
contract BNB is SafeMath{
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;
	address public owner;

    /* Мэппинги */
    mapping (address => uint256) public balanceOf; //балансы пользователей
	mapping (address => uint256) public freezeOf; // мэппинг замороженных токенов
    mapping (address => mapping (address => uint256)) public allowance; // мэппинг делегированных токенов

    /* Событие при успешном выполнении функции transfer */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /* Событие при выполнении функции сжигания токенов */
    event Burn(address indexed from, uint256 value);
	
	/* Событие при выполнении функции заморозки токенов */
    event Freeze(address indexed from, uint256 value);
	
	/* Событие при выполнении функции разморозки токенов */
    event Unfreeze(address indexed from, uint256 value);
	

    /* Конструктор */
    function BNB(
        uint256 initialSupply,
        string tokenName,
        uint8 decimalUnits,
        string tokenSymbol
        ) {
        balanceOf[msg.sender] = initialSupply; // Все токены принадлежат создателю
        totalSupply = initialSupply; // Устанавливается общая эмиссия токенов
        name = tokenName; // Устанавливается имя токена
        symbol = tokenSymbol; // Устанавливается символ токена
        decimals = decimalUnits; // Кол-во нулей
		owner = msg.sender; // Владелец == отправитель
    }

    /* Функция для отправки токенов */
    function transfer(address _to, uint256 _value) {
        if (_to == 0x0) throw; // Запрет на передачу на адрес 0x0. Проверка что соответствует ETH-адресу
		if (_value <= 0) throw; 
        if (balanceOf[msg.sender] < _value) throw; // Проверка что у отправителя <= кол-ву токенов
        if (balanceOf[_to] + _value < balanceOf[_to]) throw; // Проверка на переполнение
        balanceOf[msg.sender] = SafeMath.safeSub(balanceOf[msg.sender], _value);// Вычитает токены у отправителя
        balanceOf[_to] = SafeMath.safeAdd(balanceOf[_to], _value);//Прибавляет токены получателю
        Transfer(msg.sender, _to, _value);// Запускается событие Transfer
    }

    /* Функция для одобрения делегирования токенов */
    function approve(address _spender, uint256 _value)
        returns (bool success) {
		if (_value <= 0) throw; 
        allowance[msg.sender][_spender] = _value;
        return true;
    }   

    /* Функция для отправки делегированных токенов */
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        if (_to == 0x0) throw;
		if (_value <= 0) throw; 
        if (balanceOf[_from] < _value) throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) throw;
        if (_value > allowance[_from][msg.sender]) throw;
        balanceOf[_from] = SafeMath.safeSub(balanceOf[_from], _value);
        balanceOf[_to] = SafeMath.safeAdd(balanceOf[_to], _value);
        allowance[_from][msg.sender] = SafeMath.safeSub(allowance[_from][msg.sender], _value);
        Transfer(_from, _to, _value);
        return true;
    }

	 /* Функция для сжигания токенов */
    function burn(uint256 _value) returns (bool success) {
        if (balanceOf[msg.sender] < _value) throw;//проверка что на балансе есть нужное кол-во токенов
		if (_value <= 0) throw; 
        balanceOf[msg.sender] = SafeMath.safeSub(balanceOf[msg.sender], _value);// вычитание
        totalSupply = SafeMath.safeSub(totalSupply,_value);// Новое значение totalSupply
        Burn(msg.sender, _value);// Запуск события Burn
        return true;
    }
	
	 /* Функция заморозки токенов */
	function freeze(uint256 _value) returns (bool success) {
        if (balanceOf[msg.sender] < _value) throw; //проверка что на балансе есть нужное кол-во токенов
		if (_value <= 0) throw; 
        balanceOf[msg.sender] = SafeMath.safeSub(balanceOf[msg.sender], _value); // Уменьшаем в мэппинге balanceOf
        freezeOf[msg.sender] = SafeMath.safeAdd(freezeOf[msg.sender], _value); // Прибавляем в мэппинг freezeOf
        Freeze(msg.sender, _value);
        return true;
    }
	
	/* Функция разморозки токенов */
	function unfreeze(uint256 _value) returns (bool success) {
        if (freezeOf[msg.sender] < _value) throw;
		if (_value <= 0) throw;
        freezeOf[msg.sender] = SafeMath.safeSub(freezeOf[msg.sender], _value);
		balanceOf[msg.sender] = SafeMath.safeAdd(balanceOf[msg.sender], _value);
        Unfreeze(msg.sender, _value);
        return true;
    }
	
	//Позволяет создателю выводить хранящиеся на адрес контракта Эфиры и токены
	function withdrawEther(uint256 amount) {
		if(msg.sender != owner)throw;
		owner.transfer(amount);
	}
	
	// Функция payable позвлоляет контракту принимать и хранить эфиры
	function() payable {
    }
}

Контракт SafeMath содержит безопасные математические операции от OpenZeppelin. Так как тип uint не умеет работать с отрицательными и дробными числами, в этих внутренних функций идет проверка на соответствие.

Контракт BNB наследует функции из контракта SafeMath. В современной реализации контракт SafeMath стал библиотекой и подключается как библиотека.

contract BNB is SafeMath{
    string public name;
    string public symbol;
    uint8 public decimals;
    uint256 public totalSupply;
	address public owner;

Здесь все понятно. Определяются переменные
name — имя токена;
symbol — символ;
decimals — кол-во нулей;
totalSupply — общее кол-во токенов;
owner — адрес создателя (владельца) токена.

Далее создаются стандартные ERC20-мэппинги balanceOf и allowance, а также дополнительный мэппинг freezeOf для хранения баланса замороженных токенов в виде адрес=> количество.

mapping (address => uint256) public balanceOf; 
mapping (address => uint256) public freezeOf;
mapping (address => mapping (address => uint256)) public allowance;

Далее определяются 4 события:
Transfer — запускается в случае успешного выполнения функции transfer;
Burn — запускается при сжигании токенов;
Freeze — запускается при заморозке токенов;
Unfreeze — запускается при разморозке токенов.

event Transfer(address indexed from, address indexed to, uint256 value);
event Burn(address indexed from, uint256 value);
event Freeze(address indexed from, uint256 value);
event Unfreeze(address indexed from, uint256 value);

Конструктор BNB присваивает все создаваемые токены адресу создателя, устанавливает общее количество выпускаемых токенов, устанавливает имя, символ, кол-во нулей токена. А также присваивает переменной owner адрес создателя. Имя, символ, кол-во нулей и количество токенов не прописаны явно, а указываются при деплое как параметры конструктора.

    function BNB(
        uint256 initialSupply,
        string tokenName,
        uint8 decimalUnits,
        string tokenSymbol
        ) {
        balanceOf[msg.sender] = initialSupply;
        totalSupply = initialSupply;
        name = tokenName;
        symbol = tokenSymbol;
        decimals = decimalUnits;
		owner = msg.sender;
    }

Функцию transfer подробно описывал в статье про стандарт erc20. В современных версиях Solidity конструкция if() throw упразднена, вместо нее используется require(), применительно к проверке адреса это будет выглядеть так:

require(_to != address(0));

Про функцию делегирования токенов approve также подробно писал в статье Что такое токены стандарта ERC20, здесь повторяться не буду.

Пользователь может уничтожить часть или все токены на балансе своего адреса с помощью функции Burn. Регулярно можно слышать что Binance сжигает часть своих токенов, делает она это как раз с помощью этой функции. Раньше для сжигания токенов их просто отправляли на адрес 0x0000000000000000000000000000000000000000, при проверке этого адреса в Etherscan.io можно увидеть огромное количество токенов и эфиров на балансе, которые будут лежать там мертвым грузом вечно, так как ни у кого нет возможности списать их оттуда.

Функция берет параметром количество сжигаемых токенов, останавливается если баланс меньше указанного кол-ва сжигаемых токенов, останавливается если количество меньше равно нулю. Далее с помощью математической функции safeSub из контракта SafeMath уменьшает баланс токенов на указанное количество сжигаемых токенов, потом обновляет общее количество токенов в переменной totalSupply. После запускает событие Burn и возвращает true.

    function burn(uint256 _value) returns (bool success) {
        if (balanceOf[msg.sender] < _value) throw;
		if (_value <= 0) throw; 
        balanceOf[msg.sender] = SafeMath.safeSub(balanceOf[msg.sender], _value);
        totalSupply = SafeMath.safeSub(totalSupply,_value);
        Burn(msg.sender, _value);
        return true;
    }

Функции freeze и unfreeze позволяют заморозить и разморозить часть или все токены.  Замороженные токены вычитаются из баланса пользователя balanceOf и записываются в мэппинг freezeOf. Замороженные токены нельзя переводить и сжигать. Разморозить токены можно с помощью функции unfreeze.

	function freeze(uint256 _value) returns (bool success) {
        if (balanceOf[msg.sender] < _value) throw; 
		if (_value <= 0) throw; 
        balanceOf[msg.sender] = SafeMath.safeSub(balanceOf[msg.sender], _value);
        freezeOf[msg.sender] = SafeMath.safeAdd(freezeOf[msg.sender], _value);
        Freeze(msg.sender, _value);
        return true;
    }

	function unfreeze(uint256 _value) returns (bool success) {
        if (freezeOf[msg.sender] < _value) throw;
		if (_value <= 0) throw;
        freezeOf[msg.sender] = SafeMath.safeSub(freezeOf[msg.sender], _value);
		balanceOf[msg.sender] = SafeMath.safeAdd(balanceOf[msg.sender], _value);
        Unfreeze(msg.sender, _value);
        return true;
    }

Наличие безымянной функции с модификатором payable позволяет хранить на адресе контракта эфиры и другие токены. С помощью функции withdrawEther создатель может перечислить на свой адрес эфиры и токены.

	function() payable {
    }
	function withdrawEther(uint256 amount) {
		if(msg.sender != owner)throw;
		owner.transfer(amount);
    }

Весь код можно задеплоить в блокчейн со своим именем токена.

Насколько публикация полезна?

Нажмите на звезду, чтобы оценить!

Средняя оценка 0 / 5. Количество оценок: 0

Оценок пока нет. Поставьте оценку первым.

Так как вы нашли эту публикацию полезной...

Подписывайтесь на нас в соцсетях!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *