Примена Вавес паметних налога: од аукција до бонус програма

Примена Вавес паметних налога: од аукција до бонус програма

Блоцкцхаин се често повезује само са криптовалутама, али области примене ДЛТ технологије су много шире. Једна од области која највише обећава за коришћење блокчејна је паметни уговор који се извршава аутоматски и не захтева поверење између страна које су га склопиле.

РИДЕ – језик за паметне уговоре

Вавес је развио посебан језик за паметне уговоре – РИДЕ. Њена комплетна документација се налази овде. И овде - чланак на ову тему на Һабру.

РИДЕ уговор је предикат и враћа „тачно“ или „нетачно“ као излаз. Сходно томе, трансакција се или бележи у блок ланцу или одбија. Паметни уговор у потпуности гарантује испуњење наведених услова. Генерисање трансакција из уговора у РИДЕ тренутно није могуће.

Данас постоје две врсте Вавес паметних уговора: паметни налози и паметна средства. Паметни налог је обичан кориснички налог, али за њега је постављена скрипта која контролише све трансакције. Скрипта паметног налога може изгледати овако, на пример:

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

тк је трансакција која се обрађује и коју дозвољавамо коришћење механизма за подударање шаблона само ако није трансакција преноса. Упаривање шаблона у РИДЕ се користи за проверу типа трансакције. Сви постојећи налози могу се обрадити у скрипти паметног налога врсте трансакција.

Скрипта такође може да декларише променљиве, да користи конструкције „ако-онда-елсе” и друге методе за потпуну проверу услова. Да би се осигурало да уговори имају доказиву комплетност и сложеност (цену) коју је лако предвидети пре него што извршење уговора почне, РИДЕ не садржи наредбе петље или скока.

Остале карактеристике Вавес налога укључују присуство „стања“, односно стања налога. Можете уписати бесконачан број парова (кључ, вредност) у стање налога користећи трансакције података (ДатаТрансацтион). Ове информације се затим могу обрадити и преко РЕСТ АПИ-ја и директно у паметном уговору.

Свака трансакција може садржати низ доказа у које се може унети потпис учесника, ИД тражене трансакције итд.

Рад са РИДЕ преко ИДЕ омогућава вам да видите састављени приказ уговора (ако је састављен), креирате нове налоге и поставите скрипте за њега, као и да пошаљете трансакције преко командне линије.

За цео циклус, укључујући креирање налога, инсталирање паметног уговора на њега и слање трансакција, такође можете да користите библиотеку за интеракцију са РЕСТ АПИ-јем (на пример, Ц#, Ц, Јава, ЈаваСцрипт, Питхон, Руст, Еликир) . Да бисте почели да радите са ИДЕ, само кликните на дугме НОВО.

Могућности за коришћење паметних уговора су широке: од забране трансакција на одређене адресе („црна листа“) до сложених дАппс-а.

Хајде сада да погледамо конкретне примере употребе паметних уговора у пословању: приликом вођења аукција, осигурања и креирања програма лојалности.

Аукције

Један од услова за успешну аукцију је транспарентност: учесници морају бити уверени да је немогуће манипулисати понудама. Ово се може постићи захваљујући блокчејну, где ће непроменљиви подаци о свим опкладама и времену када су направљене бити доступни свим учесницима.

На Вавес блоцкцхаин-у, понуде се могу евидентирати у стању аукцијског налога преко ДатаТрансацтион-а.

Такође можете подесити време почетка и завршетка аукције користећи бројеве блокова: учесталост генерисања блокова у Вавес блоцкцхаину је приближно једнака 60 секунди.

1. Енглеска аукција узлазне цене

Учесници енглеске аукције дају понуде у међусобној конкуренцији. Свака нова опклада мора бити већа од претходне. Аукција се завршава када нема више понуђача који би премашили последњу понуду. У овом случају, највиша понуда мора да обезбеди наведени износ.

Постоји и опција аукције у којој продавац одређује минималну цену за лот, а коначна цена мора да је премаши. У супротном, парцела остаје непродата.

У овом примеру радимо са налогом посебно креираним за аукцију. Трајање аукције је 3000 блокова, а почетна цена лота је 0,001 ТАЛАС. Учесник може дати понуду тако што ће послати ДатаТрансацтион са кључном „ценом“ и вредношћу своје понуде.

Цена нове понуде мора бити виша од тренутне цене за овај кључ, а учесник мора имати најмање токене [нев_бид + Цоммиссион] на свом налогу. Адреса понуђача мора бити забележена у пољу „пошиљалац“ у ДатаТрансацтион, а тренутна висина блока понуда мора бити унутар периода аукције.

Ако је на крају аукције учесник одредио највишу цену, може послати берзанску трансакцију да плати одговарајући лот по наведеној цени и валутном пару.

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. Холандска аукција пада цена

На холандској аукцији, много се у почетку нуди по цени већој од оне коју је купац спреман да плати. Цена се смањује корак по корак све док један од учесника не пристане да купи парцелу по тренутној цени.

У овом примеру користимо исте константе као у претходном, као и корак цене када се делта смањује. Скрипта налога проверава да ли је учесник заиста први који је ставио опкладу. У супротном, блоцкцхаин не прихвата трансакцију података.

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. Аукција „алл-паи“

„Алл-паи“ је аукција у којој сви учесници плаћају понуду, без обзира на то ко је добио лот. Сваки нови учесник плаћа понуду, а учесник који да максималну понуду добија лот.

У нашем примеру, сваки учесник аукције даје понуду преко ДатаТрансацтион са (кључ, вредност)* = („победник“, адреса),(„цена“, цена). Таква ДатаТрансацтион је одобрена само ако овај учесник већ има ТрансферТрансакцију са својим потписом и његова понуда је већа од свих претходних. Аукција се наставља док се не достигне ендХеигхт.

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

Осигурање / Цровдфундинг

Хајде да размотримо ситуацију у којој треба да осигурате имовину корисника од финансијских губитака. На пример, корисник жели гаранцију да ће, ако токен ослаби, моћи да врати пун износ плаћен за ове токене, и спреман је да плати разуман износ осигурања.

Да би се ово спровело, потребно је издати „токене осигурања“. Затим се на налог власника полисе инсталира скрипта, која омогућава да се извршавају само оне ЕкцхангеТрансакције које испуњавају одређене услове.

Да бисте спречили двоструку потрошњу, потребно је да захтевате од корисника да унапред пошаље ДатаТрансацтион на налог уговарача полисе са (кључ, вредност) = (пурцхасеТрансацтионИд, селлОрдерИд) и забраните слање ДатаТрансацтионс са кључем који је већ коришћен.

Стога, докази корисника морају садржати ИД трансакције куповине токена осигурања. Валутни пар мора бити исти као у трансакцији куповине. Трошак такође мора бити једнак оном који је фиксиран у тренутку куповине, умањен за цену осигурања.

Подразумева се да накнадно налог осигурања купује токене осигурања од корисника по цени која није нижа од оне по којој их је купио: налог осигурања креира ЕкцхангеТрансакцију, корисник потписује налог (ако је трансакција правилно обављена), рачун осигурања потписује другу наруџбу и целу трансакцију и шаље је блокчејну.

Ако не дође до куповине, корисник може да креира ЕкцхангеТрансацтион у складу са правилима описаним у скрипти и пошаље трансакцију у блоцкцхаин. На овај начин корисник може да врати новац потрошен на куповину осигураних токена.

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

Токен осигурања се може учинити паметним средством, на пример, да би се забранио његов пренос трећим лицима.

Ова шема се такође може применити за токене за цровдфундинг, који се враћају власницима ако се не прикупи потребан износ.

Порези на трансакције

Паметни уговори су применљиви и у случајевима када је потребно наплатити порез на сваку трансакцију са више врста имовине. Ово се може урадити преко новог средства са инсталираним спонзорство за трансакције са паметним средствима:

1. Издајемо ФееЦоин, који ће бити послат корисницима по фиксној цени: 0,01 ВАВЕС = 0,001 ФееЦоин.

2. Подесите спонзорство за ФееЦоин и курс: 0,001 ВАВЕС = 0,001 ФееЦоин.

3. Подесите следећу скрипту за паметно средство:

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
}

Сада сваки пут када неко пренесе Н паметних средстава, даће вам ФееЦоин у износу од Н/такДивисор (који се од вас може купити по цени од 10 *Н/такДивисор ВАВЕС), а ви ћете рудару дати Н/такДивисор ВАВЕС. Као резултат, ваш профит (порез) ће бити 9*Н / такДивисор ВАВЕС.

Такође можете извршити опорезивање користећи скрипту паметне имовине и МассТрансферТрансацтион:

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
}

Цасхбацк и програми лојалности

Цасхбацк је врста програма лојалности у којем купцу враћају део износа потрошеног на производ или услугу.

Приликом имплементације овог случаја помоћу паметног налога, морамо проверити доказе на исти начин као што смо то урадили у случају осигурања. Да би спречио двоструку потрошњу, корисник мора да пошаље ДатаТрансацтион са (кључ, вредност) = (пурцхасеТрансацтионИд, цасхбацкТрансацтионИд) пре него што добије повраћај новца.

Такође морамо поставити забрану на постојеће кључеве користећи ДатаТрансацтион. цасхбацкДивисор - јединица подељена уделом повраћаја новца. Оне. ако је удео поврата новца 0.1, онда је цасхбацкДивисор 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)
}

Атомска замена

Атомска замена омогућава корисницима да размењују средства без помоћи размене. Код атомске размене, оба учесника у трансакцији су обавезна да је потврде у одређеном временском периоду.

Ако бар један од учесника не пружи тачну потврду трансакције у времену предвиђеном за трансакцију, трансакција се поништава и до размене не долази.

У нашем примеру користићемо следећу скрипту паметног налога:

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
}

У следећем чланку ћемо погледати употребу паметних налога у финансијским инструментима као што су опције, фјучерси и менице.

Извор: ввв.хабр.цом

Додај коментар