Aplikimet e llogarive inteligjente të Waves: nga ankandet në programet e bonusit

Aplikimet e llogarive inteligjente të Waves: nga ankandet në programet e bonusit

Blockchain shpesh lidhet vetëm me kriptovalutat, por fushat e aplikimit të teknologjisë DLT janë shumë më të gjera. Një nga fushat më premtuese për përdorimin e blockchain është një kontratë inteligjente që ekzekutohet automatikisht dhe nuk kërkon besim midis palëve që kanë hyrë në të.

RIDE – një gjuhë për kontratat inteligjente

Waves ka zhvilluar një gjuhë të veçantë për kontratat inteligjente - RIDE. Gjendet dokumentacioni i plotë i tij këtu. Dhe këtu - artikull mbi këtë temë në Habr.

Kontrata RIDE është një kallëzues dhe kthen "e vërtetë" ose "false" si rezultat. Prandaj, transaksioni ose regjistrohet në blockchain ose refuzohet. Kontrata inteligjente garanton plotësisht përmbushjen e kushteve të specifikuara. Gjenerimi i transaksioneve nga një kontratë në RIDE aktualisht nuk është i mundur.

Sot ekzistojnë dy lloje të kontratave inteligjente të Waves: llogaritë inteligjente dhe aktivet inteligjente. Një llogari inteligjente është një llogari e zakonshme përdoruesi, por për të është vendosur një skript që kontrollon të gjitha transaksionet. Një skrip i llogarisë inteligjente mund të duket kështu, për shembull:

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

tx është një transaksion që përpunohet dhe ne lejojmë përdorimin e mekanizmit të përputhjes së modelit vetëm nëse nuk është një transaksion transferimi. Përputhja e modelit në RIDE përdoret për të kontrolluar llojin e transaksionit. Të gjitha llogaritë ekzistuese mund të përpunohen në skriptin e llogarisë inteligjente llojet e transaksioneve.

Skripti gjithashtu mund të deklarojë variabla, të përdorë konstruksione "if-ather-else" dhe metoda të tjera për të kontrolluar plotësisht kushtet. Për të siguruar që kontratat të kenë plotësi dhe kompleksitet (kosto) të provueshme që është e lehtë të parashikohet përpara se të fillojë ekzekutimi i kontratës, RIDE nuk përmban unaza ose deklarata kërcimi.

Karakteristika të tjera të llogarive Waves përfshijnë praninë e një "gjendje", domethënë gjendjen e llogarisë. Mund të shkruani një numër të pafund çiftesh (çelës, vlerë) në gjendjen e llogarisë duke përdorur transaksionet e të dhënave (DataTransaction). Ky informacion më pas mund të përpunohet si përmes API-së REST ashtu edhe drejtpërdrejt në kontratën inteligjente.

Çdo transaksion mund të përmbajë një sërë provash, në të cilat mund të futet nënshkrimi i pjesëmarrësit, ID-ja e transaksionit të kërkuar, etj.

Duke punuar me RIDE nëpërmjet IDE ju lejon të shihni pamjen e përpiluar të kontratës (nëse është e përpiluar), të krijoni llogari të reja dhe të vendosni skripta për të, si dhe të dërgoni transaksione përmes linjës së komandës.

Për një cikël të plotë, duke përfshirë krijimin e një llogarie, instalimin e një kontrate inteligjente në të dhe dërgimin e transaksioneve, mund të përdorni gjithashtu një bibliotekë për të bashkëvepruar me REST API (për shembull, C#, C, Java, JavaScript, Python, Rust, Elixir) . Për të filluar punën me IDE, thjesht klikoni butonin NEW.

Mundësitë për përdorimin e kontratave inteligjente janë të gjera: nga ndalimi i transaksioneve në adresa të caktuara (“lista e zezë”) deri te dApps komplekse.

Tani le të shohim shembuj specifikë të përdorimit të kontratave inteligjente në biznes: gjatë kryerjes së ankandeve, sigurimit dhe krijimit të programeve të besnikërisë.

Ankandet

Një nga kushtet për një ankand të suksesshëm është transparenca: pjesëmarrësit duhet të jenë të sigurt se është e pamundur të manipulohen ofertat. Kjo mund të arrihet falë blockchain, ku të dhënat e pandryshueshme për të gjitha bastet dhe kohën kur janë bërë do të jenë të disponueshme për të gjithë pjesëmarrësit.

Në blockchain Waves, ofertat mund të regjistrohen në gjendjen e llogarisë së ankandit nëpërmjet DataTransaction.

Ju gjithashtu mund të vendosni kohën e fillimit dhe të përfundimit të ankandit duke përdorur numrat e bllokut: frekuenca e gjenerimit të bllokut në zinxhirin e valëve është afërsisht e barabartë me 60 sekonda.

1. Ankandi i çmimit në rritje në anglisht

Pjesëmarrësit në një ankand anglisht vendosin oferta në konkurrencë me njëri-tjetrin. Çdo bast i ri duhet të tejkalojë atë të mëparshëm. Ankandi përfundon kur nuk ka më ofertues për të tejkaluar ofertën e fundit. Në këtë rast, ofertuesi më i lartë duhet të sigurojë shumën e deklaruar.

Ekziston gjithashtu një opsion ankandi në të cilin shitësi vendos një çmim minimal për lotin, dhe çmimi përfundimtar duhet ta tejkalojë atë. Përndryshe, loti mbetet i pashitur.

Në këtë shembull, ne po punojmë me një llogari të krijuar posaçërisht për ankand. Kohëzgjatja e ankandit është 3000 blloqe, dhe çmimi fillestar i lotit është 0,001 VALËT. Një pjesëmarrës mund të bëjë një ofertë duke dërguar një DataTransaction me "çmimin" kyç dhe vlerën e ofertës së tyre.

Çmimi i ofertës së re duhet të jetë më i lartë se çmimi aktual për këtë çelës dhe pjesëmarrësi duhet të ketë të paktën shenja [new_bid + komision] në llogarinë e tij. Adresa e ofertuesit duhet të regjistrohet në fushën "dërguesi" në DataTransaction dhe lartësia aktuale e bllokut të ofertës duhet të jetë brenda periudhës së ankandit.

Nëse në fund të ankandit pjesëmarrësi ka vendosur çmimin më të lartë, ai mund të dërgojë një Transaksion Exchange për të paguar për lotin përkatës me çmimin e specifikuar dhe çiftin e monedhës.

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. Ankandi holandez i çmimeve në rënie

Në një ankand holandez, një shumë ofrohet fillimisht me një çmim më të lartë se ai që blerësi është i gatshëm të paguajë. Çmimi ulet hap pas hapi derisa njëri nga pjesëmarrësit të pranojë të blejë lotin me çmimin aktual.

Në këtë shembull ne përdorim të njëjtat konstante si në atë të mëparshmen, si dhe hapin e çmimit kur delta zvogëlohet. Skripti i llogarisë kontrollon nëse pjesëmarrësi është me të vërtetë i pari që vendos një bast. Përndryshe, DataTransaction nuk pranohet nga 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. Ankandi "të gjitha me pagesë"

"All-pay" është një ankand në të cilin të gjithë pjesëmarrësit paguajnë ofertën, pavarësisht se kush e fiton lotin. Çdo pjesëmarrës i ri paguan një ofertë dhe pjesëmarrësi që bën ofertën maksimale fiton shortin.

Në shembullin tonë, çdo pjesëmarrës në ankand vendos një ofertë nëpërmjet DataTransaction me (çelës, vlerë)* = (“fitues”, adresë), (“çmim”, çmim). Një transaksion i tillë i të dhënave miratohet vetëm nëse ky pjesëmarrës tashmë ka një TransferTransaksion me nënshkrimin e tij dhe oferta e tij është më e lartë se të gjitha të mëparshmet. Ankandi vazhdon derisa të arrihet Lartësia e fundit.

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

Sigurim / Crowdfunding

Le të shqyrtojmë një situatë ku ju duhet të siguroni asetet e përdoruesve kundër humbjeve financiare. Për shembull, një përdorues kërkon një garanci që nëse një token zhvlerësohet, ai do të jetë në gjendje të marrë mbrapsht shumën e plotë të paguar për këto token dhe është i gatshëm të paguajë një shumë të arsyeshme sigurimi.

Për ta zbatuar këtë, duhet të lëshohen "tokenat e sigurimit". Më pas një skript instalohet në llogarinë e mbajtësit të policës, duke lejuar që të ekzekutohen vetëm ato ExchangeTransactions që plotësojnë kushte të caktuara.

Për të parandaluar shpenzimet e dyfishta, duhet t'i kërkoni përdoruesit që të dërgojë një DataTransaction në llogarinë e mbajtësit të policës paraprakisht me (çelës, vlerë) = (purchaseTransactionId, sellOrderId) dhe të ndalojë dërgimin e DataTransactions me çelësin e përdorur tashmë.

Prandaj, provat e përdoruesit duhet të përmbajnë ID-në e transaksionit të blerjes së tokenit të sigurimit. Çifti i monedhës duhet të jetë i njëjtë si në transaksionin e blerjes. Kostoja gjithashtu duhet të jetë e barabartë me atë të fiksuar në momentin e blerjes, minus çmimin e sigurimit.

Kuptohet që më pas llogaria e sigurimit blen argumente sigurimi nga përdoruesi me një çmim jo më të ulët se ai me të cilin ai i bleu: llogaria e sigurimit krijon një Transaksion Exchange, përdoruesi nënshkruan porosinë (nëse transaksioni është kryer në mënyrë korrekte), llogaria e sigurimit nënshkruan porosinë e dytë dhe të gjithë transaksionin dhe e dërgon atë në blockchain.

Nëse nuk ndodh asnjë blerje, përdoruesi mund të krijojë një ExchangeTransaction sipas rregullave të përshkruara në skript dhe ta dërgojë transaksionin në blockchain. Në këtë mënyrë përdoruesi mund të kthejë paratë e shpenzuara për blerjen e tokeneve të siguruara.

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

Një shenjë sigurimi mund të bëhet një aktiv i zgjuar, për shembull, për të ndaluar transferimin e tij tek palët e treta.

Kjo skemë mund të zbatohet edhe për tokenat e crowdfunding, të cilët u kthehen pronarëve nëse shuma e kërkuar nuk është mbledhur.

Taksat e transaksionit

Kontratat inteligjente janë të zbatueshme edhe në rastet kur është e nevojshme të mblidhet taksa për çdo transaksion me disa lloje aktivesh. Kjo mund të bëhet përmes një aseti të ri me të instaluar sponsorizimi për transaksionet me aktive inteligjente:

1. Ne lëshojmë FeeCoin, i cili do t'u dërgohet përdoruesve me një çmim fiks: 0,01 VALËT = 0,001 FeeCoin.

2. Vendosni sponsorizimin për FeeCoin dhe kursin e këmbimit: 0,001 WAVE = 0,001 FeeCoin.

3. Vendosni skriptin e mëposhtëm për aktivin inteligjent:

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
}

Tani sa herë që dikush transferon N asete inteligjente, ata do t'ju japin FeeCoin në shumën e N/taxDivisor (i cili mund të blihet nga ju në 10 *N/taxDivisor WAVES), dhe ju do t'i jepni minatorit N/taxDivisor WAVES. Si rezultat, fitimi (taksa) juaj do të jetë 9*N / tatimDivisor WAVES.

Ju gjithashtu mund të kryeni tatimin duke përdorur një skript të aktiveve inteligjente dhe 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
}

Programet e kthimit të parave dhe besnikërisë

Cashback është një lloj programi besnikërie në të cilin blerësi merr mbrapsht një pjesë të shumës së shpenzuar për një produkt ose shërbim.

Kur zbatojmë këtë rast duke përdorur një llogari inteligjente, ne duhet të kontrollojmë provat në të njëjtën mënyrë si kemi bërë në rastin e sigurimit. Për të parandaluar shpenzimet e dyfishta, përdoruesi duhet të dërgojë një Transaksion të Dhënave me (çelës, vlerë) = (purchaseTransactionId, cashbackTransactionId) përpara se të marrë cashback.

Ne gjithashtu duhet të vendosim një ndalim për çelësat ekzistues duke përdorur DataTransaction. CashbackDivisor - njësi e ndarë me pjesën e kthimit të parave. Ato. nëse pjesa e kthimit të parave është 0.1, atëherë 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)
}

Shkëmbimi atomik

Shkëmbimi atomik i lejon përdoruesit të shkëmbejnë aktive pa ndihmën e një shkëmbimi. Me një shkëmbim atomik, të dy pjesëmarrësit në transaksion duhet ta konfirmojnë atë brenda një periudhe të caktuar kohore.

Nëse të paktën njëri nga pjesëmarrësit nuk jep konfirmimin e saktë të transaksionit brenda kohës së caktuar për transaksionin, transaksioni anulohet dhe shkëmbimi nuk ndodh.

Në shembullin tonë, ne do të përdorim skriptin e mëposhtëm të llogarisë inteligjente:

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
}

Në artikullin vijues do të shikojmë përdorimin e llogarive inteligjente në instrumente financiare si opsionet, kontratat e së ardhmes dhe faturat.

Burimi: www.habr.com

Shto një koment