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,只需单击“新建”按钮。

使用智能合约的可能性很广泛:从禁止某些地址(“黑名单”)的交易到复杂的 dApp。

现在让我们看一下在商业中使用智能合约的具体示例:进行拍卖、保险和创建忠诚度计划时。

拍卖会

成功拍卖的条件之一是透明度:参与者必须确信不可能操纵出价。 这要归功于区块链,所有参与者都可以获得有关所有投注及其时间的不可变数据。

在Waves区块链上,出价可以通过DataTransaction记录在拍卖账户状态中。

您还可以使用区块编号设置拍卖的开始和结束时间:Waves 区块链中区块生成的频率大约等于 60 秒。

1. 英式升价拍卖

英式拍卖的参与者相互竞争出价。 每个新的赌注必须超过之前的赌注。 当没有更多的投标人超过最后的出价时,拍卖结束。 在这种情况下,最高出价者必须提供规定的金额。

还有一种拍卖选项,卖方为拍品设定最低价格,最终价格必须超过该价格。 否则,该拍品仍无法售出。

在此示例中,我们正在使用专门为拍卖创建的帐户。 拍卖时长为3000个区块,起始价格为0,001 WAVES。 参与者可以通过发送包含关键“价格”和出价价值的数据交易来出价。

新出价的价格必须高于该密钥的当前价格,并且参与者的账户中必须至少有 [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. 荷兰式拍卖价格递减

在荷兰式拍卖中,拍卖品最初的报价高于买家愿意支付的价格。 价格逐步下降,直到其中一位参与者同意以当前价格购买该批次为止。

在此示例中,我们使用与前一个示例相同的常量,以及 Delta 减小时的价格步长。 帐户脚本检查参与者是否确实是第一个下注的人。 否则,数据交易不会被区块链接受。

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 使用 (key, value)* = (“获胜者”, 地址),(“价格”, 价格) 出价。 仅当该参与者已经拥有带有其签名的 TransferTransaction 并且其出价高于所有先前的出价时,此类 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)
}

保险/众筹

让我们考虑一种情况,您需要确保用户的资产免受财务损失。 例如,用户希望得到保证,如果代币贬值,他将能够拿回为这些代币支付的全额金额,并愿意支付合理金额的保险。

为了实现这一点,需要发行“保险代币”。 然后,在保单持有人的帐户上安装一个脚本,仅允许执行满足特定条件的那些 ExchangeTransactions。

为了防止双重支出,您需要请求用户提前向投保人账户发送DataTransaction(key,value)=(purchaseTransactionId,sellOrderId),并禁止发送已使用key的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)
}

例如,保险代币可以成为智能资产,以禁止其转让给第三方。

该方案也可以用于众筹代币,如果未筹集到所需金额,则将众筹代币退还给所有者。

交易税

智能合约也适用于需要对多种资产的每笔交易征税的情况。 这可以通过安装了新资产来完成 赞助 对于智能资产交易:

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数量的FeeCoin(可以以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
}

现金返还和忠诚度计划

现金返还是一种忠诚度计划,买家可以返还购买产品或服务的部分金额。

在使用智能账户实施本案例时,我们必须按照保险案例中相同的方式检查证据。 为了防止双重支出,用户在收到现金返还之前必须发送一个带有 (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)
}

原子交换

原子交换允许用户在没有交易所帮助的情况下交换资产。 对于原子交换,交易双方都需要在一定时间内进行确认。

如果至少一个参与者没有在指定的交易时间内提供正确的交易确认,则交易将被取消并且交换不会发生。

在我们的示例中,我们将使用以下智能帐户脚本:

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

添加评论