Uporaba pametnih računov Waves: od dražb do bonus programov

Uporaba pametnih računov Waves: od dražb do bonus programov

Blockchain pogosto povezujemo le s kriptovalutami, vendar so področja uporabe tehnologije DLT veliko širša. Eno najbolj obetavnih področij za uporabo blockchaina je pametna pogodba, ki se izvaja samodejno in ne zahteva zaupanja med strankami, ki so jo sklenile.

RIDE – jezik za pametne pogodbe

Waves je razvil poseben jezik za pametne pogodbe – RIDE. Celotna dokumentacija se nahaja tukaj. In tukaj - članek na to temo na Habru.

Pogodba RIDE je predikat in kot izhod vrne »true« ali »false«. V skladu s tem se transakcija zabeleži v verigi blokov ali zavrne. Pametna pogodba v celoti zagotavlja izpolnjevanje navedenih pogojev. Generiranje transakcij iz pogodbe v RIDE trenutno ni mogoče.

Danes obstajata dve vrsti pametnih pogodb Waves: pametni računi in pametna sredstva. Pametni račun je običajni uporabniški račun, vendar je zanj nastavljena skripta, ki nadzoruje vse transakcije. Skript pametnega računa je lahko na primer videti takole:

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

tx je transakcija v obdelavi, ki jo dovolimo z uporabo mehanizma za ujemanje vzorcev le, če ne gre za transakcijo prenosa. Ujemanje vzorcev v RIDE se uporablja za preverjanje vrste transakcije. Vse obstoječe račune je mogoče obdelati v skriptu pametnega računa vrste transakcij.

Skript lahko tudi deklarira spremenljivke, uporablja konstrukcije »if-then-else« in druge metode za popolno preverjanje pogojev. Da bi zagotovili, da imajo pogodbe dokazljivo popolnost in zapletenost (strošek), ki jo je enostavno predvideti, preden se začne izvajanje pogodbe, RIDE ne vsebuje zank ali stavkov o skokih.

Druge značilnosti računov Waves vključujejo prisotnost »stanja«, to je stanje računa. V stanje računa lahko s pomočjo podatkovnih transakcij (DataTransaction) zapišete neskončno število parov (ključ, vrednost). Te informacije je nato mogoče obdelati prek API-ja REST in neposredno v pametni pogodbi.

Vsaka transakcija lahko vsebuje niz dokazil, v katere se lahko vnese podpis udeleženca, ID zahtevane transakcije ipd.

Delo z RIDE prek IDE omogoča ogled sestavljenega pogleda pogodbe (če je sestavljena), ustvarjanje novih računov in nastavitev skriptov zanjo ter pošiljanje transakcij prek ukazne vrstice.

Za celoten cikel, vključno z ustvarjanjem računa, namestitvijo pametne pogodbe nanj in pošiljanjem transakcij, lahko uporabite tudi knjižnico za interakcijo z REST API (na primer C#, C, Java, JavaScript, Python, Rust, Elixir) . Če želite začeti delati z IDE, preprosto kliknite gumb NOVO.

Možnosti uporabe pametnih pogodb so široke: od prepovedi transakcij na določene naslove (»črna lista«) do kompleksnih dApps.

Zdaj pa poglejmo konkretne primere uporabe pametnih pogodb v poslu: pri izvajanju dražb, zavarovanju in ustvarjanju programov zvestobe.

Dražbe

Eden od pogojev za uspešno dražbo je transparentnost: udeleženci morajo biti prepričani, da je nemogoče manipulirati s ponudbami. To je mogoče doseči zahvaljujoč blockchainu, kjer bodo vsem udeležencem na voljo nespremenljivi podatki o vseh stavah in času, ko so bile sklenjene.

V verigi blokov Waves je mogoče ponudbe zabeležiti v stanju dražbenega računa prek DataTransaction.

Začetni in končni čas dražbe lahko nastavite tudi s številkami blokov: pogostost generiranja blokov v verigi blokov Waves je približno enaka 60 sekund.

1. Angleška dražba z naraščajočo ceno

Udeleženci angleške dražbe med seboj tekmujejo. Vsaka nova stava mora preseči prejšnjo. Dražba se zaključi, ko ni več dražiteljev, ki bi presegli zadnjo ponudbo. V tem primeru mora najvišji ponudnik zagotoviti navedeni znesek.

Obstaja tudi možnost dražbe, pri kateri prodajalec postavi minimalno ceno lota, končna cena pa jo mora preseči. V nasprotnem primeru parcela ostane neprodana.

V tem primeru delamo z računom, ustvarjenim posebej za dražbo. Trajanje dražbe je 3000 blokov, izklicna cena lota pa je 0,001 WAVES. Udeleženec lahko odda ponudbo tako, da pošlje DataTransaction s ključem "price" in vrednostjo svoje ponudbe.

Cena nove ponudbe mora biti višja od trenutne cene za ta ključ, udeleženec pa mora imeti na svojem računu vsaj žetone [nova_ponudba + provizija]. Naslov dražitelja mora biti zapisan v polju "pošiljatelj" v DataTransaction, trenutna višina bloka ponudb pa mora biti znotraj obdobja dražbe.

Če je udeleženec na koncu dražbe določil najvišjo ceno, lahko pošlje ExchangeTransaction za plačilo ustreznega sklopa po določeni ceni in valutnem 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 dražba padajočih cen

Na nizozemski dražbi je lot na začetku ponujen po ceni, ki je višja od tiste, ki jo je kupec pripravljen plačati. Cena se postopoma znižuje, dokler se eden od udeležencev ne strinja z nakupom parcele po trenutni ceni.

V tem primeru uporabljamo iste konstante kot v prejšnjem, pa tudi cenovni korak, ko se delta zmanjša. Skript računa preveri, ali je udeleženec res prvi sklenil stavo. V nasprotnem primeru veriga blokov ne sprejme DataTransaction.

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. Dražba "vse plačano"

»All-pay« je dražba, pri kateri vsi udeleženci plačajo ponudbo, ne glede na to, kdo zmaga. Vsak novi udeleženec plača ponudbo, žreb pa zmaga udeleženec, ki ponudi najvišjo vrednost.

V našem primeru vsak udeleženec dražbe odda ponudbo prek DataTransaction z (ključ, vrednost)* = (»zmagovalec«, naslov),(»cena«, cena). Taka DataTransaction je odobrena le, če ima ta udeleženec že TransferTransaction s svojim podpisom in je njegova ponudba višja od vseh prejšnjih. Dražba se nadaljuje, dokler ni dosežena 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)
}

Zavarovanje / množično financiranje

Poglejmo si situacijo, ko morate zavarovati sredstva uporabnikov pred finančnimi izgubami. Na primer, uporabnik želi jamstvo, da bo v primeru amortizacije žetona lahko dobil nazaj celoten znesek, plačan za te žetone, in je pripravljen plačati razumen znesek zavarovanja.

Za izvedbo tega je treba izdati "zavarovalne žetone". Nato se na račun zavarovanca namesti skript, ki omogoča izvedbo le tistih menjalnih transakcij, ki izpolnjujejo določene pogoje.

Da preprečite dvojno porabo, morate od uporabnika vnaprej zahtevati pošiljanje DataTransaction na račun zavarovanca s (ključ, vrednost) = (purchaseTransactionId, sellOrderId) in prepovedati pošiljanje DataTransaction z že uporabljenim ključem.

Zato morajo uporabnikova dokazila vsebovati ID transakcije nakupa zavarovalnega žetona. Valutni par mora biti enak kot pri nakupni transakciji. Tudi stroški morajo biti enaki tistim, določenim ob nakupu, zmanjšani za ceno zavarovanja.

Razume se, da nato zavarovalni račun od uporabnika kupi zavarovalne žetone po ceni, ki ni nižja od tiste, po kateri jih je kupil: zavarovalni račun ustvari ExchangeTransaction, uporabnik podpiše naročilo (če je transakcija pravilno zaključena), zavarovalni račun podpiše drugo naročilo in celotno transakcijo ter jo pošlje v blockchain .

Če do nakupa ne pride, lahko uporabnik ustvari ExchangeTransaction v skladu s pravili, opisanimi v skriptu, in pošlje transakcijo v verigo blokov. Tako lahko uporabnik vrne denar, porabljen za nakup zavarovanih žetonov.

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

Zavarovalni žeton lahko postane pametno sredstvo, na primer, da se prepove njegov prenos tretjim osebam.

Ta shema se lahko izvaja tudi za žetone množičnega financiranja, ki se vrnejo lastnikom, če zahtevani znesek ni bil zbran.

Transakcijski davki

Pametne pogodbe so uporabne tudi v primerih, ko je treba pobrati davek na vsako transakcijo z več vrstami sredstev. To je mogoče storiti z novim sredstvom z nameščenim sponzorstvo za transakcije s pametnimi sredstvi:

1. Izdajamo FeeCoin, ki bo poslan uporabnikom po fiksni ceni: 0,01 WAVES = 0,001 FeeCoin.

2. Nastavite sponzorstvo za FeeCoin in menjalni tečaj: 0,001 WAVES = 0,001 FeeCoin.

3. Nastavite naslednji skript 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
}

Zdaj vsakič, ko nekdo prenese N pametnih sredstev, vam bo dal FeeCoin v višini N/taxDivisor (ki ga je mogoče kupiti pri vas pri 10 *N/taxDivisor WAVES), vi pa boste rudarju dali N/taxDivisor WAVES. Posledično bo vaš dobiček (davek) 9*N / taxDivisor WAVES.

Obdavčitev lahko izvedete tudi z uporabo skripte pametnega sredstva in 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
}

Vračilo denarja in programi zvestobe

Cashback je vrsta programa zvestobe, v katerem kupec dobi nazaj del zneska, porabljenega za izdelek ali storitev.

Pri izvedbi tega primera s pametnim računom moramo dokazila preveriti na enak način, kot smo to storili pri zavarovalnem primeru. Da prepreči dvojno porabo, mora uporabnik poslati DataTransaction s (ključ, vrednost) = (purchaseTransactionId, cashbackTransactionId), preden prejme vračilo denarja.

Nastaviti moramo tudi prepoved obstoječih ključev, ki uporabljajo DataTransaction. cashbackDivisor - enota, deljena z deležem cashbacka. Tisti. če je delež denarnega vračila 0.1, potem 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 zamenjava

Atomska zamenjava omogoča uporabnikom izmenjavo sredstev brez pomoči menjave. Pri atomski zamenjavi morata oba udeleženca v transakciji to potrditi v določenem časovnem obdobju.

Če vsaj eden od udeležencev ne poda pravilne potrditve transakcije v času, določenem za transakcijo, se transakcija razveljavi in ​​menjava ne pride.

V našem primeru bomo uporabili naslednji skript pametnega 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
}

V naslednjem članku si bomo ogledali uporabo pametnih računov v finančnih instrumentih, kot so opcije, terminske pogodbe in menice.

Vir: www.habr.com

Dodaj komentar