Penerapan akun pintar Waves: dari lelang hingga program bonus

Penerapan akun pintar Waves: dari lelang hingga program bonus

Blockchain sering dikaitkan hanya dengan mata uang kripto, namun bidang penerapan teknologi DLT jauh lebih luas. Salah satu bidang yang paling menjanjikan dalam penggunaan blockchain adalah kontrak pintar yang dijalankan secara otomatis dan tidak memerlukan kepercayaan antara pihak-pihak yang menandatanganinya.

RIDE – bahasa untuk kontrak pintar

Waves telah mengembangkan bahasa khusus untuk kontrak pintar – RIDE. Dokumentasi lengkapnya ada di sini. Dan di sini - artikel tentang topik ini di Habr.

Kontrak RIDE adalah predikat dan mengembalikan “benar” atau “salah” sebagai keluaran. Oleh karena itu, transaksi tersebut dicatat di blockchain atau ditolak. Kontrak pintar sepenuhnya menjamin terpenuhinya kondisi yang ditentukan. Menghasilkan transaksi dari kontrak di RIDE saat ini tidak dimungkinkan.

Saat ini ada dua jenis kontrak pintar Waves: akun pintar dan aset pintar. Akun pintar adalah akun pengguna biasa, tetapi skrip diatur untuk mengontrol semua transaksi. Skrip akun pintar mungkin terlihat seperti ini, misalnya:

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

tx adalah transaksi yang sedang diproses yang kami izinkan menggunakan mekanisme pencocokan pola hanya jika bukan transaksi transfer. Pencocokan pola di RIDE digunakan untuk memeriksa jenis transaksi. Semua akun yang ada dapat diproses di skrip akun pintar jenis transaksi.

Skrip juga dapat mendeklarasikan variabel, menggunakan konstruksi “if-then-else” dan metode lain untuk memeriksa kondisi sepenuhnya. Untuk memastikan bahwa kontrak memiliki kelengkapan dan kompleksitas (biaya) yang dapat dibuktikan dan mudah diprediksi sebelum pelaksanaan kontrak dimulai, RIDE tidak berisi pernyataan loop atau jump.

Fitur lain dari akun Waves termasuk adanya “status”, yaitu status akun. Anda dapat menulis pasangan (kunci, nilai) dalam jumlah tak terbatas ke status akun menggunakan transaksi data (DataTransaction). Informasi ini kemudian dapat diproses melalui REST API dan langsung di kontrak pintar.

Setiap transaksi dapat berisi serangkaian bukti, yang dapat dimasukkan tanda tangan peserta, ID transaksi yang diperlukan, dll.

Bekerja dengan RIDE melalui IDE memungkinkan Anda melihat tampilan kontrak yang dikompilasi (jika dikompilasi), membuat akun baru dan mengatur skrip untuknya, serta mengirim transaksi melalui baris perintah.

Untuk siklus penuh, termasuk membuat akun, menginstal kontrak pintar di dalamnya, dan mengirim transaksi, Anda juga dapat menggunakan perpustakaan untuk berinteraksi dengan REST API (misalnya, C#, C, Java, JavaScript, Python, Rust, Elixir) . Untuk mulai bekerja dengan IDE, cukup klik tombol BARU.

Kemungkinan penggunaan kontrak pintar sangat luas: mulai dari melarang transaksi ke alamat tertentu (“daftar hitam”) hingga dApps yang kompleks.

Sekarang mari kita lihat contoh spesifik penggunaan kontrak pintar dalam bisnis: saat melakukan lelang, asuransi, dan membuat program loyalitas.

Lelang

Salah satu syarat keberhasilan lelang adalah transparansi: peserta harus yakin bahwa manipulasi penawaran tidak mungkin dilakukan. Hal ini dapat dicapai berkat blockchain, di mana data yang tidak dapat diubah tentang semua taruhan dan waktu pembuatannya akan tersedia untuk semua peserta.

Di blockchain Waves, tawaran dapat dicatat di status akun lelang melalui DataTransaction.

Anda juga dapat mengatur waktu mulai dan berakhirnya lelang menggunakan nomor blok: frekuensi pembuatan blok di blockchain Waves kira-kira sama dengan 60 detik.

1. Lelang harga naik bahasa Inggris

Peserta lelang Inggris mengajukan penawaran dalam persaingan satu sama lain. Setiap taruhan baru harus melebihi taruhan sebelumnya. Lelang berakhir ketika tidak ada lagi penawar yang melebihi penawaran terakhir. Dalam hal ini, penawar tertinggi harus memberikan jumlah yang ditentukan.

Ada juga opsi lelang di mana penjual menetapkan harga minimum untuk suatu lot, dan harga akhir harus melebihi harga tersebut. Jika tidak, maka lot tersebut tetap tidak terjual.

Dalam contoh ini, kami bekerja dengan akun yang khusus dibuat untuk lelang. Durasi lelang adalah 3000 blok, dan harga awal lot adalah 0,001 WAVES. Peserta dapat mengajukan penawaran dengan mengirimkan DataTransaksi dengan kunci “harga” dan nilai penawarannya.

Harga tawaran baru harus lebih tinggi dari harga kunci ini saat ini, dan peserta harus memiliki setidaknya token [penawaran_baru + komisi] di akunnya. Alamat penawar harus dicatat di kolom "pengirim" di DataTransaction, dan tinggi blok penawaran saat ini harus berada dalam periode lelang.

Jika pada akhir lelang peserta telah menetapkan harga tertinggi, ia dapat mengirimkan ExchangeTransaction untuk membayar lot yang sesuai pada harga dan pasangan mata uang yang ditentukan.

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. Lelang Belanda penurunan harga

Dalam lelang Belanda, suatu barang awalnya ditawarkan dengan harga lebih tinggi dari harga yang bersedia dibayar pembeli. Harga diturunkan selangkah demi selangkah hingga salah satu peserta setuju untuk membeli lot pada harga saat ini.

Dalam contoh ini kita menggunakan konstanta yang sama seperti pada contoh sebelumnya, serta langkah harga ketika delta menurun. Skrip akun memeriksa apakah peserta memang yang pertama kali memasang taruhan. Jika tidak, DataTransaction tidak diterima oleh blockchain.

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. Lelang “semua bayar”

“All-pay” adalah lelang di mana semua peserta membayar penawarannya, terlepas dari siapa yang memenangkan lot. Setiap peserta baru membayar tawaran, dan peserta yang membuat tawaran maksimum memenangkan undian.

Dalam contoh kita, setiap peserta lelang mengajukan penawaran melalui DataTransaction dengan (kunci, nilai)* = (“pemenang”, alamat),(“harga”, harga). Transaksi Data tersebut disetujui hanya jika peserta tersebut sudah memiliki Transaksi Transfer dengan tanda tangannya dan tawarannya lebih tinggi dari semua peserta sebelumnya. Lelang berlanjut hingga endHeight tercapai.

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)
}

Asuransi / Crowdfunding

Mari kita pertimbangkan situasi ketika Anda perlu mengasuransikan aset pengguna terhadap kerugian finansial. Misalnya, pengguna menginginkan jaminan bahwa jika suatu token terdepresiasi, ia akan dapat memperoleh kembali jumlah penuh yang dibayarkan untuk token tersebut, dan bersedia membayar sejumlah asuransi yang wajar.

Untuk menerapkan hal ini, “token asuransi” perlu diterbitkan. Kemudian skrip diinstal pada akun pemegang polis, yang hanya mengizinkan Transaksi Bursa yang memenuhi kondisi tertentu untuk dieksekusi.

Untuk mencegah pembelanjaan ganda, Anda perlu meminta pengguna untuk mengirimkan DataTransaction ke akun pemegang polis terlebih dahulu dengan (key, value) = (purchaseTransactionId, sellOrderId) dan melarang pengiriman DataTransactions dengan kunci yang sudah digunakan.

Oleh karena itu, bukti pengguna harus memuat ID transaksi pembelian token asuransi. Pasangan mata uang harus sama seperti pada transaksi pembelian. Biayanya juga harus sama dengan biaya yang ditetapkan pada saat pembelian, dikurangi harga asuransi.

Dapat dipahami bahwa selanjutnya akun asuransi membeli token asuransi dari pengguna dengan harga yang tidak lebih rendah dari harga saat ia membelinya: akun asuransi membuat ExchangeTransaction, pengguna menandatangani pesanan (jika transaksi diselesaikan dengan benar), akun asuransi menandatangani pesanan kedua dan seluruh transaksi dan mengirimkannya ke blockchain.

Jika tidak ada pembelian yang terjadi, pengguna dapat membuat ExchangeTransaction sesuai dengan aturan yang dijelaskan dalam skrip dan mengirimkan transaksi ke blockchain. Dengan cara ini pengguna dapat mengembalikan uang yang dihabiskan untuk pembelian token yang diasuransikan.

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)
}

Token asuransi dapat dijadikan aset cerdas, misalnya, untuk melarang transfernya ke pihak ketiga.

Skema ini juga dapat diterapkan untuk token crowdfunding, yang dikembalikan ke pemilik jika jumlah yang dibutuhkan belum terkumpul.

Pajak transaksi

Kontrak pintar juga berlaku jika perlu memungut pajak pada setiap transaksi dengan beberapa jenis aset. Hal ini dapat dilakukan melalui aset baru yang sudah terpasang sponsorship untuk transaksi dengan aset pintar:

1. Kami menerbitkan FeeCoin, yang akan dikirimkan ke pengguna dengan harga tetap: 0,01 WAVES = 0,001 FeeCoin.

2. Tetapkan sponsor untuk FeeCoin dan nilai tukar: 0,001 WAVES = 0,001 FeeCoin.

3. Tetapkan skrip berikut untuk aset pintar:

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
}

Sekarang setiap kali seseorang mentransfer N aset pintar, mereka akan memberi Anda FeeCoin sejumlah N/taxDivisor (yang dapat dibeli dari Anda dengan harga 10 *N/taxDivisor WAVES), dan Anda akan memberikan N/taxDivisor WAVES kepada penambang. Hasilnya, keuntungan (pajak) Anda akan menjadi 9*N / taxDivisor WAVES.

Anda juga dapat melakukan perpajakan menggunakan skrip aset pintar dan MassTransferTransaction:

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
}

Program cashback dan loyalitas

Cashback adalah jenis program loyalitas di mana pembeli mendapatkan kembali sebagian dari jumlah yang dibelanjakan untuk suatu produk atau layanan.

Saat mengimplementasikan kasus ini menggunakan akun pintar, kita harus memeriksa bukti-buktinya seperti yang kita lakukan pada kasus asuransi. Untuk mencegah pembelanjaan ganda, pengguna harus mengirimkan DataTransaction dengan (key, value) = (purchaseTransactionId, cashbackTransactionId) sebelum menerima cashback.

Kita juga harus menetapkan larangan pada kunci yang ada menggunakan DataTransaction. cashbackDivisor - unit dibagi dengan bagian cashback. Itu. jika bagi hasil cashback 0.1 maka 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)
}

Pertukaran atom

Pertukaran atom memungkinkan pengguna untuk menukar aset tanpa bantuan pertukaran. Dengan pertukaran atom, kedua peserta transaksi diharuskan untuk mengkonfirmasinya dalam jangka waktu tertentu.

Jika setidaknya salah satu peserta tidak memberikan konfirmasi transaksi yang benar dalam waktu yang ditentukan untuk transaksi, transaksi dibatalkan dan pertukaran tidak terjadi.

Dalam contoh kita, kita akan menggunakan skrip akun pintar berikut:

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
}

Pada artikel selanjutnya kita akan melihat penggunaan akun pintar dalam instrumen keuangan seperti opsi, kontrak berjangka, dan tagihan.

Sumber: www.habr.com

Tambah komentar