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

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

Blockchain se često povezuje samo s kriptovalutama, no područja primjene DLT tehnologije mnogo su šira. Jedno od područja koja najviše obećavaju 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. Njegova kompletna dokumentacija se nalazi здесь. I ovdje - članak na ovu temu na Habru.

RIDE ugovor je predikat i vraća "true" ili "false" kao izlaz. Sukladno tome, transakcija se ili bilježi u blockchain ili odbija. Pametni ugovor u potpunosti jamči ispunjenje navedenih uvjeta. Generiranje transakcija iz ugovora u RIDE trenutno nije moguće.

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

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

tx je transakcija koja se obrađuje koju dopuštamo korištenjem mehanizma podudaranja uzorka samo ako se ne radi o transakciji prijenosa. Uparivanje uzorka u RIDE koristi se za provjeru vrste transakcije. Svi postojeći računi mogu se obraditi u skripti pametnog računa vrste transakcija.

Skripta također može deklarirati varijable, koristiti "if-then-else" konstrukcije i druge metode za potpunu provjeru uvjeta. Kako bi se osiguralo da ugovori imaju dokazivu cjelovitost i složenost (trošak) koje je lako predvidjeti prije početka izvršenja ugovora, RIDE ne sadrži petlje ili izjave o skoku.

Ostale značajke Waves računa uključuju prisutnost "stanja", odnosno stanja računa. Možete upisati beskonačan broj parova (ključ, vrijednost) u stanje računa koristeći podatkovne transakcije (DataTransaction). Te se informacije zatim mogu obraditi putem REST API-ja i izravno u pametnom ugovoru.

Svaka transakcija može sadržavati niz dokaza u koje se može unijeti potpis sudionika, ID tražene transakcije i sl.

Rad s RIDE putem IDE omogućuje vam pregled sastavljenog prikaza ugovora (ako je sastavljen), kreiranje novih računa i postavljanje skripti za njega, kao i slanje transakcija putem naredbenog retka.

Za cijeli ciklus, uključujući stvaranje računa, instaliranje pametnog ugovora na njega i slanje transakcija, također možete koristiti biblioteku za interakciju s REST API-jem (na primjer, C#, C, Java, JavaScript, Python, Rust, Elixir) . Za početak rada s IDE-om samo kliknite gumb NOVO.

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

Pogledajmo sada konkretne primjere korištenja pametnih ugovora u poslovanju: prilikom provođenja dražbi, osiguranja i kreiranja programa vjernosti.

Aukcije

Jedan od uvjeta za uspješnu aukciju je transparentnost: sudionici moraju biti sigurni da je nemoguće manipulirati 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 sudionicima.

Na Waves blockchainu ponude se mogu bilježiti u stanju aukcijskog računa putem DataTransaction.

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

1. Engleska aukcija uzlaznih cijena

Sudionici engleske dražbe daju ponude u konkurenciji jedni s drugima. Svaka nova oklada mora premašiti prethodnu. Dražba završava kada više nema ponuditelja koji premašuju posljednju ponudu. U tom slučaju, najviši ponuditelj mora osigurati navedeni iznos.

Postoji i opcija aukcije u kojoj prodavatelj postavlja minimalnu cijenu lota, a konačna cijena mora biti viša od nje. U protivnom, parcela ostaje neprodana.

U ovom primjeru radimo s računom koji je posebno kreiran za dražbu. Trajanje aukcije je 3000 blokova, a početna cijena lota je 0,001 WAVES. Sudionik može dati ponudu slanjem DataTransaction s ključem "cijena" i vrijednošću svoje ponude.

Cijena nove ponude mora biti viša od trenutne cijene za ovaj ključ, a sudionik mora imati najmanje [new_bid + provizija] tokena na svom računu. Adresa ponuditelja mora biti zabilježena u polju "pošiljatelj" u DataTransaction, a trenutna visina bloka ponude mora biti unutar razdoblja dražbe.

Ako je na kraju dražbe sudionik postavio najvišu cijenu, može poslati ExchangeTransaction za plaćanje odgovarajućeg lota 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. Nizozemska aukcija pada cijena

Na nizozemskoj aukciji, lot se u početku nudi po cijeni višoj od one koju je kupac spreman platiti. Cijena se smanjuje korak po korak sve dok jedan od sudionika ne pristane kupiti parcelu po trenutnoj cijeni.

U ovom primjeru koristimo iste konstante kao u prethodnom, kao i cjenovni korak kada se delta smanjuje. Skripta računa provjerava je li sudionik doista prvi stavio okladu. Inače, DataTransaction ne prihvaća 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. Aukcija "sve-pay"

“All-pay” je aukcija u kojoj svi sudionici plaćaju ponudu, bez obzira tko je osvojio lot. Svaki novi sudionik plaća ponudu, a sudionik koji napravi maksimalnu ponudu pobjeđuje na lutu.

U našem primjeru, svaki sudionik dražbe daje ponudu putem DataTransaction s (ključ, vrijednost)* = (“pobjednik”, adresa),(“cijena”, cijena). Takva DataTransaction je odobrena samo ako ovaj sudionik već ima TransferTransaction sa svojim potpisom i njegova ponuda je viša od svih prethodnih. Dražba se nastavlja dok se ne dosegne 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 trebate osigurati imovinu korisnika od financijskih gubitaka. Na primjer, korisnik želi jamstvo da će u slučaju deprecijacije tokena moći dobiti natrag puni iznos plaćen za te tokene i spreman je platiti razuman iznos osiguranja.

Da bi se to provelo, potrebno je izdati "žetone osiguranja". Zatim se na račun osiguranika instalira skripta koja dopušta izvršenje samo onih ExchangeTransakcija koje ispunjavaju određene uvjete.

Kako biste spriječili dvostruku potrošnju, potrebno je unaprijed zatražiti od korisnika slanje DataTransaction na račun osiguranika s (ključ, vrijednost) = (purchaseTransactionId, sellOrderId) i zabraniti slanje DataTransaction s ključem koji je već korišten.

Stoga, korisnički dokazi moraju sadržavati ID transakcije kupnje tokena osiguranja. Valutni par mora biti isti kao u transakciji kupnje. Trošak također mora biti jednak onom utvrđenom u trenutku kupnje, umanjen za cijenu osiguranja.

Podrazumijeva se da naknadno račun osiguranja kupuje žetone 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 izvršena), račun osiguranja potpisuje drugi nalog i cijelu transakciju te šalje u blockchain .

Ako se kupnja ne dogodi, korisnik može kreirati ExchangeTransaction prema pravilima opisanim u skripti i poslati transakciju u blockchain. Na taj način korisnik može vratiti novac utrošen za kupnju 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 može se učiniti pametnim sredstvom, na primjer, kako bi se zabranio njegov prijenos trećim stranama.

Ova se shema također može implementirati za crowdfunding tokene, koji se vraćaju vlasnicima ako se ne prikupi potreban iznos.

Porezi na transakcije

Pametni ugovori također su primjenjivi u slučajevima kada je potrebno naplatiti porez na svaku transakciju s nekoliko vrsta imovine. To se može učiniti putem novog sredstva s instaliranim sponzorstvo za transakcije s pametnom imovinom:

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

2. Postavite sponzorstvo za FeeCoin i tečaj: 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 netko prenese N pametnih sredstava, dat će vam FeeCoin u iznosu od N/taxDivisor (koji se može kupiti od vas za 10 *N/taxDivisor WAVES), a vi ćete dati rudaru N/taxDivisor WAVES. Kao rezultat toga, vaša će dobit (porez) biti 9*N / taxDivisor WAVES.

Također možete izvršiti oporezivanje pomoću skripte 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
}

Povrat novca i programi vjernosti

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

Prilikom provedbe ovog slučaja korištenjem pametnog računa, moramo provjeriti dokaze na isti način kao što smo to učinili u slučaju osiguranja. Kako bi spriječio dvostruku potrošnju, korisnik mora poslati DataTransaction s (ključ, vrijednost) = (purchaseTransactionId, cashbackTransactionId) prije nego što primi povrat novca.

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

Atomic swap omogućuje korisnicima razmjenu sredstava bez pomoći razmjene. Kod atomic swapa, oba sudionika u transakciji moraju je potvrditi u određenom vremenskom razdoblju.

Ako barem jedan od sudionika ne dostavi ispravnu potvrdu transakcije u roku predviđenom za transakciju, transakcija se poništava i razmjena 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 korištenje pametnih računa u financijskim instrumentima kao što su opcije, ročnice i mjenice.

Izvor: www.habr.com

Dodajte komentar