تطبيق حسابات Waves الذكية: من المزادات إلى برامج المكافآت

تطبيق حسابات Waves الذكية: من المزادات إلى برامج المكافآت

غالبًا ما ترتبط تقنية Blockchain بالعملات المشفرة فقط، ولكن مجالات تطبيق تقنية DLT أوسع بكثير. أحد المجالات الواعدة لاستخدام تقنية blockchain هو العقد الذكي الذي يتم تنفيذه تلقائيًا ولا يتطلب الثقة بين الأطراف التي أبرمته.

RIDE – لغة العقود الذكية

قامت شركة Waves بتطوير لغة خاصة للعقود الذكية – RIDE. يقع وثائقها الكاملة هنا. و هنا - مقالة حول هذا الموضوع على هبر.

عقد RIDE هو مسند ويعيد "صحيح" أو "خطأ" كمخرجات. وفقًا لذلك، يتم تسجيل المعاملة في blockchain أو رفضها. يضمن العقد الذكي بشكل كامل استيفاء الشروط المحددة. إن إنشاء معاملات من عقد في RIDE غير ممكن حاليًا.

يوجد اليوم نوعان من عقود Waves الذكية: الحسابات الذكية والأصول الذكية. الحساب الذكي هو حساب مستخدم عادي، ولكن تم إعداد برنامج نصي له للتحكم في جميع المعاملات. قد يبدو البرنامج النصي للحساب الذكي بهذا الشكل، على سبيل المثال:

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

tx هي معاملة تتم معالجتها ونسمح باستخدام آلية مطابقة النمط فقط إذا لم تكن معاملة تحويل. يتم استخدام مطابقة الأنماط في RIDE للتحقق من نوع المعاملة. يمكن معالجة كافة الحسابات الموجودة في البرنامج النصي للحساب الذكي أنواع المعاملات.

يمكن للبرنامج النصي أيضًا الإعلان عن المتغيرات واستخدام بنيات "if-then-else" وطرق أخرى للتحقق من الشروط بشكل كامل. للتأكد من اكتمال العقود وتعقيدها (التكلفة) الذي يسهل التنبؤ به قبل بدء تنفيذ العقد، لا يحتوي RIDE على حلقات أو بيانات انتقالية.

تتضمن الميزات الأخرى لحسابات Waves وجود "حالة"، أي حالة الحساب. يمكنك كتابة عدد لا نهائي من الأزواج (المفتاح، القيمة) لحالة الحساب باستخدام معاملات البيانات (DataTransaction). يمكن بعد ذلك معالجة هذه المعلومات من خلال REST API ومباشرة في العقد الذكي.

يمكن أن تحتوي كل معاملة على مجموعة من الأدلة، حيث يمكن إدخال توقيع المشارك ومعرف المعاملة المطلوبة وما إلى ذلك.

العمل مع RIDE عبر IDE يسمح لك برؤية العرض المجمع للعقد (إذا تم تجميعه)، وإنشاء حسابات جديدة وتعيين البرامج النصية له، بالإضافة إلى إرسال المعاملات عبر سطر الأوامر.

للحصول على دورة كاملة، بما في ذلك إنشاء حساب وتثبيت عقد ذكي عليه وإرسال المعاملات، يمكنك أيضًا استخدام مكتبة للتفاعل مع REST API (على سبيل المثال، C#، C، Java، JavaScript، Python، Rust، Elixir) . لبدء العمل مع IDE، فقط انقر فوق الزر جديد.

إمكانيات استخدام العقود الذكية واسعة: من حظر المعاملات إلى عناوين معينة ("القائمة السوداء") إلى التطبيقات اللامركزية المعقدة.

الآن دعونا نلقي نظرة على أمثلة محددة لاستخدام العقود الذكية في الأعمال التجارية: عند إجراء المزادات والتأمين وإنشاء برامج الولاء.

المزادات

أحد شروط نجاح المزاد هو الشفافية: يجب أن يكون المشاركون على ثقة من أنه من المستحيل التلاعب بالعطاءات. يمكن تحقيق ذلك بفضل blockchain، حيث ستكون البيانات غير القابلة للتغيير حول جميع الرهانات والوقت الذي تم إجراؤها متاحة لجميع المشاركين.

في Waves blockchain، يمكن تسجيل العطاءات في حالة حساب المزاد عبر DataTransaction.

يمكنك أيضًا ضبط وقت بداية ونهاية المزاد باستخدام أرقام الكتل: إن تكرار إنشاء الكتلة في Waves blockchain يساوي تقريبًا 60 ثواني.

1. مزاد الأسعار التصاعدية باللغة الإنجليزية

يقوم المشاركون في مزاد باللغة الإنجليزية بوضع العطاءات في منافسة مع بعضهم البعض. يجب أن يتجاوز كل رهان جديد الرهان السابق. ينتهي المزاد عندما لا يكون هناك المزيد من مقدمي العروض لتجاوز العرض الأخير. وفي هذه الحالة، يجب على مقدم أعلى عطاء تقديم المبلغ المذكور.

يوجد أيضًا خيار المزاد الذي يحدد فيه البائع الحد الأدنى لسعر القطعة، ويجب أن يتجاوزه السعر النهائي. وبخلاف ذلك، تبقى الكمية غير مباعة.

في هذا المثال، نحن نعمل مع حساب تم إنشاؤه خصيصًا للمزاد. مدة المزاد هي 3000 قطعة، والسعر المبدئي للوط هو 0,001 WAVES. يمكن للمشارك تقديم عرض عن طريق إرسال DataTransaction مع مفتاح "السعر" وقيمة عرضه.

يجب أن يكون سعر العرض الجديد أعلى من السعر الحالي لهذا المفتاح، ويجب أن يكون لدى المشارك على الأقل رموز [new_bid + Commission] في حسابه. يجب تسجيل عنوان مقدم العرض في حقل "المرسل" في DataTransaction، ويجب أن يكون ارتفاع كتلة العطاء الحالي خلال فترة المزاد.

إذا قام المشارك في نهاية المزاد بتحديد أعلى سعر، فيمكنه إرسال ExchangeTransaction لدفع ثمن الكمية المقابلة بالسعر المحدد وزوج العملات.

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. المزاد الهولندي للأسعار المتناقصة

في المزاد الهولندي، يتم عرض الكثير في البداية بسعر أعلى مما يرغب المشتري في دفعه. ينخفض ​​السعر خطوة بخطوة حتى يوافق أحد المشاركين على شراء الكمية بالسعر الحالي.

في هذا المثال نستخدم نفس الثوابت كما في المثال السابق، وكذلك خطوة السعر عندما تنخفض الدلتا. يتحقق البرنامج النصي للحساب مما إذا كان المشارك هو بالفعل أول من وضع رهانًا. وبخلاف ذلك، لن يتم قبول DataTransaction بواسطة 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. مزاد "الدفع بالكامل"

"الدفع الشامل" هو مزاد يدفع فيه جميع المشاركين المزايدة، بغض النظر عمن يفوز بالقرعة. يدفع كل مشارك جديد عرضًا، والمشارك الذي يقدم الحد الأقصى للمزايدة يفوز بالقرعة.

في مثالنا، يقدم كل مشارك في المزاد عرضًا عبر DataTransaction باستخدام (المفتاح، القيمة)* = (“الفائز”، العنوان)،(“السعر”، السعر). تتم الموافقة على معاملة البيانات هذه فقط إذا كان لدى هذا المشارك بالفعل معاملة نقل بتوقيعه وكان عرضه أعلى من جميع العروض السابقة. يستمر المزاد حتى الوصول إلى endHeight.

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

التأمين / التمويل الجماعي

دعونا نفكر في الموقف الذي تحتاج فيه إلى تأمين أصول المستخدمين ضد الخسائر المالية. على سبيل المثال، يريد المستخدم ضمانًا بأنه في حالة انخفاض قيمة الرمز المميز، فسيكون قادرًا على استرداد المبلغ المدفوع بالكامل مقابل هذه الرموز المميزة، ويكون على استعداد لدفع مبلغ معقول من التأمين.

لتنفيذ ذلك، يجب إصدار "رموز التأمين". ثم يتم تثبيت برنامج نصي على حساب حامل البوليصة، مما يسمح فقط بتنفيذ معاملات Exchange التي تستوفي شروطًا معينة.

لمنع الإنفاق المزدوج، تحتاج إلى مطالبة المستخدم بإرسال DataTransactions إلى حساب صاحب البوليصة مقدمًا باستخدام (مفتاح، قيمة) = (purchaseTransactionId، SellOrderId) وحظر إرسال DataTransactions بمفتاح تم استخدامه بالفعل.

ولذلك، يجب أن تحتوي إثباتات المستخدم على معرف المعاملة لشراء رمز التأمين المميز. يجب أن يكون زوج العملات هو نفسه الموجود في معاملة الشراء. ويجب أن تكون التكلفة أيضًا مساوية للتكلفة المحددة في وقت الشراء، مطروحًا منها سعر التأمين.

من المفهوم أنه بعد ذلك يشتري حساب التأمين رموز التأمين من المستخدم بسعر لا يقل عن السعر الذي اشتراها به: يقوم حساب التأمين بإنشاء معاملة تبادل، ويوقع المستخدم الطلب (إذا تم إكمال المعاملة بشكل صحيح)، يقوم حساب التأمين بتوقيع الطلب الثاني والمعاملة بأكملها ويرسلها إلى blockchain.

في حالة عدم حدوث أي عملية شراء، يمكن للمستخدم إنشاء معاملة Exchange وفقًا للقواعد الموضحة في البرنامج النصي وإرسال المعاملة إلى blockchain. بهذه الطريقة يمكن للمستخدم إعادة الأموال التي أنفقها على شراء الرموز المؤمنة.

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. نقوم بإصدار FeeCoin، والتي سيتم إرسالها إلى المستخدمين بسعر ثابت: 0,01 WAVES = 0,001 FeeCoin.

2. قم بتعيين الرعاية لـ FeeCoin وسعر الصرف: 0,001 WAVES = 0,001 FeeCoin.

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
}

الآن في كل مرة يقوم شخص ما بنقل أصول ذكية N، سيمنحك FeeCoin بمبلغ N/taxDivisor (والتي يمكن شراؤها منك بسعر 10 *N/taxDivisor WAVES)، وستمنح المُعدن N/taxDivisor WAVES. ونتيجة لذلك، فإن الربح (الضريبة) الخاص بك سيكون 9*N / TaxDivisor WAVES.

يمكنك أيضًا إجراء الضرائب باستخدام البرنامج النصي للأصول الذكية و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
}

برامج الاسترداد النقدي والولاء

استرداد النقود هو نوع من برامج الولاء حيث يسترد المشتري جزءًا من المبلغ الذي أنفقه على منتج أو خدمة.

عند تنفيذ هذه الحالة باستخدام الحساب الذكي يجب علينا التحقق من الأدلة بنفس الطريقة التي قمنا بها في حالة التأمين. لمنع الإنفاق المزدوج، يجب على المستخدم إرسال DataTransaction مع (مفتاح، قيمة) = (purchaseTransactionId، CashbackTransactionId) قبل تلقي الاسترداد النقدي.

يجب علينا أيضًا فرض حظر على المفاتيح الموجودة باستخدام DataTransaction. قسم الاسترداد النقدي - الوحدة مقسومة على حصة الاسترداد النقدي. أولئك. إذا كانت حصة الاسترداد النقدي 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
}

في المقالة التالية سنلقي نظرة على استخدام الحسابات الذكية في الأدوات المالية مثل الخيارات والعقود الآجلة والفواتير.

المصدر: www.habr.com

إضافة تعليق