Waves 스마트 계정의 적용: 경매부터 보너스 프로그램까지

Waves 스마트 계정의 적용: 경매부터 보너스 프로그램까지

블록체인은 암호화폐에만 연관되는 경우가 많지만 DLT 기술의 적용 영역은 훨씬 더 넓습니다. 블록체인 사용에 있어 가장 유망한 분야 중 하나는 자동으로 실행되고 계약 당사자 간의 신뢰가 필요하지 않은 스마트 계약입니다.

RIDE – 스마트 계약을 위한 언어

Waves는 스마트 계약을 위한 특수 언어인 RIDE를 개발했습니다. 전체 문서가 있습니다. 여기에. 그리고 여기 - 이 주제에 관한 기사 하브르에서.

RIDE 계약은 조건자이며 "true" 또는 "false"를 출력으로 반환합니다. 따라서 거래는 블록체인에 기록되거나 거부됩니다. 스마트 계약은 지정된 조건의 이행을 완전히 보장합니다. RIDE의 계약에서 트랜잭션을 생성하는 것은 현재 불가능합니다.

현재 Waves 스마트 계약에는 스마트 계정과 스마트 자산이라는 두 가지 유형이 있습니다. 스마트 계정은 일반 사용자 계정이지만 모든 거래를 제어하는 ​​스크립트가 설정되어 있습니다. 스마트 계정 스크립트는 다음과 같습니다.

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

tx는 전송 트랜잭션이 아닌 경우에만 패턴 일치 메커니즘을 사용하도록 허용하는 처리 중인 트랜잭션입니다. RIDE의 패턴 매칭은 트랜잭션 유형을 확인하는 데 사용됩니다. 모든 기존 계정은 스마트 계정 스크립트에서 처리될 수 있습니다. 거래 유형.

또한 스크립트는 변수를 선언하고 "if-then-else" 구문과 조건을 완전히 확인하기 위한 기타 방법을 사용할 수 있습니다. 계약 실행이 시작되기 전에 예측하기 쉬운 계약의 완전성과 복잡성(비용)이 입증될 수 있도록 RIDE에는 루프나 점프 문이 포함되어 있지 않습니다.

Waves 계정의 다른 기능에는 "상태", 즉 계정 상태의 존재가 포함됩니다. 데이터 트랜잭션(DataTransaction)을 사용하여 계정 상태에 무한한 수의 쌍(키, 값)을 쓸 수 있습니다. 그러면 이 정보는 REST API를 통해 그리고 스마트 계약에서 직접 처리될 수 있습니다.

각 거래에는 참가자의 서명, 필요한 거래 ID 등을 입력할 수 있는 일련의 증명이 포함될 수 있습니다.

RIDE를 통해 작업 IDE 계약의 컴파일된 보기(컴파일된 경우)를 보고, 새 계정을 생성하고 이에 대한 스크립트를 설정하고, 명령줄을 통해 트랜잭션을 보낼 수 있습니다.

계정 생성, 스마트 계약 설치 및 트랜잭션 전송을 포함한 전체 주기 동안 REST API(예: C#, C, Java, JavaScript, Python, Rust, Elixir)와 상호 작용하기 위한 라이브러리를 사용할 수도 있습니다. . IDE 작업을 시작하려면 NEW 버튼을 클릭하기만 하면 됩니다.

스마트 계약을 사용할 수 있는 가능성은 광범위합니다. 특정 주소("블랙리스트")에 대한 거래 금지부터 복잡한 dApp까지.

이제 경매, 보험 수행, 충성도 프로그램 생성 등 비즈니스에서 스마트 계약을 사용하는 구체적인 사례를 살펴보겠습니다.

경매

성공적인 경매를 위한 조건 중 하나는 투명성입니다. 참가자는 입찰을 조작하는 것이 불가능하다는 것을 확신해야 합니다. 이는 모든 베팅과 베팅 시간에 대한 불변의 데이터를 모든 참가자가 사용할 수 있는 블록체인 덕분에 달성할 수 있습니다.

Waves 블록체인에서는 DataTransaction을 통해 경매 계정 상태에 입찰이 기록될 수 있습니다.

블록 번호를 사용하여 경매의 시작 및 종료 시간을 설정할 수도 있습니다. Waves 블록체인의 블록 생성 빈도는 대략 다음과 같습니다. 60 초.

1. 영어 오름차순 경매

영국 경매장 참가자들은 서로 경쟁적으로 입찰합니다. 각각의 새로운 베팅은 이전 베팅보다 높아야 합니다. 마지막 입찰을 초과하는 입찰자가 더 이상 없을 때 경매가 종료됩니다. 이 경우 최고가 입찰자는 해당 금액을 제시하여야 합니다.

판매자가 로트의 최소 가격을 설정하고 최종 가격이 이를 초과해야 하는 경매 옵션도 있습니다. 그렇지 않으면 해당 로트는 판매되지 않은 상태로 유지됩니다.

이 예에서는 경매를 위해 특별히 생성된 계정으로 작업하고 있습니다. 경매 기간은 3000블록이며 로트의 시작 가격은 0,001 WAVES입니다. 참가자는 키 "가격"과 입찰 가치가 포함된 DataTransaction을 전송하여 입찰을 할 수 있습니다.

새 입찰 가격은 이 키의 현재 가격보다 높아야 하며 참가자는 자신의 계정에 최소한 [new_bid + Commission] 토큰을 보유해야 합니다. DataTransaction의 "sender" 필드에 입찰자의 주소가 기록되어야 하며, 현재 입찰 블록 높이가 경매 기간 내에 있어야 합니다.

경매가 끝날 때 참가자가 최고 가격을 설정한 경우 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이 블록체인에서 승인되지 않습니다.

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을 통해 입찰합니다. 이러한 DataTransaction은 해당 참가자가 이미 자신의 서명이 있는 TransferTransaction을 갖고 있고 그의 입찰이 이전의 모든 입찰보다 높은 경우에만 승인됩니다. 경매는 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)
}

보험/크라우드펀딩

금전적 손실로부터 사용자의 자산을 보호해야 하는 상황을 고려해 보겠습니다. 예를 들어, 사용자는 토큰의 가치가 하락할 경우 해당 토큰에 대해 지불한 전액을 돌려받을 수 있고 합리적인 금액의 보험금을 지불할 의사가 있다는 보장을 원합니다.

이를 구현하려면 “보험 토큰”이 발행되어야 합니다. 그런 다음 보험 계약자의 계정에 스크립트가 설치되어 특정 조건을 충족하는 ExchangeTransactions만 실행할 수 있습니다.

이중 지출을 방지하려면 (key, value) = (purchaseTransactionId, SellOrderId)로 미리 보험 계약자 계정으로 DataTransaction을 보내도록 사용자에게 요청하고, 이미 사용된 키로 DataTransaction을 보내는 것을 금지해야 합니다.

따라서 사용자의 증명에는 보험 토큰 구매의 거래 ID가 포함되어야 합니다. 통화 쌍은 구매 거래와 동일해야 합니다. 또한 비용은 구매 당시 확정된 금액에서 보험 가격을 뺀 금액과 같아야 합니다.

이후에 보험 계정은 사용자가 구매한 가격보다 낮지 않은 가격으로 사용자로부터 보험 토큰을 구매하는 것으로 이해됩니다. 보험 계정은 ExchangeTransaction을 생성하고 사용자는 주문에 서명합니다(거래가 올바르게 완료된 경우). 보험 계정은 두 번째 주문과 전체 거래에 서명하고 이를 블록체인으로 보냅니다.

구매가 발생하지 않으면 사용자는 스크립트에 설명된 규칙에 따라 ExchangeTransaction을 생성하고 해당 트랜잭션을 블록체인으로 보낼 수 있습니다. 이런 방식으로 사용자는 보장된 토큰 구매에 지출된 금액을 반환할 수 있습니다.

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

예를 들어, 보험 토큰을 스마트 자산으로 만들어 제XNUMX자에게 양도하는 것을 금지할 수 있습니다.

이 계획은 필요한 금액이 수집되지 않은 경우 소유자에게 반환되는 크라우드 펀딩 토큰에도 구현될 수 있습니다.

거래세

여러 유형의 자산을 사용하여 각 거래에 대해 세금을 징수해야 하는 경우에도 스마트 계약을 적용할 수 있습니다. 이는 설치된 새 자산을 통해 수행할 수 있습니다. 후원 스마트 자산 거래의 경우:

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개의 스마트 자산을 전송할 때마다 N/taxDivisor(10 *N/taxDivisor WAVES로 구매 가능) 금액의 FeeCoin을 제공하고, 귀하는 채굴자에게 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
}

캐시백 및 로열티 프로그램

캐시백은 구매자가 제품이나 서비스에 지출한 금액의 일부를 돌려받는 일종의 로열티 프로그램입니다.

스마트 계정을 이용하여 이 사례를 구현할 때에는 보험 사례에서와 동일한 방식으로 증명을 확인해야 합니다. 이중 지출을 방지하려면 사용자는 캐시백을 받기 전에 (key, value) = (purchaseTransactionId, cashbackTransactionId)로 DataTransaction을 보내야 합니다.

또한 DataTransaction을 사용하여 기존 키에 대한 금지를 설정해야 합니다. cashbackDivisor - 단위를 캐시백 지분으로 나눈 값입니다. 저것들. 캐시백 지분이 0.1이면 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)
}

원자 교환

Atomic Swap을 사용하면 사용자는 교환의 도움 없이 자산을 교환할 수 있습니다. 아토믹 스왑의 경우 거래에 참여한 두 참가자 모두 일정 기간 내에 이를 확인해야 합니다.

참여자 중 최소 한 명이 거래에 할당된 시간 내에 거래에 대한 정확한 확인을 제공하지 않는 경우 거래가 취소되고 교환이 이루어지지 않습니다.

이 예에서는 다음 스마트 계정 스크립트를 사용합니다.

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
}

다음 기사에서는 옵션, 선물, 청구서 등 금융 상품에서 스마트 계정을 사용하는 방법을 살펴보겠습니다.

출처 : habr.com

코멘트를 추가