Waves intelligens fiókok alkalmazása: az aukciótól a bónuszprogramokig

Waves intelligens fiókok alkalmazása: az aukciótól a bónuszprogramokig

A blokkláncot gyakran csak a kriptovalutákhoz kötik, de a DLT technológia alkalmazási területei sokkal szélesebbek. A blokklánc használatának egyik legígéretesebb területe az intelligens szerződés, amely automatikusan végrehajtásra kerül, és nem igényel bizalmat az azt megkötő felek között.

RIDE – az intelligens szerződések nyelve

A Waves kifejlesztett egy speciális nyelvet az intelligens szerződésekhez – a RIDE-et. A teljes dokumentáció megtalálható itt. És itt - cikk ebben a témában a Habr.

A RIDE szerződés egy predikátum, és „igaz” vagy „hamis” értéket ad vissza kimenetként. Ennek megfelelően a tranzakció vagy rögzítésre kerül a blokkláncban, vagy elutasításra kerül. Az okos szerződés teljes mértékben garantálja a meghatározott feltételek teljesítését. Tranzakciók generálása szerződésből a RIDE-ben jelenleg nem lehetséges.

Ma kétféle Waves intelligens szerződés létezik: intelligens fiókok és intelligens eszközök. Az intelligens fiók egy normál felhasználói fiók, de egy szkript van beállítva hozzá, amely minden tranzakciót vezérel. Egy intelligens fiók szkriptje például így nézhet ki:

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

A tx egy olyan tranzakció, amely feldolgozás alatt áll, és csak akkor engedélyezzük a mintaillesztési mechanizmus használatát, ha nem átviteli tranzakcióról van szó. A RIDE mintaillesztése a tranzakció típusának ellenőrzésére szolgál. Az összes meglévő fiók feldolgozható az intelligens fiókszkriptben tranzakciótípusok.

A szkript emellett deklarálhat változókat, használhat „if-then-else” konstrukciókat és egyéb módszereket a feltételek teljes ellenőrzéséhez. Annak biztosítására, hogy a szerződések bizonyítható teljessége és összetettsége (költsége) legyen, amely könnyen megjósolható a szerződés végrehajtásának megkezdése előtt, a RIDE nem tartalmaz ciklusokat vagy jump utasításokat.

A Waves-fiókok egyéb jellemzői közé tartozik egy „állapot”, azaz a fiók állapota. A számla állapotához végtelen számú pár (kulcs, érték) írható adattranzakciókkal (DataTransaction). Ezek az információk ezután a REST API-n keresztül és közvetlenül az intelligens szerződésben is feldolgozhatók.

Minden tranzakció tartalmazhat egy sor igazolást, amelybe beírható a résztvevő aláírása, a kívánt tranzakció azonosítója stb.

Munka a RIDE segítségével IDE lehetővé teszi a szerződés összeállított nézetének megtekintését (ha össze van állítva), új fiókokat hozhat létre és szkripteket állíthat be hozzá, valamint tranzakciókat küldhet a parancssoron keresztül.

Egy teljes ciklusban, beleértve a fiók létrehozását, az intelligens szerződés telepítését és a tranzakciók küldését, használhat egy könyvtárat a REST API-val való interakcióhoz (például C#, C, Java, JavaScript, Python, Rust, Elixir) . Az IDE-vel való munka megkezdéséhez kattintson az ÚJ gombra.

Az intelligens szerződések használatának lehetőségei szélesek: bizonyos címekre irányuló tranzakciók tiltásától („fekete lista”) az összetett dApp-okig.

Most nézzünk konkrét példákat az intelligens szerződések üzleti életben való használatára: aukciók lebonyolítása, biztosítás és hűségprogramok létrehozása során.

Aukciók

A sikeres aukció egyik feltétele az átláthatóság: a résztvevőknek biztosnak kell lenniük abban, hogy az ajánlatokat lehetetlen manipulálni. Ez a blokkláncnak köszönhetően érhető el, ahol az összes fogadásról és a fogadás időpontjáról változtathatatlan adatok állnak majd minden résztvevő rendelkezésére.

A Waves blokkláncon az ajánlatok aukciós fiók állapotában rögzíthetők a DataTransaction segítségével.

Az aukció kezdő és befejező időpontját blokkszámokkal is beállíthatja: a blokkgenerálás gyakorisága a Waves blokkláncban megközelítőleg megegyezik 60 másodpercig.

1. Angol növekvő árverés

Egy angol aukció résztvevői egymással versengve licitálnak. Minden új fogadásnak meg kell haladnia az előzőt. Az aukció akkor ér véget, ha már nincs több licitáló, aki meghaladja az utolsó ajánlatot. Ebben az esetben a legmagasabb ajánlatot tevőnek kell megadnia a feltüntetett összeget.

Létezik olyan aukciós lehetőség is, amelyben az eladó minimális árat határoz meg a tételre, és a végső árnak meg kell haladnia azt. Ellenkező esetben a tétel eladatlan marad.

Ebben a példában egy kifejezetten az aukcióhoz létrehozott fiókkal dolgozunk. Az aukció időtartama 3000 blokk, a tétel kikiáltási ára 0,001 WAVES. A résztvevő egy DataTransaction elküldésével tehet ajánlatot az „ár” kulcs és az ajánlata értékével.

Az új licit árának magasabbnak kell lennie a kulcs jelenlegi áránál, és a résztvevőnek legalább [új_licit + jutalék] tokennek kell lennie a fiókjában. Az Adattranzakcióban a „feladó” mezőben rögzíteni kell az ajánlattevő címét, és az aktuális ajánlati blokk magasságának az aukciós időszakon belül kell lennie.

Ha az aukció végén a résztvevő a legmagasabb árat állította be, akkor Tőzsdei Tranzakciót küldhet a megfelelő tétel kiegyenlítésére a megadott áron és devizapáron.

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. Csökkenő árak holland aukciója

A holland aukción kezdetben a tételt magasabb áron kínálják, mint amennyit a vevő hajlandó fizetni. Az ár lépésről lépésre csökken, amíg az egyik résztvevő beleegyezik, hogy az aktuális áron megvásárolja a tételt.

Ebben a példában ugyanazokat a konstansokat használjuk, mint az előzőben, valamint azt az árlépést, amikor a delta csökken. A számla szkript ellenőrzi, hogy a résztvevő valóban az első, aki fogad. Ellenkező esetben a DataTransactiont a blokklánc nem fogadja el.

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. „Minden fizetés” aukció

Az „all-pay” olyan aukció, amelyen minden résztvevő kifizeti az ajánlatot, függetlenül attól, hogy ki nyeri a tételt. Minden új résztvevő fizet egy licitet, és az a résztvevő nyer, aki a maximális licitet teszi.

Példánkban minden aukciós résztvevő a DataTransaction segítségével tesz ajánlatot a következővel: (kulcs, érték)* = ("nyertes", cím),("ár", ár). Egy ilyen AdatTranzakciót csak akkor hagynak jóvá, ha ez a résztvevő már rendelkezik aláírásával ellátott TransferTranzakcióval, és az ajánlata magasabb, mint az összes korábbi. Az aukció az endHeight eléréséig tart.

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

Biztosítás / Crowdfunding

Tekintsünk egy olyan helyzetet, amikor biztosítania kell a felhasználók vagyonát pénzügyi veszteségek ellen. Például egy felhasználó garanciát szeretne arra vonatkozóan, hogy ha egy token leértékelődik, vissza tudja kapni a tokenekért kifizetett teljes összeget, és hajlandó ésszerű összegű biztosítást fizetni.

Ennek megvalósításához „biztosítási tokeneket” kell kiállítani. Ezután egy szkript kerül telepítésre a kötvénytulajdonos számlájára, amely csak bizonyos feltételeknek megfelelő ExchangeTransactions végrehajtását teszi lehetővé.

A dupla költés elkerülése érdekében előzetesen fel kell kérni a felhasználót, hogy küldjön DataTransactiont a szerződő számlájára (kulcs, érték) = (purchaseTransactionId, sellOrderId) és meg kell tiltani az AdatTranzakciók küldését már használt kulccsal.

Ezért a felhasználó igazolásainak tartalmazniuk kell a biztosítási token vásárlás tranzakciós azonosítóját. A devizapárnak meg kell egyeznie a vásárlási tranzakcióban szereplővel. A költségnek meg kell egyeznie a vásárláskor rögzített költséggel, levonva a biztosítás árát.

Érthető, hogy ezt követően a biztosítási számla a biztosítási tokeneket nem alacsonyabb áron vásárolja meg a felhasználótól, mint amelyen azokat megvásárolta: a biztosítási számla csereTranzakciót hoz létre, a felhasználó aláírja a megbízást (ha a tranzakció helyesen fejeződött be), biztosítási számla aláírja a második megbízást és a teljes tranzakciót és elküldi a blokkláncnak.

Ha nem történik vásárlás, a felhasználó létrehozhat egy ExchangeTransaction-t a szkriptben leírt szabályok szerint, és elküldheti a tranzakciót a blokkláncnak. Így a felhasználó vissza tudja téríteni a biztosított tokenek vásárlására fordított pénzt.

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

A biztosítási token okos eszközzé tehető, például, hogy megtiltsa annak harmadik félnek való átadását.

Ez a konstrukció a közösségi finanszírozási tokenek esetében is megvalósítható, amelyeket visszakapnak a tulajdonosok, ha a szükséges összeget nem gyűjtik be.

Tranzakciós adók

Az intelligens szerződések olyan esetekben is alkalmazhatók, amikor minden egyes tranzakció után több típusú eszközzel kell adót beszedni. Ezt egy új, telepített eszközön keresztül lehet megtenni szponzoráció intelligens eszközökkel végzett tranzakciók esetén:

1. FeeCoint bocsátunk ki, amelyet rögzített áron küldünk a felhasználóknak: 0,01 WAVES = 0,001 FeeCoin.

2. Állítsa be a FeeCoin szponzorációját és az árfolyamot: 0,001 WAVES = 0,001 FeeCoin.

3. Állítsa be a következő szkriptet az intelligens eszközhöz:

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
}

Mostantól minden alkalommal, amikor valaki N intelligens eszközt ad át, FeeCoint ad N/taxDivisor összegben (amelyet 10 *N/taxDivisor WAVES áron vásárolhat meg), Ön pedig N/taxDivisor WAVES-t ad a bányásznak. Ennek eredményeként a nyereség (adó) 9*N / taxDivisor WAVES lesz.

Adózást is végrehajthat intelligens eszközszkript és MassTransferTransaction segítségével:

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 és hűségprogramok

A Cashback egy olyan hűségprogram, amelyben a vevő visszakapja a termékre vagy szolgáltatásra költött összeg egy részét.

Ennek az esetnek az intelligens fiókkal történő megvalósítása során ugyanúgy kell ellenőriznünk a bizonylatokat, mint a biztosítási ügyben. A dupla költés elkerülése érdekében a felhasználónak DataTransactiont kell küldenie a (kulcs, érték) = (purchaseTransactionId, cashbackTransactionId) értékkel, mielőtt pénzvisszatérítést kapna.

A DataTransactiont használó meglévő kulcsok tiltását is be kell állítanunk. cashbackDivisor – egység osztva a cashback részesedéssel. Azok. ha a pénzvisszatérítési részesedés 0.1, akkor 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)
}

Atomcsere

Az Atomic swap lehetővé teszi a felhasználók számára, hogy eszközöket cseréljenek csere nélkül. Az atomcsere esetén a tranzakció mindkét résztvevőjének meg kell erősítenie azt egy bizonyos időn belül.

Ha a résztvevők közül legalább az egyik nem ad megfelelő visszaigazolást a tranzakcióról a tranzakcióra kijelölt időn belül, a tranzakció törlésre kerül, és a csere nem történik meg.

Példánkban a következő intelligens fiók szkriptet fogjuk használni:

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
}

A következő cikkben megvizsgáljuk az intelligens számlák használatát olyan pénzügyi eszközökben, mint az opciók, határidős ügyletek és váltók.

Forrás: will.com

Hozzászólás