Waves aqlli hisoblarini qo'llash: auktsionlardan bonus dasturlarigacha

Waves aqlli hisoblarini qo'llash: auktsionlardan bonus dasturlarigacha

Blokcheyn ko'pincha faqat kriptovalyutalar bilan bog'lanadi, ammo DLT texnologiyasini qo'llash sohalari ancha kengroqdir. Blockchain-dan foydalanishning eng istiqbolli yo'nalishlaridan biri bu avtomatik ravishda amalga oshiriladigan va unga kirgan tomonlar o'rtasida ishonchni talab qilmaydigan aqlli shartnomadir.

RIDE – aqlli shartnomalar tili

Waves aqlli shartnomalar uchun maxsus tilni ishlab chiqdi - RIDE. Uning to'liq hujjatlari joylashgan shu yerda. Va bu erda - ushbu mavzu bo'yicha maqola Habrda.

RIDE shartnomasi predikat bo'lib, natija sifatida "to'g'ri" yoki "noto'g'ri" ni qaytaradi. Shunga ko'ra, tranzaktsiya blokcheynda qayd etiladi yoki rad etiladi. Smart shartnoma belgilangan shartlarning bajarilishini to'liq kafolatlaydi. RIDE-da shartnomadan tranzaksiyalarni yaratish hozircha mumkin emas.

Bugungi kunda Waves aqlli shartnomalarining ikki turi mavjud: aqlli hisoblar va aqlli aktivlar. Aqlli hisob - bu oddiy foydalanuvchi hisobi, lekin u uchun barcha tranzaktsiyalarni boshqaradigan skript o'rnatilgan. Aqlli hisob skripti shunday ko'rinishi mumkin, masalan:

match tx {
  case t: TransferTransaction | MassTransferTransaction => false
  case _ => true
}

tx ishlov berilayotgan tranzaksiya bo‘lib, biz namunani moslashtirish mexanizmidan foydalanishga ruxsat beramiz, agar u transfer tranzaksiyasi bo‘lmasa. Tranzaksiya turini tekshirish uchun RIDE-da naqsh moslashuvidan foydalaniladi. Barcha mavjud hisoblar smart hisob skriptida qayta ishlanishi mumkin tranzaktsiya turlari.

Skript shuningdek, o'zgaruvchilarni e'lon qilishi, "if-then-alse" konstruktsiyalarini va shartlarni to'liq tekshirish uchun boshqa usullardan foydalanishi mumkin. Shartnomalar isbotlangan to'liqligi va murakkabligi (narxi)ga ega bo'lishini ta'minlash uchun shartnomani bajarish boshlanishidan oldin bashorat qilish oson, RIDE looplar yoki o'tish bayonotlarini o'z ichiga olmaydi.

Waves hisoblarining boshqa xususiyatlariga "holat" ning mavjudligi, ya'ni hisobning holati kiradi. Ma'lumotlar tranzaksiyalari (DataTransaction) yordamida hisob holatiga cheksiz sonli juftliklarni (kalit, qiymat) yozishingiz mumkin. Keyinchalik bu ma'lumot REST API orqali ham, to'g'ridan-to'g'ri aqlli shartnomada ham qayta ishlanishi mumkin.

Har bir tranzaksiya bir qator dalillarni o'z ichiga olishi mumkin, ularga ishtirokchining imzosi, kerakli operatsiya identifikatori va boshqalar kiritilishi mumkin.

orqali RIDE bilan ishlash BU YERGA shartnomaning tuzilgan ko'rinishini ko'rish (agar u tuzilgan bo'lsa), yangi hisoblar yaratish va unga skriptlarni o'rnatish, shuningdek, buyruq qatori orqali tranzaktsiyalarni yuborish imkonini beradi.

Hisob yaratish, unga aqlli shartnoma o'rnatish va tranzaktsiyalarni jo'natish kabi to'liq tsikl uchun siz REST API bilan ishlash uchun kutubxonadan ham foydalanishingiz mumkin (masalan, C#, C, Java, JavaScript, Python, Rust, Elixir) . IDE bilan ishlashni boshlash uchun “YANGI” tugmasini bosish kifoya.

Aqlli kontraktlardan foydalanish imkoniyatlari keng: tranzaktsiyalarni ma'lum manzillarga ("qora ro'yxat") taqiqlashdan tortib murakkab dAppsgacha.

Keling, biznesda aqlli shartnomalardan foydalanishning aniq misollarini ko'rib chiqaylik: auktsionlar o'tkazish, sug'urta qilish va sodiqlik dasturlarini yaratishda.

Auktsionlar

Muvaffaqiyatli auktsionning shartlaridan biri shaffoflikdir: ishtirokchilar takliflarni manipulyatsiya qilish mumkin emasligiga ishonch hosil qilishlari kerak. Bunga blokcheyn tufayli erishish mumkin, bu erda barcha garovlar va ular qilingan vaqt haqidagi o'zgarmas ma'lumotlar barcha ishtirokchilar uchun mavjud bo'ladi.

Waves blokcheynida takliflar DataTransaction orqali auktsion hisobi holatida yozilishi mumkin.

Blok raqamlari yordamida kim oshdi savdosining boshlanish va tugash vaqtini ham belgilashingiz mumkin: Waves blokcheynida blok yaratish chastotasi taxminan teng. 60 soniya.

1. Inglizcha oshib borayotgan narx auktsioni

Ingliz auktsioni ishtirokchilari bir-birlari bilan raqobatda o'z takliflarini joylashtiradilar. Har bir yangi tikish avvalgisidan oshishi kerak. Oxirgi taklifdan oshib ketadigan ishtirokchilar qolmaganda kim oshdi savdosi tugaydi. Bunday holda, eng yuqori narx taklifi ko'rsatilgan miqdorni taqdim etishi kerak.

Shuningdek, kim oshdi savdosi opsiyasi ham mavjud, unda sotuvchi lot uchun minimal narxni belgilaydi va yakuniy narx undan oshishi kerak. Aks holda, lot sotilmay qoladi.

Ushbu misolda biz kim oshdi savdosi uchun maxsus yaratilgan hisob bilan ishlayapmiz. Auktsion muddati 3000 blok, lotning boshlang‘ich narxi esa 0,001 TO‘LQIN. Ishtirokchi "narx" kaliti va o'z taklifining qiymati bilan DataTransaction jo'natish orqali taklif qo'yishi mumkin.

Yangi taklifning narxi ushbu kalitning joriy narxidan yuqori bo'lishi kerak va ishtirokchining hisobida kamida [new_bid + komissiya] tokenlari bo'lishi kerak. Savdo ishtirokchisining manzili DataTransaction-dagi "yuboruvchi" maydoniga yozilishi kerak va joriy taklif blokining balandligi auktsion davrida bo'lishi kerak.

Agar kim oshdi savdosi yakunida ishtirokchi eng yuqori narxni belgilagan bo'lsa, u belgilangan narx va valyuta juftligi bo'yicha tegishli lot uchun to'lovni amalga oshirish uchun ExchangeTransaction yuborishi mumkin.

let startHeight = 384120
let finishHeight = startHeight + 3000
let startPrice = 100000
 
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
 
match tx {
case d : DataTransaction =>
  #проверяем, задана ли в стейте цена
  let currentPrice = if isDefined(getInteger(this, "price"))
 
                      #извлекаем цену из стейта
                      then extract(getInteger(this, "price"))
                      else startPrice
 
  #извлекаем цену из транзакции
  let newPrice = extract(getInteger(d.data, "price"))
  let priceIsBigger = newPrice > currentPrice
  let fee = 700000
  let hasMoney = wavesBalance(tx.sender) + fee >= newPrice
 
  #убеждаемся, что в текущей транзакции два поля и что отправитель совпадает с указанным в транзакции
  let correctFields = size(d.data) == 2 &&      
      d.sender == addressFromString(extract(getString(d.data,"sender")))
  startHeight <= height && height <= finishHeight && priceIsBigger && hasMoney && correctFields
case e : ExchangeTransaction =>
  let senderIsWinner = e.sender == addressFromString(extract(getString(this, "sender"))) #убеждаемся, что лот обменивает тот, кто его выиграл
  let correctAssetPair = e.sellOrder.assetPair.amountAsset == token && ! isDefined(e.sellOrder.assetPair.priceAsset)
  let correctAmount = e.amount == 1
  let correctPrice = e.price == extract(getInteger(this, "price"))
 
  height > finishHeight && senderIsWinner && correctAssetPair && correctAmount && correctPrice
case _ => false
}

2. Narxlarni pasaytirish Gollandiya auktsioni

Gollandiya auktsionida dastlab lot xaridor to'lashga tayyor bo'lgan narxdan yuqori narxda taklif qilinadi. Ishtirokchilardan biri lotni joriy narxda sotib olishga rozi bo'lmaguncha narx bosqichma-bosqich pasaytiriladi.

Ushbu misolda biz oldingi kabi bir xil konstantalardan foydalanamiz, shuningdek, delta pasayganda narx qadami. Hisob skripti ishtirokchi birinchi bo'lib pul tikishini tekshiradi. Aks holda, DataTransaction blokcheyn tomonidan qabul qilinmaydi.

let startHeight = 384120
let finishHeight = startHeight + 3000
let startPrice = 100000000
let delta = 100
 
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
match tx {
case d : DataTransaction =>
  let currentPrice = startPrice - delta * (height - startHeight)
 
  #извлекаем из поступившей дата-транзакции поле "price"
  let newPrice = extract(getInteger(d.data, "price"))
 
  #убеждаемся, что в стейте текущего аккаунта не содержится поля "sender"
  let noBetsBefore = !isDefined(getInteger(this, "sender"))
  let fee = 700000
  let hasMoney = wavesBalance(tx.sender) + fee >= newPrice
 
  #убеждаемся, что в текущей транзакции только два поля
  let correctFields = size(d.data) == 2 && newPrice == currentPrice && d.sender == addressFromString(extract(getString(d.data, "sender")))
  startHeight <= height && height <= finishHeight && noBetsBefore && hasMoney && correctFields
case e : ExchangeTransaction =>
 
  #убеждаемся, что отправитель текущей транзакции указан в стейте аккаунта по ключу sender
  let senderIsWinner = e.sender == addressFromString(extract(getString(this, "sender")))
 
  #убеждаемся, что аmount ассета указан корректно, и что прайс-ассет - waves
  let correctAssetPair = e.sellOrder.assetPair.amountAsset == token && ! isDefined(e.sellOrder.assetPair.priceAsset)
  let correctAmount = e.amount == 1
  let correctPrice = e.price == extract(getInteger(this, "price"))
  height > finishHeight && senderIsWinner && correctAssetPair && correctAmount && correctPrice
case _ => false
}

3. “barcha toʻlov” kim oshdi savdosi

"All-pay" - bu kim lotda g'olib bo'lishidan qat'i nazar, barcha ishtirokchilar taklifni to'laydigan auktsiondir. Har bir yangi ishtirokchi taklifni to'laydi va maksimal taklifni kiritgan ishtirokchi lotda g'alaba qozonadi.

Bizning misolimizda, har bir auktsion ishtirokchisi DataTransaction orqali (kalit, qiymat)* = (“g‘olib”, manzil), (“narx”, narx) bilan taklif qo‘yadi. Bunday ma'lumotlar tranzaktsiyasi, agar ushbu ishtirokchi o'z imzosi bilan TransferTransactionga ega bo'lsa va uning taklifi barcha oldingilaridan yuqori bo'lsa, tasdiqlanadi. Auktsion endHeight ga yetguncha davom etadi.

let startHeight = 1000
let endHeight = 2000
let this = extract(tx.sender)
let token = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
match tx {
 case d: DataTransaction =>
   #извлекаем из поступившей дата-транзакции поле "price"
   let newPrice = extract(getInteger(d.data, "price"))
 
   #извлекаем из пруфов транзакции публичный ключ аккаунта
   let pk = d.proofs[1]
   let address = addressFromPublicKey(pk)
 
   #извлекаем транзакцию доказательство из пруфов поступившей дата транзакции
   let proofTx = extract(transactionById(d.proofs[2]))
   
   height > startHeight && height < endHeight
   && size(d.data) == 2
   #убеждаемся, что адрес победителя, извлеченный из текущей транзакции, совпадает с адресом, извлеченным из пруфов
   && extract(getString(d.data, "winner")) == toBase58String(address.bytes)
   && newPrice > extract(getInteger(this, "price"))
   #проверяем, что транзакция подписана
   && sigVerify(d.bodyBytes, d.proofs[0], d.proofs[1])
   #проверяем корректность транзакции, указанной в пруфах
   && match proofTx {
     case tr : TransferTransaction =>
       tr.sender == address &&
       tr.amount == newPrice
     case _ => false
   }
 case t: TransferTransaction =>
 sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
 || (
   height > endHeight
   && extract(getString(this, "winner")) == toBase58String((addressFromRecipient(t.recipient)).bytes)
   && t.assetId == token
   && t.amount == 1
 )
 case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}

Sug'urta / Crowdfunding

Keling, foydalanuvchilarning aktivlarini moliyaviy yo'qotishlardan sug'urtalashingiz kerak bo'lgan vaziyatni ko'rib chiqaylik. Misol uchun, foydalanuvchi agar token qadrsizlansa, u ushbu tokenlar uchun to‘langan to‘liq summani qaytarib olishi va o‘rtacha miqdorda sug‘urta to‘lashga tayyorligi kafolatini istaydi.

Buni amalga oshirish uchun "sug'urta tokenlari" chiqarilishi kerak. Keyin polis egasining hisobiga skript o'rnatiladi, bu faqat ma'lum shartlarga javob beradigan ExchangeTransactionsni amalga oshirishga imkon beradi.

Ikki marta sarflanishining oldini olish uchun siz foydalanuvchidan (kalit, qiymat) = (purchaseTransactionId, sellOrderId) bilan polis egasining hisobiga DataTransaction yuborishni soʻrashingiz va allaqachon ishlatilgan kalit bilan DataTransaction joʻnatishni taqiqlashingiz kerak.

Shuning uchun, foydalanuvchining dalillarida sug'urta tokenini sotib olishning tranzaksiya identifikatori bo'lishi kerak. Valyuta juftligi sotib olish operatsiyasidagi kabi bo'lishi kerak. Narx, shuningdek, sug'urta narxini chegirib tashlagan holda, sotib olish paytida belgilangan qiymatga teng bo'lishi kerak.

Keyinchalik sug'urta hisobvarag'i foydalanuvchidan sug'urta tokenlarini u sotib olganidan past bo'lmagan narxda sotib olishi tushuniladi: sug'urta hisobvarag'i ExchangeTransaction-ni yaratadi, foydalanuvchi buyurtmani imzolaydi (agar tranzaktsiya to'g'ri bajarilgan bo'lsa), sug'urta hisob ikkinchi tartibini va butun operatsiyani imzolaydi va blokcheyna yuboradi.

Hech qanday xarid sodir bo'lmasa, foydalanuvchi skriptda tasvirlangan qoidalarga muvofiq ExchangeTransaction yaratishi va tranzaktsiyani blokcheynga yuborishi mumkin. Shunday qilib, foydalanuvchi sug'urtalangan tokenlarni sotib olishga sarflangan pulni qaytarishi mumkin.

let insuranceToken = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
 
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let freezePeriod = 150000
let insurancePrice = 10000
match tx {
 
 #убеждаемся, что, если поступила дата-транзакция, то у нее ровно одно поле и в стейте еще нет такого ключа
 case d : DataTransaction => size(d.data) == 1 && !isDefined(getBinary(this, d.data[0].key))
 case e : ExchangeTransaction =>
 
   #если у транзакции нет седьмого пруфа, проверяем корректность подписи
   if !isDefined(e.proofs[7]) then
     sigVerify(e.bodyBytes, e.proofs[0], e.senderPublicKey)
   else
     #если у транзакции есть седьмой пруф, извлекаем из него транзакцию и узнаём её высоту
     let purchaseTx = transactionById(e.proofs[7])
     let purchaseTxHeight = extract(transactionHeightById(e.proofs[7]))
    
     #обрабатываем транзакцию из пруфа
     match purchaseTx {
       case purchase : ExchangeTransaction =>
         let correctSender = purchase.sender == e.sellOrder.sender
         let correctAssetPair = e.sellOrder.assetPair.amountAsset == insuranceToken &&
                                purchase.sellOrder.assetPair.amountAsset == insuranceToken &&
                                e.sellOrder.assetPair.priceAsset == purchase.sellOrder.assetPair.priceAsset
         let correctPrice = e.price == purchase.price - insurancePrice && e.amount == purchase.amount
         let correctHeight = height > purchaseTxHeight + freezePeriod
 
         #убеждаемся, что в транзакции-пруфе указан верный ID текущей транзакции
         let correctProof = extract(getBinary(this, toBase58String(purchase.id))) == e.sellOrder.id
         correctSender && correctAssetPair && correctPrice && correctHeight && correctProof
     case _ => false
   }
 case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}

Sug'urta tokenini aqlli aktivga aylantirish mumkin, masalan, uni uchinchi shaxslarga topshirishni taqiqlash.

Ushbu sxema, agar kerakli miqdor yig'ilmagan bo'lsa, egalariga qaytariladigan kraudfanding tokenlari uchun ham amalga oshirilishi mumkin.

Tranzaksiya soliqlari

Aqlli shartnomalar, shuningdek, bir nechta turdagi aktivlar bilan har bir operatsiya bo'yicha soliq undirish zarur bo'lgan hollarda ham qo'llaniladi. Buni o'rnatilgan yangi aktiv orqali amalga oshirish mumkin homiylik aqlli aktivlar bilan operatsiyalar uchun:

1. Biz FeeCoin chiqaramiz, u foydalanuvchilarga belgilangan narxda yuboriladi: 0,01 WAVES = 0,001 FeeCoin.

2. FeeCoin va valyuta kursi uchun homiylikni belgilang: 0,001 WAVES = 0,001 FeeCoin.

3. Smart aktiv uchun quyidagi skriptni o‘rnating:

let feeAssetId = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
let taxDivisor = 10
 
match tx {
  case t: TransferTransaction =>
    t.feeAssetId == feeAssetId && t.fee == t.amount / taxDivisor
  case e: ExchangeTransaction | MassTransferTransaction => false
  case _ => true
}

Endi kimdir har safar N/taxDivisor aktivlarini o‘tkazsa, ular sizga N/taxDivisor miqdorida FeeCoin beradi (uni sizdan 10 *N/taxDivisor WAVES da xarid qilish mumkin), siz esa konchiga N/taxDivisor WAVES berasiz. Natijada, sizning foyda (soliq) 9*N / taxDivisor WAVES bo'ladi.

Shuningdek, siz aqlli aktiv skripti va MassTransferTransaction yordamida soliq solishni amalga oshirishingiz mumkin:

let taxDivisor = 10
 
match tx {
  case t : MassTransferTransaction =>
    let twoTransfers = size(t.transfers) == 2
    let issuerIsRecipient = t.transfers[0].recipient == addressFromString("3MgkTXzD72BTfYpd9UW42wdqTVg8HqnXEfc")
    let taxesPaid = t.transfers[0].amount >= t.transfers[1].amount / taxDivisor
    twoTransfers && issuerIsRecipient && taxesPaid
  case _ => false
}

Cashback va sodiqlik dasturlari

Cashback - bu sodiqlik dasturining bir turi bo'lib, unda xaridor mahsulot yoki xizmatga sarflangan summaning bir qismini qaytarib oladi.

Ushbu ishni aqlli hisob yordamida amalga oshirayotganda, biz sug'urta ishida bo'lgani kabi dalillarni tekshirishimiz kerak. Ikki marta sarflanishining oldini olish uchun foydalanuvchi naqd pul olishdan oldin (kalit, qiymat) = (purchaseTransactionId, cashbackTransactionId) bilan DataTransaction yuborishi kerak.

Shuningdek, DataTransaction yordamida mavjud kalitlarga taqiq qo'yishimiz kerak. cashbackDivisor - naqd pul ulushiga bo'lingan birlik. Bular. agar naqd pul ulushi 0.1 bo'lsa, u holda cashbackDivisor 1 / 0.1 = 10.

let cashbackToken = base58'8jfD2JBLe23XtCCSQoTx5eAW5QCU6Mbxi3r78aNQLcNf'
 
#извлекаем из транзакции адрес отправителя
let this = extract(tx.sender)
let cashbackDivisor = 10
match tx {
 
 #убеждаемся, что, если поступила дата-транзакция, то у нее ровно одно поле и в стейте еще нет такого ключа
 case d : DataTransaction => size(d.data) == 1 && !isDefined(getBinary(this, d.data[0].key))
 case e : TransferTransaction =>
 
   #если у транзакции нет седьмого пруфа, проверяем корректность подписи
   if !isDefined(e.proofs[7]) then
     sigVerify(e.bodyBytes, e.proofs[0], e.senderPublicKey)
   else
 
     #если у транзакции есть седьмой пруф, извлекаем из него транзакцию и узнаём её высоту
     let purchaseTx = transactionById(e.proofs[7])
     let purchaseTxHeight = extract(transactionHeightById(e.proofs[7]))
    
     #обрабатываем транзакцию из пруфа
     match purchaseTx {
       case purchase : TransferTransaction =>
         let correctSender = purchase.sender == e.sender
         let correctAsset = e.assetId == cashbackToken
         let correctPrice = e.amount == purchase.amount / cashbackDivisor
 
         #убеждаемся, что в транзакции-пруфе указан верный ID текущей транзакции
         let correctProof = extract(getBinary(this, toBase58String(purchase.id))) == e.id
         correctSender && correctAsset && correctPrice && correctProof
     case _ => false
   }
 case _ => sigVerify(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)
}

Atom almashinuvi

Atom almashinuvi foydalanuvchilarga birja yordamisiz aktivlarni almashish imkonini beradi. Atom almashinuvi bilan bitimning ikkala ishtirokchisi ham ma'lum vaqt ichida uni tasdiqlashi kerak.

Agar ishtirokchilardan kamida bittasi bitim uchun ajratilgan vaqt ichida bitimning to'g'ri tasdiqlanishini taqdim etmasa, bitim bekor qilinadi va almashinuv sodir bo'lmaydi.

Bizning misolimizda biz quyidagi aqlli hisob skriptidan foydalanamiz:

let Bob = Address(base58'3NBVqYXrapgJP9atQccdBPAgJPwHDKkh6A8')
let Alice = Address(base58'3PNX6XwMeEXaaP1rf5MCk8weYeF7z2vJZBg')
 
let beforeHeight = 100000
 
let secret = base58'BN6RTYGWcwektQfSFzH8raYo9awaLgQ7pLyWLQY4S4F5'
match tx {
  case t: TransferTransaction =>
    let txToBob = t.recipient == Bob && sha256(t.proofs[0]) == secret && 20 + beforeHeight >= height
    let backToAliceAfterHeight = height >= 21 + beforeHeight && t.recipient == Alice
    txToBob || backToAliceAfterHeight
  case _ => false
}

Keyingi maqolada biz optsionlar, fyucherslar va veksellar kabi moliyaviy vositalarda aqlli hisoblardan foydalanishni ko'rib chiqamiz.

Manba: www.habr.com

a Izoh qo'shish