Applicazione di cunti intelligenti Waves: da l'asta à i prugrammi bonus

Applicazione di cunti intelligenti Waves: da l'asta à i prugrammi bonus

Blockchain hè spessu assuciatu solu cù cripto muniti, ma i spazii di l'applicazione di a tecnulugia DLT sò assai più largu. Unu di i spazii più promettenti per l'usu di u blockchain hè un cuntrattu intelligente chì hè eseguitu automaticamente è ùn deve micca fiducia trà e partiti chì anu intrutu in questu.

RIDE - una lingua per i cuntratti intelligenti

Waves hà sviluppatu una lingua speciale per i cuntratti intelligenti - RIDE. A so documentazione cumpleta si trova ccà. È quì - articulu nantu à stu tema nantu à Habr.

U cuntrattu RIDE hè un predicatu è torna "true" o "false" cum'è output. Per quessa, a transazzione hè o arregistrata in u blockchain o rifiutata. U cuntrattu intelligente guarantisci cumplettamente u cumpletu di e cundizioni specificate. A generazione di transazzione da un cuntrattu in RIDE ùn hè micca pussibule.

Oghje ci sò dui tipi di cuntratti intelligenti di Waves: cunti intelligenti è asset intelligenti. Un contu intelligente hè un contu d'utilizatore regulare, ma un script hè stallatu per ellu chì cuntrolla tutte e transazzione. Un script di contu intelligente puderia vede cusì, per esempiu:

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

tx hè una transazzione trattata chì permettemu di utilizà u mecanismu di currispondenza di mudelli solu s'ellu ùn hè micca una transazzione di trasferimentu. Pattern matching in RIDE hè utilizatu per verificà u tipu di transazzione. Tutti i cunti esistenti ponu esse trattati in u script di contu intelligente tippi di transazzione.

U script pò ancu dichjarà variabili, utilizate custruzzioni "se-then-else" è altri metudi per cuntrollà cumplettamente e cundizioni. Per assicurà chì i cuntratti anu una cumplessità pruvata è cumplessità (costu) chì hè faciule da predichendu prima di l'esekzione di u cuntrattu principia, RIDE ùn cuntene micca loops o dichjarazioni di salto.

Altre caratteristiche di i cunti Waves includenu a prisenza di un "statu", vale à dì u statu di u contu. Pudete scrive un numeru infinitu di pariglii (chjavi, valore) à u statu di u contu utilizendu transazzione di dati (DataTransaction). Questa infurmazione pò esse trattata sia attraversu l'API REST sia direttamente in u cuntrattu intelligente.

Ogni transazzione pò cuntene una serie di prove, in quale a firma di u participante, l'ID di a transazzione necessaria, etc.

U travagliu cù RIDE via QUI permette di vede a vista cumpilata di u cuntrattu (se hè cumpilatu), creanu novi cunti è stabilisce scripts per questu, è ancu mandà transacciones via a linea di cummanda.

Per un ciclu sanu, cumprese a creazione di un contu, l'installazione di un cuntrattu intelligente è l'inviu di transazzione, pudete ancu aduprà una biblioteca per interagisce cù l'API REST (per esempiu, C#, C, Java, JavaScript, Python, Rust, Elixir) . Per cumincià à travaglià cù l'IDE, basta à cliccà u buttone NEW.

E pussibulità di usu di cuntratti intelligenti sò largu: da pruibisce transacciones à certi indirizzi ("lista nera") à dApps cumplessi.

Avà fighjemu l'esempii specifichi di l'usu di cuntratti intelligenti in l'affari: quandu si facenu l'asta, l'assicuranza, è a creazione di prugrammi di fidelizazione.

L'asta

Una di e cundizioni per una subasta riescita hè a trasparenza: i participanti devenu esse cunfidenti chì hè impussibile di manipulà offerte. Questu pò esse ottenutu grazia à u blockchain, induve e dati immutabili nantu à tutte e scumesse è u tempu quandu sò stati fatti seranu dispunibili per tutti i participanti.

Nantu à u blockchain Waves, l'offerte ponu esse registrate in u statu di u contu di l'asta via DataTransaction.

Pudete ancu stabilisce l'ora di iniziu è di fine di l'asta usendu numeri di bloccu: a freccia di generazione di blocchi in u blockchain Waves hè apprussimatamente uguale à 60 seconde.

1. Auction English ascending price

I participanti in una subasta inglesa ponenu offerte in cuncurrenza cù l'altri. Ogni nova scumessa deve superà a precedente. L'asta finisce quandu ùn ci hè più offerenti per superà l'ultima offerta. In questu casu, u più altu offerente deve furnisce l'ammontu indicatu.

Ci hè ancu una opzione d'asta in quale u vinditore stabilisce un prezzu minimu per u lottu, è u prezzu finali deve esse sopra. Altrimenti, u lottu resta invendutu.

In questu esempiu, avemu travagliatu cù un contu creatu specificamente per l'asta. A durata di l'asta hè di 3000 blocchi, è u prezzu di partenza di u lottu hè 0,001 WAVES. Un participante pò fà una offerta mandendu una DataTransaction cù a chjave "prezzu" è u valore di a so offerta.

U prezzu di a nova offerta deve esse più altu ch'è u prezzu attuale per questa chjave, è u participante deve avè almenu [new_bid + commission] tokens in u so contu. L'indirizzu di l'offerente deve esse registratu in u campu "mittente" in DataTransaction, è l'altezza attuale di u bloccu di offerta deve esse in u periodu di l'asta.

Se à a fine di l'asta, u participante hà stabilitu u prezzu più altu, pò mandà una Transazzione di Scambio per pagà u lottu currispundente à u prezzu specificatu è a coppia di valuta.

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. Auction Olandese di i prezzi diminuenti

In una subasta Olandese, assai hè inizialmente offertu à un prezzu più altu di ciò chì u cumpratore hè dispostu à pagà. U prezzu diminuisce passu à passu finu à chì unu di i participanti accetta di cumprà u lottu à u prezzu attuale.

In questu esempiu usemu i stessi custanti cum'è in u precedente, è ancu u passu di prezzu quandu delta diminuisce. U script di u contu verifica se u participante hè veramente u primu à fà una scumessa. Altrimenti, a DataTransaction ùn hè micca accettata da u 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. Asta "all-pay"

"All-pay" hè una subasta in quale tutti i participanti paganu l'offerta, indipendentemente da quale vince u lottu. Ogni novu participante paga una offerta, è u participante chì face l'offerta massima vince u lottu.

In u nostru esempiu, ogni participante à l'asta mette una offerta via DataTransaction cù (chjave, valore) * = ("vincitore", indirizzu), ("prezzu", prezzu). Un tali DataTransaction hè appruvatu solu se stu participante hà digià un TransferTransaction cù a so firma è a so offerta hè più altu ch'è tutti i precedenti. L'asta cuntinueghja finu à a fine Height hè ghjunta.

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

Assicuranza / Crowdfunding

Cunsideremu una situazione induve avete bisognu di assicurà l'assi di l'utilizatori contr'à perdite finanziarie. Per esempiu, un utilizatore vole una guaranzia chì, se un token si deprezza, puderà ritruvà a quantità tutale pagata per questi tokens, è hè dispostu à pagà una quantità raghjone d'assicuranza.

Per implementà questu, "tokens d'assicuranza" deve esse emessu. Allora un script hè stallatu nantu à u contu di l'assicuratore, chì permette solu quelli ExchangeTransactions chì scontranu certi cundizioni per esse eseguitu.

Per prevene a doppia spesa, avete bisognu di dumandà à l'utilizatore per mandà una DataTransaction à u contu di l'assicuratore in anticipu cù (key, value) = (purchaseTransactionId, sellOrderId) è pruibisce di mandà DataTransactions cù una chjave chì hè digià stata utilizata.

Dunque, e prove di l'utilizatori devenu cuntene l'ID di transazzione di l'acquistu di token d'assicuranza. A coppia di valute deve esse uguali à a transazzione di compra. U costu deve ancu esse uguali à quellu fissatu à u mumentu di a compra, minus u prezzu di l'assicuranza.

Hè intesu chì in seguitu u cuntu d'assicuranza compra tokens d'assicuranza da l'utilizatore à un prezzu micca più bassu di quellu à quale l'hà acquistatu: u cuntu d'assicuranza crea una Transazzione di Scambio, l'utilizatore firma l'ordine (se a transazzione hè cumpleta bè), u contu d'assicuranza firma u sicondu ordine è tutta a transazzione è u manda à u blockchain .

Se ùn ci hè micca una compra, l'utilizatore pò creà un ExchangeTransaction secondu e regule descritte in u script è mandà a transazzione à u blockchain. In questu modu, l'utilizatore pò rinvià i soldi spesi in a compra di tokens assicurati.

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

Un token d'assicuranza pò esse fattu un attivu intelligente, per esempiu, per pruibisce u so trasferimentu à terzu.

Stu schema pò ancu esse implementatu per i tokens di crowdfunding, chì sò tornati à i pruprietarii se a quantità necessaria ùn hè micca stata cullata.

Tasse di transazzione

I cuntratti intelligenti sò ancu applicabili in i casi induve hè necessariu di cullà l'impositu nantu à ogni transazzione cù parechji tipi di assi. Questu pò esse fattu attraversu un novu attivu cù installatu sponsorizazione per transazzione cù l'assi intelligenti:

1. Emettemu FeeCoin, chì serà mandatu à l'utilizatori à un prezzu fissu: 0,01 WAVES = 0,001 FeeCoin.

2. Stabbilisce u sponsorizazione per FeeCoin è u scambiu: 0,001 WAVES = 0,001 FeeCoin.

3. Stabilite u script seguente per l'asset smart:

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
}

Avà ogni volta chì qualchissia trasferimentu N assi intelligenti, vi darà FeeCoin in a quantità di N/taxDivisor (chì pò esse acquistatu da voi à 10 *N/taxDivisor WAVES), è vi darà u minatore N/taxDivisor WAVES. In u risultatu, u vostru prufittu (tassa) serà 9*N / taxDivisor WAVES.

Pudete ancu eseguisce a tassazione cù un script di asset intelligente è 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 è prugrammi di fidelizazione

Cashback hè un tipu di prugramma di lealtà in u quale u cumpratore torna una parte di a quantità spesa in un pruduttu o serviziu.

Quandu l'implementazione di stu casu utilizendu un contu intelligente, duvemu verificà e prove in u listessu modu chì avemu fattu in u casu d'assicuranza. Per prevene a doppia spesa, l'utilizatore deve mandà una DataTransaction cù (key, value) = (purchaseTransactionId, cashbackTransactionId) prima di riceve cashback.

Avemu da ancu stabilisce una prohibizione di e chjave esistenti cù DataTransaction. cashbackDivisor - unità divisa da a parte di cashback. Quelli. se a parte di cashback hè 0.1, allora 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)
}

Scambiu atomicu

U scambiu atomicu permette à l'utilizatori di scambià l'assi senza l'aiutu di un scambiu. Cù un swap atomicu, i dui participanti in a transazzione sò tenuti à cunfirmà in un certu periodu di tempu.

Se almenu unu di i participanti ùn furnisce micca a cunferma curretta di a transazzione in u tempu attribuitu per a transazzione, a transazzione hè annullata è u scambiu ùn si faci micca.

In u nostru esempiu, useremu u seguente script di contu intelligente:

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 u prossimu articulu avemu da circà à l'usu di cunti intelligenti in strumenti finanziarii cum'è opzioni, futures è bills.

Source: www.habr.com

Add a comment