Applikaasjes fan Waves smart accounts: fan feilingen oant bonusprogramma's

Applikaasjes fan Waves smart accounts: fan feilingen oant bonusprogramma's

Blockchain wurdt faak allinich ferbûn mei Krypto-faluta, mar de gebieten fan tapassing fan DLT-technology binne folle breder. Ien fan 'e meast kânsrike gebieten foar it brûken fan blockchain is in tûk kontrakt dat automatysk wurdt útfierd en gjin fertrouwen nedich is tusken de partijen dy't it yngien hawwe.

RIDE - in taal foar tûke kontrakten

Waves hat in spesjale taal ûntwikkele foar tûke kontrakten - RIDE. De folsleine dokumintaasje is te finen hjir. En hjir - artikel oer dit ûnderwerp op Habr.

It RIDE-kontrakt is in predikaat en jout "wier" of "falsk" as útfier. Dêrtroch wurdt de transaksje of opnommen yn 'e blockchain of ôfwiisd. It tûke kontrakt garandearret folslein de ferfolling fan bepaalde betingsten. It generearjen fan transaksjes út in kontrakt yn RIDE is op it stuit net mooglik.

Tsjintwurdich binne d'r twa soarten Waves smart kontrakten: smart accounts en smart assets. In smart account is in gewoane brûkersaccount, mar der is in skript foar ynsteld dat alle transaksjes kontrolearret. In tûk accountskript kin der sa útsjen, bygelyks:

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

tx is in transaksje dy't wurdt ferwurke dat wy tastean it brûken fan it patroan oerienkommende meganisme allinnich as it is net in oerdracht transaksje. Pattern oerienkomst yn RIDE wurdt brûkt om it type transaksje te kontrolearjen. Alle besteande akkounts kinne wurde ferwurke yn it smart account skript transaksje typen.

It skript kin ek fariabelen ferklearje, brûke "as-dan-oars" konstruksjes en oare metoaden foar folslein kontrolearjen fan betingsten. Om te soargjen dat kontrakten bewiisbere folsleinens en kompleksiteit (kosten) hawwe dy't maklik te foarsizzen is foardat de útfiering fan kontrakten begjint, befettet RIDE gjin loops of sprong-útspraken.

Oare funksjes fan Waves-akkounts omfetsje de oanwêzigens fan in "steat", dat is de steat fan it akkount. Jo kinne in ûneinich oantal pearen (kaai, wearde) skriuwe nei de akkountstatus mei gegevenstransaksjes (DataTransaction). Dizze ynformaasje kin dan wurde ferwurke sawol fia de REST API as direkt yn it tûke kontrakt.

Elke transaksje kin in array fan bewiis befetsje, wêryn de hantekening fan 'e dielnimmer, de ID fan' e fereaske transaksje, ensfh.

Wurkje mei RIDE fia HERE kinne jo de gearstalde werjefte fan it kontrakt sjen (as it is kompilearre), nije akkounts oanmeitsje en skripts foar it ynstelle, en ek transaksjes ferstjoere fia de kommandorigel.

Foar in folsleine syklus, ynklusyf it meitsjen fan in akkount, it ynstallearjen fan in tûk kontrakt derop en it ferstjoeren fan transaksjes, kinne jo ek in bibleteek brûke foar ynteraksje mei de REST API (bygelyks C#, C, Java, JavaScript, Python, Rust, Elixir) . Om te begjinnen mei wurkjen mei de IDE, klik gewoan op de NIJE knop.

De mooglikheden foar it brûken fan tûke kontrakten binne breed: fan transaksjes ferbean nei bepaalde adressen ("swarte list") oant komplekse dApps.

Litte wy no nei spesifike foarbylden sjen fan it brûken fan tûke kontrakten yn bedriuw: by it fieren fan feilingen, fersekering en it meitsjen fan loyaliteitsprogramma's.

Auctions

Ien fan 'e betingsten foar in suksesfolle feiling is transparânsje: dielnimmers moatte der wis fan wêze dat it ûnmooglik is om biedingen te manipulearjen. Dit kin berikt wurde mei tank oan de blockchain, dêr't ûnferoarlike gegevens oer alle bets en de tiid doe't se waarden makke sil beskikber wêze foar alle dielnimmers.

Op 'e Waves blockchain kinne biedingen wurde opnommen yn' e steat fan 'e feilingkonto fia DataTransaction.

Jo kinne ek de start- en eintiid fan 'e feiling ynstelle mei bloknûmers: de frekwinsje fan blokgeneraasje yn' e Waves blockchain is sawat gelyk oan 60 sekonden.

1. Ingelske oprinnende priis feiling

Dielnimmers oan in Ingelske feiling biede yn konkurrinsje mei elkoar. Eltse nije bet moat boppe de foarige. De feiling einiget as d'r gjin bieders mear binne om it lêste bod te oersjen. Yn dit gefal moat de heechste bieder it opjûne bedrach leverje.

Der is ek in feiling opsje wêryn de ferkeaper stelt in minimum priis foar it lot, en de definitive priis moat boppe it. Oars bliuwt it lot net ferkocht.

Yn dit foarbyld wurkje wy mei in akkount spesifyk makke foar de feiling. De doer fan 'e feiling is 3000 blokken, en de startpriis fan it lot is 0,001 WAVES. In dielnimmer kin in bod pleatse troch in DataTransaction te stjoeren mei de kaai "priis" en de wearde fan har bod.

De priis fan it nije bod moat heger wêze as de hjoeddeistige priis foar dizze kaai, en de dielnimmer moat op syn minst [new_bid + kommisje] tokens yn syn akkount hawwe. It adres fan 'e bieder moat wurde opnommen yn it "ferstjoerder" fjild yn 'e DataTransaction, en de hjoeddeistige hichte fan 'e biedingsblok moat binnen de feilingperioade wêze.

As oan 'e ein fan' e feiling de dielnimmer de heechste priis ynsteld hat, kin hy in ExchangeTransaction stjoere om te beteljen foar it oerienkommende lot op 'e oantsjutte priis en muntpaar.

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. Nederlânske feiling fan ôfnimmende prizen

Yn in Nederlânske feiling wurdt ynearsten in soad oanbean foar in priis heger as wat de keaper ree is te beteljen. De priis wurdt stap foar stap ferlege oant ien fan 'e dielnimmers ynstimt om it lot te keapjen tsjin' e hjoeddeistige priis.

Yn dit foarbyld brûke wy deselde konstanten as yn 'e foarige, lykas de priisstap as delta ôfnimt. It akkount skript kontrolearret oft de dielnimmer is yndie de earste te pleatsen in weddenskip. Oars wurdt de DataTransaction net akseptearre troch de 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. Feiling "all-pay"

"All-pay" is in feiling wêryn alle dielnimmers it bod betelje, nettsjinsteande wa't it lot wint. Elke nije dielnimmer betellet in bod, en de dielnimmer dy't it maksimale bod makket, wint it lot.

Yn ús foarbyld pleatst elke feilingdielnimmer in bod fia DataTransaction mei (kaai, wearde) * = ("winner", adres), ("priis", priis). Sa'n DataTransaksje wurdt allinich goedkard as dizze dielnimmer al in TransferTransaksje hat mei syn hantekening en syn bod heger is as alle foargeande. De feiling giet troch oant einHichte wurdt berikt.

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

Fersekering / Crowdfunding

Litte wy in situaasje beskôgje wêr't jo fermogen fan brûkers moatte fersekerje tsjin finansjele ferliezen. Bygelyks, in brûker wol in garânsje dat as in token depreciates, hy sil by steat wêze om te krijen werom it folsleine bedrach betelle foar dizze tokens, en is ree om te beteljen in ridlik bedrach fan fersekering.

Om dit út te fieren, moatte "fersekeringstokens" wurde útjûn. Dan wurdt in skript ynstalleare op 'e akkount fan' e beliedhâlder, wêrtroch allinich de ExchangeTransactions kinne wurde útfierd dy't oan bepaalde betingsten foldwaan.

Om dûbele útjeften te foarkommen, moatte jo de brûker freegje om fan tefoaren in DataTransaction nei it akkount fan 'e polishâlder te stjoeren mei (kaai, wearde) = (purchaseTransactionId, sellOrderId) en ferbiede it ferstjoeren fan DataTransactions mei in kaai dy't al brûkt is.

Dêrom moatte de bewizen fan 'e brûker de transaksje-ID fan' e oankeap fan fersekeringstoken befetsje. It falutapaar moat itselde wêze as yn 'e oankeaptransaksje. De kosten moatte ek gelyk wêze oan dy fêst op it momint fan oankeap, minus de priis fan fersekering.

It wurdt begrepen dat it fersekeringsakkount dêrnei fersekeringstokens fan 'e brûker keapet foar in priis net leger as dejinge wêrop hy se kocht: it fersekeringsakkount makket in ExchangeTransaction, de brûker tekenet de bestelling (as de transaksje goed is foltôge), de fersekeringskonto tekenet de twadde oarder en de heule transaksje en stjoert it nei de blockchain.

As der gjin oankeap komt, kin de brûker in ExchangeTransaction oanmeitsje neffens de regels beskreaun yn it skript en stjoere de transaksje nei de blockchain. Op dizze manier kin de brûker it jild werombringe dat is bestege oan 'e oankeap fan fersekere tokens.

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

In fersekeringstoken kin in tûke asset wurde makke, bygelyks om syn oerdracht oan tredden te ferbieden.

Dit skema kin ek útfierd wurde foar crowdfunding tokens, dy't weromjûn wurde oan de eigners as it fereaske bedrach net sammele is.

Transaksje belestingen

Slimme kontrakten binne ek fan tapassing yn gefallen wêr't it nedich is om belesting te sammeljen op elke transaksje mei ferskate soarten aktiva. Dit kin dien wurde troch in nije asset mei ynstallearre sponsoring foar transaksjes mei tûke aktiva:

1. Wy útjaan FeeCoin, dat sil stjoerd wurde oan brûkers op in fêste priis: 0,01 WAVES = 0,001 FeeCoin.

2. Stel sponsoring foar FeeCoin en wikselkoers: 0,001 WAVES = 0,001 FeeCoin.

3. Stel it folgjende skript yn foar de smart asset:

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
}

No eltse kear as immen oerstappen N smart aktiva, se sille jou dy FeeCoin yn it bedrach fan N / taxDivisor (dat kin wurde kocht fan jo op 10 * N / taxDivisor WAVES), en jo sille jaan de miner N / taxDivisor WAVES. As gefolch, dyn winst (belesting) sil wêze 9 * N / taxDivisor WAVES.

Jo kinne ek belesting útfiere mei in tûk assetskript en 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 en loyaliteit programma

Cashback is in soarte fan loyaliteitsprogramma wêryn't de keaper in diel fan it bedrach werom krijt dat is bestege oan in produkt of tsjinst.

By it útfieren fan dizze saak mei in tûk akkount, moatte wy de bewizen kontrolearje op deselde manier as yn 'e fersekeringssaak. Om dûbele útjeften te foarkommen, moat de brûker in DataTransaction stjoere mei (kaai, wearde) = (purchaseTransactionId, cashbackTransactionId) foardat hy cashback ûntfangt.

Wy moatte ek in ferbod ynstelle op besteande kaaien mei DataTransaction. cashbackDivisor - ienheid dield troch de cashback oandiel. Dy. as it cashback-diel 0.1 is, dan 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)
}

Atoomruil

Atomic swap lit brûkers aktiva útwikselje sûnder help fan in útwikseling. Mei in atomêre swap binne beide dielnimmers oan 'e transaksje ferplicht om it binnen in bepaalde perioade te befêstigjen.

As op syn minst ien fan 'e dielnimmers gjin korrekte befêstiging fan' e transaksje leveret binnen de tiid dy't foar de transaksje tawiisd is, wurdt de transaksje annulearre en de útwikseling komt net foar.

Yn ús foarbyld sille wy it folgjende smart account skript brûke:

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
}

Yn it folgjende artikel sille wy sjen nei it gebrûk fan tûke akkounts yn finansjele ynstruminten lykas opsjes, futures en rekkens.

Boarne: www.habr.com

Add a comment