Toepassing van Waves-slimrekeninge: van veilings tot bonusprogramme

Toepassing van Waves-slimrekeninge: van veilings tot bonusprogramme

Blockchain word dikwels net met kripto-geldeenhede geassosieer, maar die toepassingsgebiede van DLT-tegnologie is baie wyer. Een van die mees belowende areas vir die gebruik van blokketting is 'n slim kontrak wat outomaties uitgevoer word en nie vertroue vereis tussen die partye wat dit aangegaan het nie.

RIDE – 'n taal vir slim kontrakte

Waves het 'n spesiale taal vir slim kontrakte ontwikkel - RIDE. Die volledige dokumentasie is gevind hier. En hier - artikel oor hierdie onderwerp op Habr.

Die RIDE-kontrak is 'n predikaat en gee "waar" of "onwaar" as uitvoer terug. Gevolglik word die transaksie óf in die blokketting aangeteken óf verwerp. Die slim kontrak waarborg ten volle die nakoming van gespesifiseerde voorwaardes. Die generering van transaksies uit 'n kontrak in RIDE is tans nie moontlik nie.

Vandag is daar twee tipes Waves slim kontrakte: slim rekeninge en slim bates. 'n Slimrekening is 'n gewone gebruikersrekening, maar 'n skrif is daarvoor opgestel wat alle transaksies beheer. 'n Slim rekeningskrif kan byvoorbeeld soos volg lyk:

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

tx is 'n transaksie wat verwerk word wat ons toelaat om die patroonpasmeganisme slegs te gebruik as dit nie 'n oordragtransaksie is nie. Patroonpassing in RIDE word gebruik om die tipe transaksie na te gaan. Alle bestaande rekeninge kan in die slimrekeningskrip verwerk word transaksie tipes.

Die skrif kan ook veranderlikes verklaar, "as-dan-anders"-konstrukte en ander metodes gebruik om toestande volledig te kontroleer. Om te verseker dat kontrakte bewysbare volledigheid en kompleksiteit (koste) het wat maklik is om te voorspel voordat kontrakuitvoering begin, bevat RIDE nie lusse of sprongstellings nie.

Ander kenmerke van Waves-rekeninge sluit in die teenwoordigheid van 'n "staat", dit wil sê die toestand van die rekening. Jy kan 'n oneindige aantal pare (sleutel, waarde) na die rekeningstaat skryf deur datatransaksies (DataTransaction) te gebruik. Hierdie inligting kan dan beide deur die REST API en direk in die slim kontrak verwerk word.

Elke transaksie kan 'n reeks bewyse bevat, waarin die handtekening van die deelnemer, die ID van die vereiste transaksie, ens. ingevoer kan word.

Werk met RIDE via IDE laat jou toe om die saamgestelde aansig van die kontrak te sien (indien dit saamgestel is), nuwe rekeninge te skep en skrifte daarvoor op te stel, asook transaksies via die opdragreël te stuur.

Vir 'n volledige siklus, insluitend die skep van 'n rekening, die installering van 'n slim kontrak daarop en die stuur van transaksies, kan jy ook 'n biblioteek gebruik vir interaksie met die REST API (byvoorbeeld C#, C, Java, JavaScript, Python, Rust, Elixir) . Om met die IDE te begin werk, klik net op die NUWE-knoppie.

Die moontlikhede om slim kontrakte te gebruik is wyd: van die verbod op transaksies tot sekere adresse ("swartlys") tot komplekse dApps.

Kom ons kyk nou na spesifieke voorbeelde van die gebruik van slim kontrakte in besigheid: wanneer veilings gehou word, versekering en die skep van lojaliteitsprogramme.

Veilings

Een van die voorwaardes vir 'n suksesvolle veiling is deursigtigheid: deelnemers moet vol vertroue wees dat dit onmoontlik is om bod te manipuleer. Dit kan bereik word danksy die blokketting, waar onveranderlike data oor alle weddenskappe en die tyd wanneer dit gemaak is vir alle deelnemers beskikbaar sal wees.

Op die Waves-blokketting kan bod via DataTransaction in die veilingrekeningstaat aangeteken word.

U kan ook die begin- en eindtyd van die veiling instel deur bloknommers te gebruik: die frekwensie van blokgenerering in die Waves-blokketting is ongeveer gelyk aan 60 sekondes.

1. Engelse stygende prysveiling

Deelnemers aan 'n Engelse veiling bied in kompetisie met mekaar. Elke nuwe weddenskap moet die vorige een oorskry. Die veiling eindig wanneer daar nie meer bieërs is om die laaste bod te oorskry nie. In hierdie geval moet die hoogste bieër die vermelde bedrag verskaf.

Daar is ook 'n veilingsopsie waarin die verkoper 'n minimum prys vir die lot vasstel, en die finale prys moet dit oorskry. Andersins bly die lot onverkoop.

In hierdie voorbeeld werk ons ​​met 'n rekening wat spesifiek vir die veiling geskep is. Die veiling duur is 3000 blokke, en die begin prys van die lot is 0,001 GOLWE. 'n Deelnemer kan 'n bod plaas deur 'n DataTransaksie met die sleutel "prys" en die waarde van hul bod te stuur.

Die prys van die nuwe bod moet hoër wees as die huidige prys vir hierdie sleutel, en die deelnemer moet ten minste [nuwe_bod + kommissie] tokens in sy rekening hê. Die bieër se adres moet in die "sender"-veld in die DataTransaction aangeteken word, en die huidige bodblokhoogte moet binne die veilingstydperk wees.

As die deelnemer aan die einde van die veiling die hoogste prys gestel het, kan hy 'n ExchangeTransaction stuur om vir die ooreenstemmende lot teen die gespesifiseerde prys en geldeenheidspaar te betaal.

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. Nederlandse veiling van dalende pryse

In 'n Nederlandse veiling word baie aanvanklik aangebied teen 'n prys wat hoër is as wat die koper bereid is om te betaal. Die prys word stap vir stap verlaag totdat een van die deelnemers instem om die lot teen die huidige prys te koop.

In hierdie voorbeeld gebruik ons ​​dieselfde konstantes as in die vorige een, sowel as die prysstap wanneer delta afneem. Die rekeningskrif kontroleer of die deelnemer wel die eerste is wat 'n weddenskap plaas. Andersins word die DataTransaction nie deur die blockchain aanvaar nie.

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. Veiling "alles betaal"

“All-pay” is 'n veiling waarin alle deelnemers die bod betaal, ongeag wie die lot wen. Elke nuwe deelnemer betaal 'n bod, en die deelnemer wat die maksimum bod maak, wen die lot.

In ons voorbeeld plaas elke veilingdeelnemer 'n bod via DataTransaction met (sleutel, waarde)* = (“wenner”, adres),(“prys”, prys). So 'n DataTransaksie word slegs goedgekeur as hierdie deelnemer reeds 'n Oordragtransaksie met sy handtekening het en sy bod hoër is as al die voriges. Die veiling duur voort totdat eindhoogte bereik is.

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

Versekering / skarefinansiering

Kom ons kyk na 'n situasie waar jy gebruikers se bates teen finansiële verliese moet verseker. Byvoorbeeld, 'n gebruiker wil 'n waarborg hê dat as 'n teken depresieer, hy die volle bedrag wat vir hierdie tokens betaal is, sal kan terugkry, en bereid is om 'n redelike bedrag versekering te betaal.

Om dit te implementeer, moet "versekeringsbewyse" uitgereik word. Dan word 'n skrip op die polishouer se rekening geïnstalleer, sodat slegs daardie ExchangeTransactions wat aan sekere voorwaardes voldoen, uitgevoer kan word.

Om dubbele besteding te voorkom, moet jy die gebruiker versoek om vooraf 'n DataTransaksie na die polishouer se rekening te stuur met (sleutel, waarde) = (purchaseTransactionId, sellOrderId) en verbied die stuur van DataTransactions met 'n sleutel wat reeds gebruik is.

Daarom moet die gebruiker se bewyse die transaksie-ID van die versekeringstekenaankoop bevat. Die geldeenheidspaar moet dieselfde wees as in die aankooptransaksie. Die koste moet ook gelyk wees aan dié wat ten tyde van aankoop vasgestel is, minus die prys van versekering.

Dit word verstaan ​​dat die versekeringsrekening daarna versekeringsbewyse van die gebruiker koop teen 'n prys wat nie laer is as die een waarteen hy dit gekoop het nie: die versekeringsrekening skep 'n ExchangeTransaction, die gebruiker onderteken die bestelling (indien die transaksie korrek afgehandel is), die versekeringsrekening teken die tweede bestelling en die hele transaksie en stuur dit na die blokketting.

As geen aankoop plaasvind nie, kan die gebruiker 'n ExchangeTransaction skep volgens die reëls wat in die skrif beskryf word en die transaksie na die blockchain stuur. Op hierdie manier kan die gebruiker die geld wat aan die aankoop van versekerde tokens bestee is, teruggee.

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

'n Versekeringsteken kan 'n slim bate gemaak word, byvoorbeeld om die oordrag daarvan aan derde partye te verbied.

Hierdie skema kan ook geïmplementeer word vir skarefinansiering-tokens, wat aan die eienaars terugbesorg word indien die vereiste bedrag nie ingevorder is nie.

Transaksiebelasting

Slim kontrakte is ook van toepassing in gevalle waar dit nodig is om belasting op elke transaksie met verskeie tipes bates in te vorder. Dit kan gedoen word deur 'n nuwe bate met geïnstalleer borgskap vir transaksies met slim bates:

1. Ons reik FeeCoin uit, wat teen 'n vaste prys aan gebruikers gestuur sal word: 0,01 GOLWE = 0,001 FeeCoin.

2. Stel borgskap vir FeeCoin en wisselkoers: 0,001 GOLWE = 0,001 FeeCoin.

3. Stel die volgende skrif vir die slim bate:

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
}

Elke keer as iemand nou N slim bates oordra, sal hulle vir jou FeeCoin gee in die bedrag van N/taxDivisor (wat by jou gekoop kan word teen 10 *N/taxDivisor WAVES), en jy sal die mynwerker N/taxDivisor WAVES gee. As gevolg hiervan, sal jou wins (belasting) 9*N / taxDivisor GOLWE wees.

U kan ook belasting uitvoer deur 'n slim bate-skrip en MassTransferTransaction te gebruik:

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
}

Kontantterug- en lojaliteitsprogramme

Kontantterug is 'n tipe lojaliteitsprogram waarin die koper 'n deel van die bedrag wat aan 'n produk of diens bestee is, terugkry.

Wanneer ons hierdie saak met 'n slim rekening implementeer, moet ons die bewyse op dieselfde manier nagaan as wat ons in die versekeringssaak gedoen het. Om dubbele besteding te voorkom, moet die gebruiker 'n DataTransaction met (sleutel, waarde) = (purchaseTransactionId, cashbackTransactionId) stuur voordat hy terugbetaling ontvang.

Ons moet ook 'n verbod stel op bestaande sleutels wat DataTransaction gebruik. cashbackDivisor - eenheid gedeel deur die cashback-aandeel. Dié. as die terugbetalingsaandeel 0.1 is, dan is kontantterugVerdeler 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)
}

Atoom ruil

Atoomruil laat gebruikers toe om bates te ruil sonder die hulp van 'n ruil. Met 'n atoomruiling word van beide deelnemers aan die transaksie vereis om dit binne 'n sekere tydperk te bevestig.

As ten minste een van die deelnemers nie die korrekte bevestiging van die transaksie verskaf binne die tyd wat vir die transaksie toegeken is nie, word die transaksie gekanselleer en vind die omruiling nie plaas nie.

In ons voorbeeld sal ons die volgende slim rekeningskrif gebruik:

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
}

In die volgende artikel sal ons kyk na die gebruik van slim rekeninge in finansiële instrumente soos opsies, termynkontrakte en wissels.

Bron: will.com

Voeg 'n opmerking