image

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

Сейчас мы рассмотрим смарт-ассеты и несколько кейсов их применения, включая замораживание активов и создание ограничений на транзакции по заданным адресам.


Смарт-ассеты Waves позволяют пользователям накладывать скрипты на ассеты, следуя той же механике, что и в случае со смарт-аккаунтами. Каждая новая транзакция, созданная с использованием смарт-ассета, будет подтверждена сначала скриптом, и только потом блокчейном.

Стоит отметить следующие отличия смарт-ассетов от смарт-аккаунтов:

  1. В коде смарт-ассета нельзя осуществить проверку пруфов (о них мы говорили в первой статье).
  2. В коде смарт-аккаунта можно проверять ExchangeTransaction, только если ваш аккаунт является аккаунтом-матчером. В противном случае проверяется только ордер. В коде смарт-ассета проверить непосредственно ордер нельзя, можно проверять ExchangeTransaction, и из неё уже при необходимости извлекать ордер.
  3. У смарт-ассета, в отличие от смарт-аккаунта, нет стейта, но мы все же имеем доступ к стейтам аккаунтов из скрипта.

Смарт-ассеты значительно упрощают написание контрактов, делая реализацию многих кейсов лаконичной и элегантной.

Заморозка активов

Чтобы заморозить активы до определенной высоты блока targetHeight, можно просто задать это значение в скрипте следующего смарт-ассета:

let targetHeight = 1500000
height >= targetHeight
 
height - функция языка, возращающая текущую высоту.

Условие конкретного матчера

Чтобы установить в качестве желаемого конкретный матчер, вы можете задать его адрес в качестве отправителя в скрипте смарт-ассета следующего вида:

match tx {
    case t : ExchangeTransaction =>
        t.sender == addressFromString("3PJaDyprvekvPXPuAtxrapacuDJopgJRaU3")
    case _ => true
}

«Белый список» получателей

Чтобы разрешить отправку токенов только на определенные счета — создать «белый список» получателей — вы можете использовать смарт-ассет со следующей схемой, проверяющей вхождение в список:

match tx {
  case t : TransferTransaction =>
    let trustedRecipient1 = addressFromString("3P6ms9EotRX8JwSrebeTXYVnzpsGCrKWLv4")
    let trustedRecipient2 = addressFromString("3PLZcCJyYQnfWfzhKXRA4rteCQC9J1ewf5K")
    let trustedRecipient3 = addressFromString("3PHrS6VNPRtUD8MHkfkmELavL8JnGtSq5sx")
    t.recipient == trustedRecipient1 || t.recipient == trustedRecipient2 || t.recipient == trustedRecipient3
  case _ => false
}

В целях безопасности и доказуемой завершимости языка список не содержит реализации итератора. Поэтому он задан в качестве набора конкретных элементов.

«Черный список» получателей

Точно так же, для запрета на отправку токенов на определенные счета, вы можете создать «черный список». При этом используется абсолютно такой же смарт-ассет, но с проверкой адреса на отсутствие в черном списке:

match tx {
  case t : TransferTransaction =>
    let bannedRecipient1 = addressFromString("3P6ms9EotRX8JwSrebeTXYVnzpsGCrKWLv4")
    let bannedRecipient2 = addressFromString("3PLZcCJyYQnfWfzhKXRA4rteCQC9J1ewf5K")
    let bannedRecipient3 = addressFromString("3PHrS6VNPRtUD8MHkfkmELavL8JnGtSq5sx")
    t.recipient != bannedRecipient1 && t.recipient != bannedRecipient2 && t.recipient != bannedRecipient3
  case _ => false
}

Отправка с разрешения эмитента

При помощи смарт-ассета можно также установить опцию отправки смарт-ассета только с разрешения эмитента (commitment/debt label). Эмитент выражает свое согласие, разместив ID транзакции в стейте своего аккаунта:

match tx {
  case t : TransferTransaction =>
    let issuer = extract(addressFromString("3P6ms9EotRX8JwSrebeTXYVnzpsGCrKWLv4"))
    #убеждаемся, что в стейте эмитента содержится ID текущей транзакции
    isDefined(getInteger(issuer, toBase58String(t.id)))
  case _ => false
}

Обмен только на определенные монеты

Cмарт-ассет допускает разрешение обменивать его только на определенные монеты. Например, чтобы разрешить обмен только на биткойны, можно использовать следующий код:

let BTCId = base58'8LQW8f7P5d5PZM7GtZEBgaqRPGSzS3DfPuiXrURJ4AJS'
match tx {
  case t : ExchangeTransaction =>
    t.sellOrder.assetPair.priceAsset == BTCId ||
     t.sellOrder.assetPair.amountAsset == BTCId
  case _ => true
}

Торговля по цене от оракула

В скрипте смарт-ассета можно задать разрешение на торговлю только по цене, зафиксированной в стейте надежного оракула. Вот пример такого скрипта:

let oracle = Address(base58'3PLNmokt22NrSiNvCLvwMUP84LCMJqbXwAD')
let assetId = toBase58String(base58'oWgJN6YGZFtZrV8BWQ1PGktZikgg7jzGmtm16Ktyvjd')
 
match tx {
  #запрещаем передачу ассета
  case t: TransferTransaction | MassTransferTransaction => false
  case e: ExchangeTransaction =>
    #убеждаемся, что торговля происходит по цене, заданной в стейте оракла для этого ассета
    let correctPrice = e.price == extract(getInteger(oracle, assetId))
    #убеждаемся, что торговля происходит в обмен на WAVES
    let correctPriceAsset = !isDefined(e.sellOrder.assetPair.priceAsset) 
correctPrice && correctPriceAsset
  case _ => true
}

Здесь мы сталкиваемся с неочевидным моментом при проверке ID ассета, с которым осуществляется торговля. Дело в том, что, если ID ассета не определен, значит, речь идет о WAVES. В скрипте мы убеждаемся, что торговля осуществляется в паре с WAVES, именно таким образом.

Фиксированное увеличение цены

Можно установить фиксированную цену смарт-ассета, которая будет пошагово увеличиваться в заданной пропорции. Вот пример скрипта ассета, цена которого будет увеличивается на 5% каждые 1000 блоков:

let startPrice = 10
let startHeight = 1000
let interval = 1000
#на сколько процентов цена увеличивается за один шаг
let raise = 5
 
match tx {
  case t: TransferTransaction | MassTransferTransaction => false
  case e: ExchangeTransaction =>
    e.price == startPrice + ((height - startHeight) / interval) * (100 + raise) / 100
    && !isDefined(e.sellOrder.assetPair.priceAsset)
  case _ => true
}

Интервальный трейдинг

Также, благодаря скрипту, торговлю смарт-ассетом можно ограничить заранее определенными интервалами. Вот пример такого скрипта:

let startHeight = 10000
let interval = 44000
let limit = 1500
 
match tx {
  case t: TransferTransaction | MassTransferTransaction | ExchangeTransaction =>
    (height - startHeight) % interval < limit
  case _ => true
}

В скрипте мы убеждаемся, что с начала торговли startHeight прошло не более, чем limit интервалов. Длина интервала равна количеству блоков, заданному в поле interval.

Комментарии (0)