Primjena Waves pametnih računa: od aukcija do bonus programa

Primjena Waves pametnih računa: od aukcija do bonus programa

Blockchain se često povezuje samo s kriptovalutama, ali područja primjene DLT tehnologije su mnogo šira. Jedno od područja koja najviše obećava za korištenje blockchaina je pametni ugovor koji se izvršava automatski i ne zahtijeva povjerenje između strana koje su ga sklopile.

RIDE – jezik za pametne ugovore

Waves je razvio poseban jezik za pametne ugovore – RIDE. Njena kompletna dokumentacija se nalazi ovdje. I ovdje - članak na ovu temu na Habr.

RIDE ugovor je predikat i vraća “true” ili “false” kao izlaz. U skladu s tim, transakcija se ili bilježi u blockchain ili odbija. Pametni ugovor u potpunosti garantuje ispunjenje navedenih uslova. Generiranje transakcija iz ugovora u RIDE trenutno nije moguće.

Danas postoje dvije vrste Waves pametnih ugovora: pametni računi i pametna sredstva. Pametni nalog je običan korisnički nalog, ali za njega je postavljena skripta koja kontroliše sve transakcije. Skripta pametnog naloga može izgledati ovako, na primjer:

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

tx je transakcija koja se obrađuje i koju dopuštamo korištenje mehanizma za podudaranje uzoraka samo ako nije transakcija prijenosa. Usklađivanje uzoraka u RIDE se koristi za provjeru vrste transakcije. Svi postojeći nalozi mogu se obraditi u skripti pametnog naloga vrste transakcija.

Skripta također može deklarirati varijable, koristiti “ako-onda-else” konstrukcije i druge metode za potpunu provjeru uslova. Kako bi se osiguralo da ugovori imaju dokazivu kompletnost i složenost (cijenu) koju je lako predvidjeti prije početka izvršenja ugovora, RIDE ne sadrži naredbe petlje ili skoka.

Ostale karakteristike Waves naloga uključuju prisustvo „stanja“, odnosno stanja naloga. Možete upisati beskonačan broj parova (ključ, vrijednost) u stanje računa koristeći transakcije podataka (DataTransaction). Ove informacije se zatim mogu obraditi i kroz REST API i direktno u pametnom ugovoru.

Svaka transakcija može sadržati niz dokaza u koje se može uneti potpis učesnika, ID tražene transakcije itd.

Rad sa RIDE preko IDE omogućava vam da vidite sastavljeni prikaz ugovora (ako je sastavljen), kreirate nove naloge i postavite skripte za njega, kao i da pošaljete transakcije preko komandne linije.

Za puni ciklus, uključujući kreiranje naloga, instaliranje pametnog ugovora na njega i slanje transakcija, možete koristiti i biblioteku za interakciju sa REST API-jem (na primjer, C#, C, Java, JavaScript, Python, Rust, Elixir) . Za početak rada sa IDE, samo kliknite na dugme NOVO.

Mogućnosti korištenja pametnih ugovora su široke: od zabrane transakcija na određene adrese („crna lista“) do složenih dApp-ova.

Pogledajmo sada konkretne primjere upotrebe pametnih ugovora u poslovanju: prilikom provođenja aukcija, osiguranja i kreiranja programa lojalnosti.

Aukcije

Jedan od uslova za uspešnu aukciju je transparentnost: učesnici moraju biti uvereni da je nemoguće manipulisati ponudama. To se može postići zahvaljujući blockchainu, gdje će nepromjenjivi podaci o svim okladama i vremenu kada su napravljene biti dostupni svim učesnicima.

Na Waves blockchainu, ponude se mogu zabilježiti u stanju aukcijskog računa putem DataTransaction-a.

Također možete postaviti vrijeme početka i završetka aukcije pomoću brojeva blokova: učestalost generiranja blokova u Waves blockchainu je približno jednaka 60 sekundi.

1. Aukcija uzlazne cijene na engleskom jeziku

Učesnici engleske aukcije daju ponude u međusobnoj konkurenciji. Svaka nova opklada mora biti veća od prethodne. Aukcija se završava kada nema više ponuđača koji bi premašili posljednju ponudu. U tom slučaju, ponuđač najviše ponude mora obezbijediti navedeni iznos.

Postoji i opcija aukcije u kojoj prodavac određuje minimalnu cijenu za lot, a konačna cijena mora biti veća. Inače, parcela ostaje neprodata.

U ovom primjeru radimo s računom posebno kreiranim za aukciju. Trajanje aukcije je 3000 blokova, a početna cijena lota je 0,001 TALAS. Učesnik može dati ponudu slanjem DataTransaction sa ključnom “cijenom” i vrijednošću svoje ponude.

Cijena nove ponude mora biti viša od trenutne cijene za ovaj ključ, a učesnik mora imati najmanje tokene [new_bid + Commission] na svom računu. Adresa ponuđača mora biti zabilježena u polju "pošiljalac" u transakciji podataka, a trenutna visina bloka ponuda mora biti unutar perioda aukcije.

Ako je na kraju aukcije učesnik odredio najvišu cijenu, može poslati berzansku transakciju da plati odgovarajući lot po navedenoj cijeni i valutnom paru.

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. Holandska aukcija pada cijena

Na holandskoj aukciji, mnogo se u početku nudi po cijeni većoj od one koju je kupac spreman platiti. Cijena se smanjuje korak po korak sve dok se jedan od učesnika ne složi da kupi parcelu po trenutnoj cijeni.

U ovom primjeru koristimo iste konstante kao u prethodnom, kao i korak cijene kada se delta smanjuje. Skripta naloga proverava da li je učesnik zaista prvi koji je stavio opkladu. U suprotnom, DataTransaction neće biti prihvaćen od strane blockchaina.

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. Aukcija “all-pay”

“All-pay” je aukcija u kojoj svi učesnici plaćaju ponudu, bez obzira na to ko je dobio lot. Svaki novi učesnik plaća ponudu, a učesnik koji da maksimalnu ponudu dobija lot.

U našem primjeru, svaki učesnik aukcije daje ponudu putem DataTransaction sa (ključ, vrijednost)* = (“pobjednik”, adresa),(“cijena”, cijena). Takva transakcija podataka je odobrena samo ako ovaj učesnik već ima Transakciju prenosa sa svojim potpisom i njegova ponuda je veća od svih prethodnih. Aukcija se nastavlja dok se ne dostigne endHeight.

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

Osiguranje / Crowdfunding

Razmotrimo situaciju u kojoj morate osigurati imovinu korisnika od finansijskih gubitaka. Na primjer, korisnik želi garanciju da će, ako token oslabi, moći vratiti puni iznos plaćen za te tokene i spreman je platiti razuman iznos osiguranja.

Da bi se ovo implementiralo, potrebno je izdati “tokene osiguranja”. Zatim se na račun osiguranika instalira skripta, koja omogućava izvršavanje samo onih ExchangeTransakcija koje ispunjavaju određene uslove.

Da biste spriječili dvostruku potrošnju, trebate zatražiti od korisnika da unaprijed pošalje DataTransaction na račun ugovaratelja osiguranja sa (ključ, vrijednost) = (purchaseTransactionId, sellOrderId) i zabraniti slanje DataTransactions s ključem koji je već korišten.

Stoga, dokazi korisnika moraju sadržavati ID transakcije kupovine tokena osiguranja. Valutni par mora biti isti kao u transakciji kupovine. Trošak također mora biti jednak onom koji je fiksiran u trenutku kupovine, umanjen za cijenu osiguranja.

Podrazumijeva se da naknadno račun osiguranja kupuje tokene osiguranja od korisnika po cijeni koja nije niža od one po kojoj ih je kupio: račun osiguranja kreira ExchangeTransaction, korisnik potpisuje nalog (ako je transakcija ispravno obavljena), račun osiguranja potpisuje drugu narudžbu i cijelu transakciju i šalje je u blockchain.

Ako ne dođe do kupovine, korisnik može kreirati ExchangeTransaction prema pravilima opisanim u skripti i poslati transakciju u blockchain. Na ovaj način korisnik može vratiti novac potrošen na kupovinu osiguranih tokena.

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 osiguranja se može učiniti pametnom imovinom, na primjer, kako bi se zabranio njegov prijenos trećim stranama.

Ova šema se može implementirati i za tokene za crowdfunding, koji se vraćaju vlasnicima ako se ne prikupi potreban iznos.

Porezi na transakcije

Pametni ugovori su također primjenjivi u slučajevima kada je potrebno naplatiti porez na svaku transakciju sa više vrsta imovine. Ovo se može uraditi preko novog sredstva sa instaliranim sponzorstvo za transakcije sa pametnom imovinom:

1. Izdajemo FeeCoin, koji će biti poslat korisnicima po fiksnoj cijeni: 0,01 WAVES = 0,001 FeeCoin.

2. Postavite sponzorstvo za FeeCoin i kurs: 0,001 WAVES = 0,001 FeeCoin.

3. Postavite sljedeću skriptu za pametno sredstvo:

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
}

Sada svaki put kada neko prenese N pametnih sredstava, dat će vam FeeCoin u iznosu od N/taxDivisor (koji se može kupiti od vas po 10 *N/taxDivisor WAVES), a vi ćete rudaru dati N/taxDivisor WAVES. Kao rezultat, vaš profit (porez) će biti 9*N / taxDivisor WAVES.

Također možete izvršiti oporezivanje koristeći skriptu pametne imovine i 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
}

Cashback i programi lojalnosti

Cashback je vrsta programa lojalnosti u kojem se kupcu vraća dio iznosa potrošenog na proizvod ili uslugu.

Prilikom implementacije ovog slučaja pomoću pametnog naloga, moramo provjeriti dokaze na isti način kao što smo to učinili u slučaju osiguranja. Da bi spriječio dvostruku potrošnju, korisnik mora poslati DataTransaction sa (ključ, vrijednost) = (purchaseTransactionId, cashbackTransactionId) prije nego što primi povrat novca.

Također moramo postaviti zabranu na postojeće ključeve koristeći DataTransaction. cashbackDivisor - jedinica podijeljena udjelom povrata novca. One. ako je udio povrata novca 0.1, tada je 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)
}

Atomska zamjena

Atomska zamjena omogućava korisnicima razmjenu sredstava bez pomoći razmjene. Kod atomske zamjene oba učesnika u transakciji su dužna da je potvrde u određenom vremenskom periodu.

Ako barem jedan od učesnika ne dostavi ispravnu potvrdu transakcije u vremenu predviđenom za transakciju, transakcija se poništava i do zamjene ne dolazi.

U našem primjeru koristit ćemo sljedeću skriptu pametnog računa:

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
}

U sljedećem članku ćemo pogledati upotrebu pametnih računa u finansijskim instrumentima kao što su opcije, fjučersi i mjenice.

izvor: www.habr.com

Dodajte komentar